src/share/vm/gc_implementation/g1/sparsePRT.hpp

Tue, 25 Jan 2011 17:58:19 -0500

author
tonyp
date
Tue, 25 Jan 2011 17:58:19 -0500
changeset 2493
97ba643ea3ed
parent 2314
f95d63e2154a
child 3900
d2a62e0f25eb
permissions
-rw-r--r--

7014261: G1: RSet-related failures
Summary: A race between the concurrent cleanup thread and the VM thread while it is processing the "expanded sparse table list" causes both threads to try to free the same sparse table entry and either causes one of the threads to fail or leaves the entry in an inconsistent state. The solution is purge all entries on the expanded list that correspond go regions that are being cleaned up.
Reviewed-by: brutisso, johnc

ysr@777 1 /*
tonyp@2493 2 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
ysr@777 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ysr@777 4 *
ysr@777 5 * This code is free software; you can redistribute it and/or modify it
ysr@777 6 * under the terms of the GNU General Public License version 2 only, as
ysr@777 7 * published by the Free Software Foundation.
ysr@777 8 *
ysr@777 9 * This code is distributed in the hope that it will be useful, but WITHOUT
ysr@777 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ysr@777 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ysr@777 12 * version 2 for more details (a copy is included in the LICENSE file that
ysr@777 13 * accompanied this code).
ysr@777 14 *
ysr@777 15 * You should have received a copy of the GNU General Public License version
ysr@777 16 * 2 along with this work; if not, write to the Free Software Foundation,
ysr@777 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ysr@777 18 *
trims@1907 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
trims@1907 20 * or visit www.oracle.com if you need additional information or have any
trims@1907 21 * questions.
ysr@777 22 *
ysr@777 23 */
ysr@777 24
stefank@2314 25 #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_SPARSEPRT_HPP
stefank@2314 26 #define SHARE_VM_GC_IMPLEMENTATION_G1_SPARSEPRT_HPP
stefank@2314 27
stefank@2314 28 #include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
stefank@2314 29 #include "gc_implementation/g1/heapRegion.hpp"
stefank@2314 30 #include "memory/allocation.hpp"
stefank@2314 31 #include "memory/cardTableModRefBS.hpp"
stefank@2314 32 #include "runtime/mutex.hpp"
stefank@2314 33 #include "utilities/globalDefinitions.hpp"
stefank@2314 34
ysr@777 35 // Sparse remembered set for a heap region (the "owning" region). Maps
ysr@777 36 // indices of other regions to short sequences of cards in the other region
ysr@777 37 // that might contain pointers into the owner region.
ysr@777 38
ysr@777 39 // These tables only expand while they are accessed in parallel --
ysr@777 40 // deletions may be done in single-threaded code. This allows us to allow
ysr@777 41 // unsynchronized reads/iterations, as long as expansions caused by
ysr@777 42 // insertions only enqueue old versions for deletions, but do not delete
ysr@777 43 // old versions synchronously.
ysr@777 44
apetrusenko@984 45 class SparsePRTEntry: public CHeapObj {
ysr@777 46 public:
ysr@777 47 enum SomePublicConstants {
iveresov@1696 48 NullEntry = -1,
iveresov@1696 49 UnrollFactor = 4
ysr@777 50 };
ysr@777 51 private:
johnc@1242 52 RegionIdx_t _region_ind;
johnc@1242 53 int _next_index;
iveresov@1696 54 CardIdx_t _cards[1];
iveresov@1696 55 // WARNING: Don't put any data members beyond this line. Card array has, in fact, variable length.
iveresov@1696 56 // It should always be the last data member.
ysr@777 57 public:
iveresov@1696 58 // Returns the size of the entry, used for entry allocation.
iveresov@1696 59 static size_t size() { return sizeof(SparsePRTEntry) + sizeof(CardIdx_t) * (cards_num() - 1); }
iveresov@1696 60 // Returns the size of the card array.
iveresov@1696 61 static int cards_num() {
iveresov@1696 62 // The number of cards should be a multiple of 4, because that's our current
iveresov@1696 63 // unrolling factor.
iveresov@1696 64 static const int s = MAX2<int>(G1RSetSparseRegionEntries & ~(UnrollFactor - 1), UnrollFactor);
iveresov@1696 65 return s;
iveresov@1696 66 }
ysr@777 67
ysr@777 68 // Set the region_ind to the given value, and delete all cards.
johnc@1242 69 inline void init(RegionIdx_t region_ind);
ysr@777 70
johnc@1242 71 RegionIdx_t r_ind() const { return _region_ind; }
ysr@777 72 bool valid_entry() const { return r_ind() >= 0; }
johnc@1242 73 void set_r_ind(RegionIdx_t rind) { _region_ind = rind; }
ysr@777 74
johnc@1242 75 int next_index() const { return _next_index; }
johnc@1242 76 int* next_index_addr() { return &_next_index; }
johnc@1242 77 void set_next_index(int ni) { _next_index = ni; }
ysr@777 78
ysr@777 79 // Returns "true" iff the entry contains the given card index.
johnc@1242 80 inline bool contains_card(CardIdx_t card_index) const;
ysr@777 81
ysr@777 82 // Returns the number of non-NULL card entries.
ysr@777 83 inline int num_valid_cards() const;
ysr@777 84
ysr@777 85 // Requires that the entry not contain the given card index. If there is
ysr@777 86 // space available, add the given card index to the entry and return
ysr@777 87 // "true"; otherwise, return "false" to indicate that the entry is full.
ysr@777 88 enum AddCardResult {
ysr@777 89 overflow,
ysr@777 90 found,
ysr@777 91 added
ysr@777 92 };
johnc@1242 93 inline AddCardResult add_card(CardIdx_t card_index);
ysr@777 94
ysr@777 95 // Copy the current entry's cards into "cards".
johnc@1242 96 inline void copy_cards(CardIdx_t* cards) const;
ysr@777 97 // Copy the current entry's cards into the "_card" array of "e."
ysr@777 98 inline void copy_cards(SparsePRTEntry* e) const;
ysr@777 99
johnc@1242 100 inline CardIdx_t card(int i) const { return _cards[i]; }
ysr@777 101 };
ysr@777 102
ysr@777 103
ysr@777 104 class RSHashTable : public CHeapObj {
ysr@777 105
ysr@777 106 friend class RSHashTableIter;
ysr@777 107
ysr@777 108 enum SomePrivateConstants {
ysr@777 109 NullEntry = -1
ysr@777 110 };
ysr@777 111
ysr@777 112 size_t _capacity;
ysr@777 113 size_t _capacity_mask;
ysr@777 114 size_t _occupied_entries;
ysr@777 115 size_t _occupied_cards;
ysr@777 116
ysr@777 117 SparsePRTEntry* _entries;
johnc@1242 118 int* _buckets;
johnc@1242 119 int _free_region;
johnc@1242 120 int _free_list;
ysr@777 121
ysr@777 122 // Requires that the caller hold a lock preventing parallel modifying
ysr@777 123 // operations, and that the the table be less than completely full. If
ysr@777 124 // an entry for "region_ind" is already in the table, finds it and
ysr@777 125 // returns its address; otherwise returns "NULL."
johnc@1242 126 SparsePRTEntry* entry_for_region_ind(RegionIdx_t region_ind) const;
ysr@777 127
ysr@777 128 // Requires that the caller hold a lock preventing parallel modifying
ysr@777 129 // operations, and that the the table be less than completely full. If
ysr@777 130 // an entry for "region_ind" is already in the table, finds it and
ysr@777 131 // returns its address; otherwise allocates, initializes, inserts and
ysr@777 132 // returns a new entry for "region_ind".
johnc@1242 133 SparsePRTEntry* entry_for_region_ind_create(RegionIdx_t region_ind);
ysr@777 134
ysr@777 135 // Returns the index of the next free entry in "_entries".
johnc@1242 136 int alloc_entry();
ysr@777 137 // Declares the entry "fi" to be free. (It must have already been
ysr@777 138 // deleted from any bucket lists.
johnc@1242 139 void free_entry(int fi);
ysr@777 140
ysr@777 141 public:
ysr@777 142 RSHashTable(size_t capacity);
ysr@777 143 ~RSHashTable();
ysr@777 144
ysr@777 145 // Attempts to ensure that the given card_index in the given region is in
ysr@777 146 // the sparse table. If successful (because the card was already
ysr@777 147 // present, or because it was successfullly added) returns "true".
ysr@777 148 // Otherwise, returns "false" to indicate that the addition would
ysr@777 149 // overflow the entry for the region. The caller must transfer these
ysr@777 150 // entries to a larger-capacity representation.
johnc@1242 151 bool add_card(RegionIdx_t region_id, CardIdx_t card_index);
ysr@777 152
johnc@1242 153 bool get_cards(RegionIdx_t region_id, CardIdx_t* cards);
iveresov@1696 154
johnc@1242 155 bool delete_entry(RegionIdx_t region_id);
ysr@777 156
johnc@1242 157 bool contains_card(RegionIdx_t region_id, CardIdx_t card_index) const;
ysr@777 158
ysr@777 159 void add_entry(SparsePRTEntry* e);
ysr@777 160
iveresov@1696 161 SparsePRTEntry* get_entry(RegionIdx_t region_id);
iveresov@1696 162
ysr@777 163 void clear();
ysr@777 164
ysr@777 165 size_t capacity() const { return _capacity; }
ysr@777 166 size_t capacity_mask() const { return _capacity_mask; }
ysr@777 167 size_t occupied_entries() const { return _occupied_entries; }
ysr@777 168 size_t occupied_cards() const { return _occupied_cards; }
ysr@777 169 size_t mem_size() const;
ysr@777 170
iveresov@1696 171 SparsePRTEntry* entry(int i) const { return (SparsePRTEntry*)((char*)_entries + SparsePRTEntry::size() * i); }
ysr@777 172
ysr@777 173 void print();
ysr@777 174 };
ysr@777 175
johnc@1242 176 // ValueObj because will be embedded in HRRS iterator.
apetrusenko@984 177 class RSHashTableIter VALUE_OBJ_CLASS_SPEC {
johnc@1242 178 int _tbl_ind; // [-1, 0.._rsht->_capacity)
johnc@1242 179 int _bl_ind; // [-1, 0.._rsht->_capacity)
iveresov@1696 180 short _card_ind; // [0..SparsePRTEntry::cards_num())
johnc@1242 181 RSHashTable* _rsht;
ysr@777 182
johnc@1242 183 // If the bucket list pointed to by _bl_ind contains a card, sets
johnc@1242 184 // _bl_ind to the index of that entry, and returns the card.
johnc@1242 185 // Otherwise, returns SparseEntry::NullEntry.
johnc@1242 186 CardIdx_t find_first_card_in_list();
ysr@777 187
johnc@1242 188 // Computes the proper card index for the card whose offset in the
johnc@1242 189 // current region (as indicated by _bl_ind) is "ci".
johnc@1242 190 // This is subject to errors when there is iteration concurrent with
johnc@1242 191 // modification, but these errors should be benign.
johnc@1242 192 size_t compute_card_ind(CardIdx_t ci);
ysr@777 193
johnc@1242 194 public:
tonyp@2239 195 RSHashTableIter() :
johnc@1242 196 _tbl_ind(RSHashTable::NullEntry),
johnc@1242 197 _bl_ind(RSHashTable::NullEntry),
iveresov@1696 198 _card_ind((SparsePRTEntry::cards_num() - 1)),
tonyp@2239 199 _rsht(NULL) {}
ysr@777 200
johnc@1242 201 void init(RSHashTable* rsht) {
johnc@1242 202 _rsht = rsht;
johnc@1242 203 _tbl_ind = -1; // So that first increment gets to 0.
johnc@1242 204 _bl_ind = RSHashTable::NullEntry;
iveresov@1696 205 _card_ind = (SparsePRTEntry::cards_num() - 1);
johnc@1242 206 }
ysr@777 207
johnc@1242 208 bool has_next(size_t& card_index);
johnc@1242 209 };
ysr@777 210
ysr@777 211 // Concurrent accesss to a SparsePRT must be serialized by some external
ysr@777 212 // mutex.
ysr@777 213
ysr@777 214 class SparsePRTIter;
tonyp@2493 215 class SparsePRTCleanupTask;
ysr@777 216
apetrusenko@984 217 class SparsePRT VALUE_OBJ_CLASS_SPEC {
tonyp@2493 218 friend class SparsePRTCleanupTask;
tonyp@2493 219
ysr@777 220 // Iterations are done on the _cur hash table, since they only need to
ysr@777 221 // see entries visible at the start of a collection pause.
ysr@777 222 // All other operations are done using the _next hash table.
ysr@777 223 RSHashTable* _cur;
ysr@777 224 RSHashTable* _next;
ysr@777 225
ysr@777 226 HeapRegion* _hr;
ysr@777 227
ysr@777 228 enum SomeAdditionalPrivateConstants {
ysr@777 229 InitialCapacity = 16
ysr@777 230 };
ysr@777 231
ysr@777 232 void expand();
ysr@777 233
ysr@777 234 bool _expanded;
ysr@777 235
ysr@777 236 bool expanded() { return _expanded; }
ysr@777 237 void set_expanded(bool b) { _expanded = b; }
ysr@777 238
ysr@777 239 SparsePRT* _next_expanded;
ysr@777 240
ysr@777 241 SparsePRT* next_expanded() { return _next_expanded; }
ysr@777 242 void set_next_expanded(SparsePRT* nxt) { _next_expanded = nxt; }
ysr@777 243
tonyp@2493 244 bool should_be_on_expanded_list();
tonyp@2493 245
ysr@777 246 static SparsePRT* _head_expanded_list;
ysr@777 247
ysr@777 248 public:
ysr@777 249 SparsePRT(HeapRegion* hr);
ysr@777 250
ysr@777 251 ~SparsePRT();
ysr@777 252
ysr@777 253 size_t occupied() const { return _next->occupied_cards(); }
ysr@777 254 size_t mem_size() const;
ysr@777 255
ysr@777 256 // Attempts to ensure that the given card_index in the given region is in
ysr@777 257 // the sparse table. If successful (because the card was already
ysr@777 258 // present, or because it was successfullly added) returns "true".
ysr@777 259 // Otherwise, returns "false" to indicate that the addition would
ysr@777 260 // overflow the entry for the region. The caller must transfer these
ysr@777 261 // entries to a larger-capacity representation.
johnc@1242 262 bool add_card(RegionIdx_t region_id, CardIdx_t card_index);
ysr@777 263
ysr@777 264 // If the table hold an entry for "region_ind", Copies its
ysr@777 265 // cards into "cards", which must be an array of length at least
iveresov@1696 266 // "SparePRTEntry::cards_num()", and returns "true"; otherwise,
iveresov@1696 267 // returns "false".
johnc@1242 268 bool get_cards(RegionIdx_t region_ind, CardIdx_t* cards);
ysr@777 269
iveresov@1696 270 // Return the pointer to the entry associated with the given region.
iveresov@1696 271 SparsePRTEntry* get_entry(RegionIdx_t region_ind);
iveresov@1696 272
ysr@777 273 // If there is an entry for "region_ind", removes it and return "true";
ysr@777 274 // otherwise returns "false."
johnc@1242 275 bool delete_entry(RegionIdx_t region_ind);
ysr@777 276
ysr@777 277 // Clear the table, and reinitialize to initial capacity.
ysr@777 278 void clear();
ysr@777 279
ysr@777 280 // Ensure that "_cur" and "_next" point to the same table.
ysr@777 281 void cleanup();
ysr@777 282
ysr@777 283 // Clean up all tables on the expanded list. Called single threaded.
ysr@777 284 static void cleanup_all();
tonyp@1052 285 RSHashTable* cur() const { return _cur; }
ysr@777 286
ysr@777 287 void init_iterator(SparsePRTIter* sprt_iter);
ysr@777 288
ysr@777 289 static void add_to_expanded_list(SparsePRT* sprt);
ysr@777 290 static SparsePRT* get_from_expanded_list();
ysr@777 291
tonyp@2493 292 // The purpose of these three methods is to help the GC workers
tonyp@2493 293 // during the cleanup pause to recreate the expanded list, purging
tonyp@2493 294 // any tables from it that belong to regions that are freed during
tonyp@2493 295 // cleanup (if we don't purge those tables, there is a race that
tonyp@2493 296 // causes various crashes; see CR 7014261).
tonyp@2493 297 //
tonyp@2493 298 // We chose to recreate the expanded list, instead of purging
tonyp@2493 299 // entries from it by iterating over it, to avoid this serial phase
tonyp@2493 300 // at the end of the cleanup pause.
tonyp@2493 301 //
tonyp@2493 302 // The three methods below work as follows:
tonyp@2493 303 // * reset_for_cleanup_tasks() : Nulls the expanded list head at the
tonyp@2493 304 // start of the cleanup pause.
tonyp@2493 305 // * do_cleanup_work() : Called by the cleanup workers for every
tonyp@2493 306 // region that is not free / is being freed by the cleanup
tonyp@2493 307 // pause. It creates a list of expanded tables whose head / tail
tonyp@2493 308 // are on the thread-local SparsePRTCleanupTask object.
tonyp@2493 309 // * finish_cleanup_task() : Called by the cleanup workers after
tonyp@2493 310 // they complete their cleanup task. It adds the local list into
tonyp@2493 311 // the global expanded list. It assumes that the
tonyp@2493 312 // ParGCRareEvent_lock is being held to ensure MT-safety.
tonyp@2493 313 static void reset_for_cleanup_tasks();
tonyp@2493 314 void do_cleanup_work(SparsePRTCleanupTask* sprt_cleanup_task);
tonyp@2493 315 static void finish_cleanup_task(SparsePRTCleanupTask* sprt_cleanup_task);
tonyp@2493 316
johnc@1242 317 bool contains_card(RegionIdx_t region_id, CardIdx_t card_index) const {
ysr@777 318 return _next->contains_card(region_id, card_index);
ysr@777 319 }
ysr@777 320 };
ysr@777 321
tonyp@2239 322 class SparsePRTIter: public RSHashTableIter {
ysr@777 323 public:
ysr@777 324 void init(const SparsePRT* sprt) {
tonyp@1052 325 RSHashTableIter::init(sprt->cur());
ysr@777 326 }
ysr@777 327 bool has_next(size_t& card_index) {
ysr@777 328 return RSHashTableIter::has_next(card_index);
ysr@777 329 }
ysr@777 330 };
stefank@2314 331
tonyp@2493 332 // This allows each worker during a cleanup pause to create a
tonyp@2493 333 // thread-local list of sparse tables that have been expanded and need
tonyp@2493 334 // to be processed at the beginning of the next GC pause. This lists
tonyp@2493 335 // are concatenated into the single expanded list at the end of the
tonyp@2493 336 // cleanup pause.
tonyp@2493 337 class SparsePRTCleanupTask VALUE_OBJ_CLASS_SPEC {
tonyp@2493 338 private:
tonyp@2493 339 SparsePRT* _head;
tonyp@2493 340 SparsePRT* _tail;
tonyp@2493 341
tonyp@2493 342 public:
tonyp@2493 343 SparsePRTCleanupTask() : _head(NULL), _tail(NULL) { }
tonyp@2493 344
tonyp@2493 345 void add(SparsePRT* sprt);
tonyp@2493 346 SparsePRT* head() { return _head; }
tonyp@2493 347 SparsePRT* tail() { return _tail; }
tonyp@2493 348 };
tonyp@2493 349
stefank@2314 350 #endif // SHARE_VM_GC_IMPLEMENTATION_G1_SPARSEPRT_HPP

mercurial