/** The PUT for this Value has completed. Wakeup any blocked later PUTs. */ void completeRemotePut() { assert !_key.home(); // Attempt an eager blind attempt, assuming no blocked pending notifies if( RW_CAS(0, -1,"remote_complete") ) return; synchronized(this) { boolean res = RW_CAS(1, -1,"remote_do_notify"); assert res; // Must succeed notifyAll(); // Wake up pending blocked PUTs } }
/** Block this thread until all prior remote PUTs complete - to force * remote-PUT ordering on the home node. */ void startRemotePut() { assert !_key.home(); int x = 0; // assert I am waiting on threads with higher priority? while( (x=_rwlock.get()) != -1 ) // Spin until rwlock==-1 if( x == 1 || RW_CAS(0,1,"remote_need_notify") ) try { ForkJoinPool.managedBlock(this); } catch( InterruptedException e ) { } }
/** Atomically lower active GET count */ void lowerActiveGetCount( H2ONode h2o ) { assert _key.home(); // Only the HOME node for a key tracks replicas assert h2o != H2O.SELF;// Do not track self as a replica while( true ) { // Repeat, in case racing GETs are bumping the counter int old = _rwlock.get(); // Read the lock-word assert old > 0; // Since lowering, must be at least 1 assert old != -1; // Not write-locked, because we are an active reader assert _replicas.contains(h2o._unique_idx); // Self-bit is set if( RW_CAS(old,old-1,"rlock-") ) { if( old-1 == 0 ) // GET count fell to zero? synchronized( this ) { notifyAll(); } // Notify any pending blocked PUTs return; // Repeat until count is lowered } } }
/** Atomically insert h2o into the replica list; reports false if the Value * flagged against future replication with a -1. Also bumps the active * Get count, which remains until the Get completes (we receive an ACKACK). */ boolean setReplica( H2ONode h2o ) { assert _key.home(); // Only the HOME node for a key tracks replicas assert h2o != H2O.SELF; // Do not track self as a replica while( true ) { // Repeat, in case racing GETs are bumping the counter int old = _rwlock.get(); if( old == -1 ) return false; // Write-locked; no new replications. Read fails to read *this* value assert old >= 0; // Not negative if( RW_CAS(old,old+1,"rlock+") ) break; } // Narrow non-race here. Here is a time window where the rwlock count went // up, but the replica list does not account for the new replica. However, // the rwlock cannot go down until an ACKACK is received, and the ACK // (hence ACKACK) doesn't go out until after this function returns. _replicas.add(h2o._unique_idx); // Both rwlock taken, and replica count is up now. return true; }
/** This value was atomically extracted from the local STORE by a successful * TaskPutKey attempt (only 1 thread can ever extract and thus call here). * No future lookups will find this Value, but there may be existing uses. * Atomically set the rwlock count to -1 locking it from further GETs and * ship out invalidates to caching replicas. May need to block on active * GETs. Updates a set of Future invalidates that can be blocked against. */ Futures lockAndInvalidate( H2ONode sender, Futures fs ) { assert _key.home(); // Only the HOME node for a key tracks replicas // Write-Lock against further GETs while( true ) { // Repeat, in case racing GETs are bumping the counter int old = _rwlock.get(); assert old >= 0 : _key+", rwlock="+old; // Count does not go negative assert old != -1; // Only the thread doing a PUT ever locks if( old !=0 ) { // has readers? // Active readers: need to block until the GETs (of this very Value!) // all complete, before we can invalidate this Value - lest a racing // Invalidate bypass a GET. try { ForkJoinPool.managedBlock(this); } catch( InterruptedException e ) { } } else if( RW_CAS(0,-1,"wlock") ) break; // Got the write-lock! } // We have the set of Nodes with replicas now. Ship out invalidates. int max = _replicas.length(); for( int i=0; i<max; i++ ) if( _replicas.contains(i) && H2ONode.IDX[i] != sender ) TaskInvalidateKey.invalidate(H2ONode.IDX[i],_key,fs); return fs; }