package water; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import water.util.Log; /** * A collection of Futures. We can add more, or block on the whole collection. * Undefined if you try to add Futures while blocking. * <p><p> * Used as a service to sub-tasks, collect pending-but-not-yet-done future * tasks, that need to complete prior to *this* task completing... or if the * caller of this task is knowledgeable, pass these pending tasks along to him * to block on before he completes. */ public class Futures { // implemented as an exposed array mostly because ArrayList doesn't offer // synchronization and constant-time removal. Future[] _pending = new Future[1]; int _pending_cnt; /** Some Future task which needs to complete before this task completes */ synchronized public Futures add( Future f ) { if( f == null ) return this; if( f.isDone() ) return this; // NPE here if this Futures has already been added to some other Futures // list, and should be added to again. if( _pending_cnt == _pending.length ) { cleanCompleted(); if( _pending_cnt == _pending.length ) _pending = Arrays.copyOf(_pending,_pending_cnt<<1); } _pending[_pending_cnt++] = f; return this; } /** Merge pending-task lists as part of doing a 'reduce' step */ public void add( Futures fs ) { if( fs == null ) return; assert fs != this; // No recursive death, please for( int i=0; i<fs._pending_cnt; i++ ) add(fs._pending[i]); // NPE here if using a dead Future fs._pending = null; // You are dead, should never be inserted into again } /** Clean out from the list any pending-tasks which are already done. Note * that this drops the algorithm from O(n) to O(1) in practice, since mostly * things clean out as fast as new ones are added and the list never gets * very large. */ synchronized private void cleanCompleted() { for( int i=0; i<_pending_cnt; i++ ) if( _pending[i].isDone() ) // Done? // Do cheap array compression to remove from list _pending[i--] = _pending[--_pending_cnt]; } /** Block until all pending futures have completed */ public final void blockForPending() { try { // Block until the last Future finishes. while( true ) { Future f = null; synchronized(this) { if( _pending_cnt == 0 ) return; f = _pending[--_pending_cnt]; } f.get(); } } catch( InterruptedException e ) { throw Log.errRTExcept(e); } catch( ExecutionException e ) { throw Log.errRTExcept(e); } catch(Throwable t){ throw new RuntimeException(t); } } }