/// <summary> /// see RepeatTemplate#WaitForResults /// </summary> /// <param name="state"></param> /// <returns></returns> protected override bool WaitForResults(IRepeatInternalState state) { IResultQueue <IResultHolder> queue = ((ResultQueueInternalState)state).ResultQueue; bool result = true; while (queue.IsExpecting()) { // Careful that no runnables that are not going to finish ever get // onto the queue, else this may block forever. IResultHolder future; try { future = queue.Take(); } catch (ThreadInterruptedException) { Thread.CurrentThread.Interrupt(); throw new RepeatException("InterruptedException while waiting for result."); } if (future.Error != null) { state.GetExceptions().Add(future.Error); result = false; } else { RepeatStatus status = future.Result; result = result && CanContinue(status); ExecuteAfterInterceptors(future.Context, status); } } Assert.State(queue.IsEmpty(), "Future results queue should be empty at end of batch."); return(result); }
/// <summary> /// Use the TaskExecutor to generate a result. The /// internal state in this case is a queue of unfinished result holders of /// IResultHolder. The holder with the return value should not be /// on the queue when this method exits. The queue is scoped in the calling /// method so there is no need to synchronize access. /// </summary> /// <param name="context"></param> /// <param name="callback"></param> /// <param name="state"></param> /// <returns></returns> protected override RepeatStatus GetNextResult(IRepeatContext context, RepeatCallback callback, IRepeatInternalState state) { ExecutingRunnable runnable; IResultQueue <IResultHolder> queue = ((ResultQueueInternalState)state).ResultQueue; do { // Wrap the callback in a runnable that will add its result to the // queue when it is ready. runnable = new ExecutingRunnable(callback, context, queue); // Tell the runnable that it can expect a result. This could have // been in-lined with the constructor, but it might block, so it's // better to do it here, since we have the option (it's a private // class). runnable.Expect(); //Start the task possibly concurrently / in the future. _taskExecutor.Execute(new Task(delegate { runnable.Run(); })); // Allow termination policy to update its state. This must happen // immediately before or after the call to the task executor. Update(context); // Keep going until we get a result that is finished, or early // termination... }while (queue.IsEmpty() && !IsComplete(context)); IResultHolder result = queue.Take(); if (result.Error != null) { throw result.Error; } return(result.Result); }