public void CountDown() { MockRepository mr = Mocks; int latchSize = 5; CountDownLatch startLatch = new CountDownLatch(0); CountDownLatch latch = new CountDownLatch(latchSize); IShard shard = mr.DynamicMock <IShard>(); IExitStrategy <object> strat = mr.CreateMock <IExitStrategy <object> >(); ICallable <object> anotherCallable = mr.DynamicMock <ICallable <object> >(); IShardOperation <object> operation = mr.DynamicMock <IShardOperation <object> >(); //Expectations and results Expect.Call(strat.AddResult(null, shard)).Return(false); Expect.Call(strat.AddResult(null, shard)).Return(false); Expect.Call(strat.AddResult(null, shard)).Return(true); Expect.Call(strat.AddResult(null, shard)).Return(true); SetupResult.For(shard.ShardIds).Return(new HashedSet <ShardId>(new ShardId[] { (new ShardId(0)) })); mr.ReplayAll(); IList <StartAwareFutureTask <object> > futureTasks = new List <StartAwareFutureTask <object> >(); ParallelShardOperationCallable <object> callable = new ParallelShardOperationCallable <object>( startLatch, latch, strat, operation, shard, futureTasks); callable.Call(); // addResult returns false so latch is only decremented by 1 Assert.AreEqual(latchSize - 1, latch.Count); // addResult returns false so latch is only decremented by 1 callable.Call(); Assert.AreEqual(latchSize - 2, latch.Count); StartAwareFutureTask <object> ft = new StartAwareFutureTask_CancelReturnFalse(anotherCallable, 0); futureTasks.Add(ft); callable.Call(); // cancelling the 1 task returns false, so latch is only decremented by 1 Assert.AreEqual(latchSize - 3, latch.Count); ft = new StartAwareFutureTask_CancelReturnTrue(anotherCallable, 0); futureTasks.Add(ft); callable.Call(); // 1 decrement for myself and 1 for the task that returned true when cancelled Assert.AreEqual(latchSize - 5, latch.Count); }
/// <summary> /// Computes a result, or throws an exception if unable to do so. /// </summary> /// <returns></returns> public T Call() { try { WaitForStartSignal(); log.DebugFormat("Starting execution of {0} against shard {1}", operation.OperationName, shard); ///If addResult() returns true it means there is no more work to be ///performed. Cancel all the outstanding tasks. if (exitStrategy.AddResult(operation.Execute(shard), shard)) { log.DebugFormat("Short-circuiting execution of {0} on other threads after execution against shard {1}", operation.OperationName, shard); //It's ok to cancel ourselves because StartAwareFutureTask.cancel() //will return false if a task has already started executing, and we're //already executing. log.DebugFormat("Checking {0} future tasks to see if they need to be cancelled.", futureTasks.Count); foreach (StartAwareFutureTask <T> ft in futureTasks) { log.DebugFormat("Preparing to cancel future task %d.", ft.Id); //If a task was successfully cancelled that means it had not yet //started running. Since the task won't run, the task won't be // able to decrement the CountDownLatch. We need to decrement //it on behalf of the cancelled task. if (ft.Cancel(INTERRUPT_IF_RUNNING)) { log.Debug("Task cancel returned true, decrementing counter on its behalf."); doneSignal.CountDown(); } else { log.Debug("Task cancel returned false, not decrementing counter on its behalf."); } } } else { log.DebugFormat("No need to short-cirtcuit execution of {0} on other threads after execution against shard {1}", operation.OperationName, shard); } } finally { // counter must get decremented no matter what log.DebugFormat("Decrementing counter for operation {0} on shard {1}", operation.OperationName, shard); doneSignal.CountDown(); } return(default(T)); }
public T Apply <T>(IEnumerable <IShard> shards, IShardOperation <T> operation, IExitStrategy <T> exitStrategy) { foreach (var shard in GetNextOrderingOfShards(shards)) { var shardOperation = operation.Prepare(shard); var result = shardOperation(); if (result != null && exitStrategy.AddResult(result, shard)) { Log.Debug("Short-circuiting operation {0} after execution against shard {1}", operation.OperationName, shard); break; } } return(exitStrategy.CompileResults()); }
public T Apply <T>(IList <IShard> shards, IShardOperation <T> operation, IExitStrategy <T> exitStrategy, IExitOperationsCollector exitOperationsCollector) { foreach (IShard shard in GetNextOrderingOfShards(shards)) { if (exitStrategy.AddResult(operation.Execute(shard), shard)) { log.DebugFormat("Short-circuiting operation {0} after execution against shard {1}", operation.OperationName, shard); break; } } return(exitStrategy.CompileResults(exitOperationsCollector)); }
public async Task <T> ApplyAsync <T>(IEnumerable <IShard> shards, IAsyncShardOperation <T> operation, IExitStrategy <T> exitStrategy, CancellationToken cancellationToken) { foreach (var shard in GetNextOrderingOfShards(shards)) { var shardOperation = operation.PrepareAsync(shard); var result = await shardOperation(cancellationToken); if (result != null && exitStrategy.AddResult(result, shard)) { Log.Debug("Short-circuiting operation {0} after execution against shard {1}", operation.OperationName, shard); break; } } return(exitStrategy.CompileResults()); }
private void ExecuteForShard(object state) { var s = (IShard)state; try { Func <T> shardOperation; // Perform thread-safe preparation of the operation for a single shard. lock (this) { // Prevent execution if parallel operation has already been cancelled. if (_isCancelled) { if (--_activeCount <= 0) { Monitor.Pulse(this); } return; } shardOperation = _operation.Prepare(s); } // Perform operation execution on multiple shards in parallel. var result = shardOperation(); // Perform thread-safe aggregation of operation results. lock (this) { // Only add result if operation still has not been cancelled and result is not null. if (!_isCancelled && !Equals(result, null)) { _isCancelled = _exitStrategy.AddResult(result, s); } if (--_activeCount <= 0) { Monitor.Pulse(this); } } } catch (Exception e) { lock (this) { if (!_isCancelled) { _exception = e; _isCancelled = true; } if (--_activeCount <= 0) { Monitor.Pulse(this); } } Log.DebugFormat("Failed parallel operation '{0}' on shard '{1:X}'.", _operation.OperationName, s.ShardIds.First()); } }