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());
 }
Example #4
0
 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());
                }
            }