package water;
import jsr166y.CountedCompleter;
import water.H2O.H2OCountedCompleter;
import water.util.DistributedException;
import java.io.*;
/** Objects which are passed and {@link #dinvoke} is remotely executed.<p>
* <p>
* Efficient serialization methods for subclasses will be automatically
* generated, but explicit ones can be provided. Transient fields will
* <em>not</em> be mirrored between the VMs.
* <ol>
* <li>On the local vm, this task will be serialized and sent to a remote.</li>
* <li>On the remote, the task will be deserialized.</li>
* <li>On the remote, the {@link #dinvoke(H2ONode)} method will be executed.</li>
* <li>On the remote, the task will be serialized and sent to the local vm</li>
* <li>On the local vm, the task will be deserialized
* <em>into the original instance</em></li>
* <li>On the local vm, the {@link #onAck()} method will be executed.</li>
* <li>On the remote, the {@link #onAckAck()} method will be executed.</li>
* </ol>
*
*/
public abstract class DTask<T extends DTask> extends H2OCountedCompleter<T> {
protected DTask(H2OCountedCompleter completer, byte prior){super(completer,prior);}
protected DTask(H2OCountedCompleter completer){super(completer);}
protected DTask(byte prior) { super(prior); }
protected DTask() { super(); }
/** A distributable exception object, thrown by {@link #dinvoke}. */
protected byte[] _ex;
/** True if {@link #dinvoke} threw an exception.
* @return True if _ex is non-null */
public final boolean hasException() { return _ex != null; }
/** Capture the first exception in _ex. Later setException attempts are ignored. */
public synchronized void setException(Throwable ex) {
if(_ex == null) {
_ex = AutoBuffer.javaSerializeWritePojo(((ex instanceof DistributedException) ? (DistributedException) ex : new DistributedException(ex,false /* don't want this setException(ex) call in the stacktrace */)));
}
}
/** The _ex field as a RuntimeException or null.
* @return The _ex field as a RuntimeException or null. */
public Throwable getDException() {return _ex == null?null:(Throwable)AutoBuffer.javaSerializeReadPojo(_ex);}
// Track if the reply came via TCP - which means a timeout on ACKing the TCP
// result does NOT need to get the entire result again, just that the client
// needs more time to process the TCP result.
transient boolean _repliedTcp; // Any return/reply/result was sent via TCP
/** Top-level remote execution hook. Called on the <em>remote</em>. */
public void dinvoke( H2ONode sender ) {
// note: intentionally using H2O.submit here instead of direct compute2 call here to preserve FJ behavior
// such as exceptions being caught and handled via onExceptionalCompletion
// can't use fork() to keep correct priority level
H2O.submitTask(this);
}
/** 2nd top-level execution hook. After the primary task has received a
* result (ACK) and before we have sent an ACKACK, this method is executed on
* the <em>local vm</em>. Transients from the local vm are available here. */
public void onAck() {}
/** 3rd top-level execution hook. After the original vm sent an ACKACK, this
* method is executed on the <em>remote</em>. Transients from the remote vm
* are available here. */
public void onAckAck() {}
/** Override to remove 2 lines of logging per RPC. 0.5M RPC's will lead to
* 1M lines of logging at about 50 bytes/line produces 50M of log file,
* which will swamp all other logging output. */
public boolean logVerbose() { return true; }
// For MRTasks, we need to copyOver
protected void copyOver( T src ) { icer().copyOver((T)this,src); }
/** Task to be executed at the home node of the given key.
* Basically a wrapper around DTask which enables us to bypass
* remote/local distinction (RPC versus submitTask). */
public static abstract class DKeyTask<T extends DKeyTask,V extends Keyed> extends DTask<DKeyTask>{
private final Key _key;
public DKeyTask(H2OCountedCompleter cmp,final Key k) { super(cmp); _key = k; }
/** Override map(); will be run on Key's home node */
protected abstract void map(V v);
@Override public final void compute2(){
if(_key.home()){
Value val = Value.STORE_get(_key);
if( val != null )
map(val.<V>get()); // Call map locally
tryComplete();
} else { // Else call remotely
new RPC(_key.home_node(),this).addCompleter(this).call();
}
}
// onCompletion must be empty here, may be invoked twice (on remote and local)
@Override public final void onCompletion(CountedCompleter cc){}
/** Convenience non-blocking submit to work queues */
public void submitTask() {H2O.submitTask(this);}
/** Convenience blocking submit to work queues */
public T invokeTask() {
H2O.submitTask(this);
join();
return (T)this;
}
}
/** Task to cleanly remove value from the K/V (call it's remove()
* destructor) without the need to fetch it locally first. */
public static class RemoveCall extends DKeyTask {
public RemoveCall(H2OCountedCompleter cmp, Key k) { super(cmp, k);}
@Override protected void map(Keyed val) { val.remove();}
}
}