All Downloads are FREE. Search and download functionalities are using the official Maven repository.

water.Atomic Maven / Gradle / Ivy

There is a newer version: 3.8.2.9
Show newest version
package water;

/**
 * Atomic update of a Key
 *
 * @author 
 * @version 1.0
 */

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

  public Atomic(){ super(H2O.ATOMIC_PRIORITY); }
  public Atomic(H2O.H2OCountedCompleter completer){super(completer,H2O.ATOMIC_PRIORITY);}
  // 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 home 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 Atomic invoke( Key key ) {
    RPC> rpc = fork(key);
    return (rpc == null ? this : rpc.get()); // Block for it
  }

  // Fork off
  public final RPC> 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 ) {      // ABORT: they gave up
        // Strongly order XTNs on same key, EVEN if aborting.  Generally abort
        // means some interesting condition is already met, but perhaps met by
        // the exactly proceeding XTN whose invalidates are still roaming about
        // the system.  If we do not block, the Atomic.invoke might complete
        // before the invalidates, and the invoker might then do a DKV.get()
        // and get his original value - instead of inval & fetching afresh.
        val1.blockTillNoReaders(); // Prior XTN that made val1 may not yet have settled out; block for it
        break; 
      }
      assert val1 != val2;      // No returning the same Value
      // Attempt atomic update
      Value res = DKV.DputIfMatch(_key,val2,val1,fs);
      if( res == val1 ) {       // Success?
        fs.blockForPending();   // Block for any pending invalidates on the atomic update
        onSuccess(val1);        // Call user's post-XTN function
        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();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy