private NBSI( int max_elem, ConcurrentAutoTable ctr, NonBlockingSetInt nonb ) { super(); _non_blocking_set_int = nonb; _size = ctr; _copyIdx = ctr == null ? null : new AtomicInteger(); _copyDone = ctr == null ? null : new AtomicInteger(); // The main array of bits _bits = new long[(int)(((long)max_elem+63)>>>6)]; // Every 64th bit is moved off to it's own subarray, so that the // sign-bit is free for other purposes _nbsi64 = ((max_elem+1)>>>6) == 0 ? null : new NBSI((max_elem+1)>>>6, null, null); _sum_bits_length = _bits.length + (_nbsi64==null ? 0 : _nbsi64._sum_bits_length); }
/** * Add {@code i} to the set. This is the lower-case '{@code int}' version * of {@link #add} - no autoboxing. Negative values throw * IllegalArgumentException. * @throws IllegalArgumentException if i is negative. * @return <tt>true</tt> if i was added to the set. */ public boolean add( final int i ) { if( i < 0 ) throw new IllegalArgumentException(""+i); return _nbsi.add(i); } /**
private void writeObject(java.io.ObjectOutputStream s) throws IOException { s.defaultWriteObject(); // Nothing to write final NBSI nbsi = _nbsi; // The One Field is transient final int len = _nbsi._bits.length<<6; s.writeInt(len); // Write max element for( int i=0; i<len; i++ ) s.writeBoolean( _nbsi.contains(i) ); }
while( bits >= 0 ) { // Still in state (1)? long oldbits = bits; bits |= mask(63); // Target state of bits: sign-bit means immutable if( old.CAS( j>>6, oldbits, bits ) ) { if( oldbits == 0 ) _copyDone.addAndGet(1); break; // Success - old array word is now immutable if( bits != mask(63) ) { // Non-zero in old? long new_bits = nnn._bits[j>>6]; if( new_bits == 0 ) { // New array is still zero new_bits = bits & ~mask(63); // Desired new value: a mutable copy of bits if( !nnn.CAS( j>>6, 0, new_bits ) ) new_bits = nnn._bits[j>>6]; // Since it failed, get the new value assert new_bits != 0; if( old.CAS( j>>6, bits, mask(63) ) ) _copyDone.addAndGet(1); // One more word finished copying
private void print(int d) { StringBuffer buf = new StringBuffer(); buf.append("NBSI - _bits.len="); NBSI x = this; while( x != null ) { buf.append(" "+x._bits.length); x = x._nbsi64; } print(d,buf.toString()); x = this; while( x != null ) { for( int i=0; i<x._bits.length; i++ ) System.out.print(Long.toHexString(x._bits[i])+" "); x = x._nbsi64; System.out.println(); } if( _copyIdx.get() != 0 || _copyDone.get() != 0 ) print(d,"_copyIdx="+_copyIdx.get()+" _copyDone="+_copyDone.get()+" _words_to_cpy="+_sum_bits_length); if( _new != null ) { print(d,"__has_new - "); _new.print(d+1); } } }
private NBSI help_copy() { // Pick some words to help with - but only help copy the top-level NBSI. // Nested NBSI waits until the top is done before we start helping. NBSI top_nbsi = _non_blocking_set_int._nbsi; final int HELP = 8; // Tuning number: how much copy pain are we willing to inflict? // We "help" by forcing individual bit indices to copy. However, bits // come in lumps of 64 per word, so we just advance the bit counter by 64's. int idx = top_nbsi._copyIdx.getAndAdd(64*HELP); for( int i=0; i<HELP; i++ ) { int j = idx+i*64; j %= (top_nbsi._bits.length<<6); // Limit, wrap to array size; means we retry indices top_nbsi.help_copy_impl(j ); top_nbsi.help_copy_impl(j+63); // Also force the nested-by-64 bit } // Top level guy ready to promote? // Note: WE may not be the top-level guy! if( top_nbsi._copyDone.get() == top_nbsi._sum_bits_length ) // One shot CAS to promote - it may fail since we are racing; others // may promote as well if( _non_blocking_set_int.CAS_nbsi( top_nbsi, top_nbsi._new ) ) { //System.out.println("Promote at top level to size "+(_non_blocking_set_int._nbsi._bits.length<<6)); } // Return the new bitvector for 'fluid' programming style return _new; }
public boolean remove( final int i ) { if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array! return _new==null ? false : help_copy().remove(i); // Handle every 64th bit via using a nested array NBSI nbsi = this; // The bit array being added into int j = i; // The bit index being added while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) nbsi = nbsi._nbsi64; // Recurse j = j>>6; // Strip off low 6 bits (all set) } final long mask = mask(j); long old; do { old = nbsi._bits[j>>6]; // Read old bits if( old < 0 ) // Not mutable? // Not mutable: finish copy of word, and retry on copied word return help_copy_impl(i).help_copy().remove(i); if( (old & mask) == 0 ) return false; // Bit is already clear? } while( !nbsi.CAS( j>>6, old, old & ~mask ) ); _size.add(-1); return true; }
public boolean add( final int i ) { // Check for out-of-range for the current size bit vector. // If so we need to grow the bit vector. if( (i>>6) >= _bits.length ) return install_larger_new_bits(i). // Install larger pile-o-bits (duh) help_copy().add(i); // Finally, add to the new table // Handle every 64th bit via using a nested array NBSI nbsi = this; // The bit array being added into int j = i; // The bit index being added while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) nbsi = nbsi._nbsi64; // Recurse j = j>>6; // Strip off low 6 bits (all set) } final long mask = mask(j); long old; do { old = nbsi._bits[j>>6]; // Read old bits if( old < 0 ) // Not mutable? // Not mutable: finish copy of word, and retry on copied word return help_copy_impl(i).help_copy().add(i); if( (old & mask) != 0 ) return false; // Bit is already set? } while( !nbsi.CAS( j>>6, old, old | mask ) ); _size.add(1); return true; }
public boolean contains( final int i ) { if( (i>>6) >= _bits.length ) // Out of bounds? Not in this array! return _new==null ? false : help_copy().contains(i); // Handle every 64th bit via using a nested array NBSI nbsi = this; // The bit array being added into int j = i; // The bit index being added while( (j&63) == 63 ) { // Bit 64? (low 6 bits are all set) nbsi = nbsi._nbsi64; // Recurse j = j>>6; // Strip off low 6 bits (all set) } final long mask = mask(j); long old = nbsi._bits[j>>6]; // Read old bits if( old < 0 ) // Not mutable? // Not mutable: finish copy of word, and retry on copied word return help_copy_impl(i).help_copy().contains(i); // Yes mutable: test & return bit return (old & mask) != 0; }
private void advance() { while( true ) { _idx++; // Next index while( (_idx>>6) >= _nbsi2._bits.length ) { // Index out of range? if( _nbsi2._new == null ) { // New table? _idx = -2; // No, so must be all done return; // } _nbsi2 = _nbsi2._new; // Carry on, in the new table } if( _nbsi2.contains(_idx) ) return; } } public Integer next() {
/** Create a new empty bit-vector */ public NonBlockingSetInt( ) { _nbsi = new NBSI(63, new ConcurrentAutoTable(), this); // The initial 1-word set }
private NBSI install_larger_new_bits( final int i ) { if( _new == null ) { // Grow by powers of 2, to avoid minor grow-by-1's. // Note: must grow by exact powers-of-2 or the by-64-bit trick doesn't work right int sz = (_bits.length<<6)<<1; // CAS to install a new larger size. Did it work? Did it fail? We // don't know and don't care. Only One can be installed, so if // another thread installed a too-small size, we can't help it - we // must simply install our new larger size as a nested-resize table. CAS_new(new NBSI(sz, _size, _non_blocking_set_int)); } // Return self for 'fluid' programming style return this; }
/** * Test if {@code i} is in the set. This is the lower-case '{@code int}' * version of {@link #contains} - no autoboxing. * @return <tt>true</tt> if i was int the set. */ public boolean contains( final int i ) { return i<0 ? false : _nbsi.contains(i); } /**
/** Empty the bitvector. */ public void clear ( ) { NBSI cleared = new NBSI(63, new ConcurrentAutoTable(), this); // An empty initial NBSI while( !CAS_nbsi( _nbsi, cleared ) ) // Spin until clear works ; }
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // Read nothing final int len = s.readInt(); // Read max element _nbsi = new NBSI(len, new ConcurrentAutoTable(), this); for( int i=0; i<len; i++ ) // Read all bits if( s.readBoolean() ) _nbsi.add(i); }