package water;

/**
 * Atomic update of a Key
 *
 * @author <a href="mailto:cliffc@0xdata.com"></a>
 * @version 1.0
 */

abstract public class Atomic<T extends Atomic> extends DTask {
  protected Key _key;           // Transaction key

  public Atomic(){}
  public Atomic(H2O.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 protected 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).
   */
  protected 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 protected 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();
  }

  @Override protected byte priority() { return H2O.ATOMIC_PRIORITY; }
}

@@ -174,16 +174,19 @@
 ds DTask
+%3CT%3E
  %7B%0A  pro

improve generic type