private void cleanUp() { if (rehash()) { // If we rehashed, we needn't clean up (clean up happens as // a side effect). return; }
if (size == 0) { // No live entries == nothing to clean. return; }
// Clean log(table.length) entries picking up where we left off // last time. int index = clean; Object[] table = this.table; for (int counter = table.length; counter > 0; counter >>= 1, index = next(index)) { Object k = table[index];
if (k == TOMBSTONE || k == null) { continue; // on to next entry }
// The table can only contain null, tombstones and references. @SuppressWarnings("unchecked") Reference<ThreadLocal<?>> reference = (Reference<ThreadLocal<?>>) k; if (reference.get() == null) { // This thread local was reclaimed by the garbage collector. table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; } }
Object getAfterMiss(ThreadLocal<?> key) { Object[] table = this.table; int index = key.hash & mask;
// If the first slot is empty, the search is over. if (table[index] == null) { //1 Object value = key.initialValue();
// If the table is still the same and the slot is still empty... if (this.table == table && table[index] == null) { table[index] = key.reference; table[index + 1] = value; size++;
cleanUp(); return value; }
// The table changed during initialValue(). put(key, value); return value; }
// Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1;
// Continue search. for (index = next(index);; index = next(index)) { Object reference = table[index]; if (reference == key.reference) { //2 return table[index + 1]; }
// If no entry was found... if (reference == null) { Object value = key.initialValue();
// If the table is still the same... if (this.table == table) { // If we passed a tombstone and that slot still // contains a tombstone... if (firstTombstone > -1 && table[firstTombstone] == TOMBSTONE) { table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++;
// No need to clean up here. We aren't filling // in a null slot. return value; }
// If this slot is still empty... if (table[index] == null) { table[index] = key.reference; table[index + 1] = value; size++;
cleanUp(); return value; } }
// The table changed during initialValue(). put(key, value); return value; }
if (firstTombstone == -1 && reference == TOMBSTONE) { // Keep track of this tombstone so we can overwrite it. firstTombstone = index; } } }