package water; import water.DTask; import water.H2O.H2OCountedCompleter; /** * Atomic update of a Key * * @author <a href="mailto:cliffc@h2o.ai"></a> * @version 1.0 */ public abstract class Atomic<T extends Atomic> extends DTask { public Key _key; // Transaction key public Atomic(){} public Atomic(H2OCountedCompleter completer){super(completer);} // User's function to be run atomically. The Key's Value is fetched from the // home STORE and passed in. The returned Value is atomically installed as // the new Value (and the function is retried until it runs atomically). The // original Value is supposed to be read-only. If the original Key misses // (no Value), one is created with 0 length and wrong Value._type to allow // the Key to passed in (as part of the Value) abstract public Value atomic( Value val ); /** Executed on the transaction key's <em>home</em> node after any successful * atomic update. Override this if you need to perform some action after * the update succeeds (eg cleanup). */ public void onSuccess( Value old ){} /** Block until it completes, even if run remotely */ public final T invoke( Key key ) { RPC<Atomic<T>> rpc = fork(key); return (T)(rpc == null ? this : rpc.get()); // Block for it } // Fork off public final RPC<Atomic<T>> fork(Key key) { _key = key; if( key.home() ) { // Key is home? compute2(); // Also, run it blocking/now return null; } else { // Else run it remotely return RPC.call(key.home_node(),this); } } // The (remote) workhorse: @Override public final void compute2( ) { assert _key.home() : "Atomic on wrong node; SELF="+H2O.SELF+ ", key_home="+_key.home_node()+", key_is_home="+_key.home()+", class="+getClass(); Futures fs = new Futures(); // Must block on all invalidates eventually Value val1 = DKV.get(_key); while( true ) { // Run users' function. This is supposed to read-only from val1 and // return new val2 to atomically install. Value val2 = atomic(val1); if( val2 == null ) break; // ABORT: they gave up assert val1 != val2; // No returning the same Value // Attempt atomic update Value res = DKV.DputIfMatch(_key,val2,val1,fs); if( res == val1 ) { // Success? onSuccess(val1); // Call user's post-XTN function fs.blockForPending(); // Block for any pending invalidates on the atomic update break; } val1 = res; // Otherwise try again with the current value } // and retry _key = null; // No need for key no more, don't send it back tryComplete(); // Tell F/J this task is done } @Override public byte priority() { return H2O.ATOMIC_PRIORITY; } }