/// <summary> /// Get the next completed result, possibly executing several callbacks until /// one finally finishes. Normally a subclass would have to override both /// this method and <see cref="CreateInternalState"/> because the /// implementation of this method would rely on the details of the internal /// state. /// </summary> /// <param name="context"></param> /// <param name="callback"></param> /// <param name="state"></param> /// <returns></returns> /// <exception cref="Exception"> </exception> protected virtual RepeatStatus GetNextResult(IRepeatContext context, RepeatCallback callback, IRepeatInternalState state) { Update(context); if (Logger.IsDebugEnabled) { Logger.Debug("Repeat operation about to start at count={0}", context.GetStartedCount()); } return(callback(context)); //"DoInInteration" }
/// <summary> /// Execute the batch callback until the completion policy decides that we /// are finished. Wait for the whole batch to finish before returning even if /// the task executor is asynchronous. /// </summary> /// <param name="callback"></param> /// <returns></returns> public RepeatStatus Iterate(RepeatCallback callback) { IRepeatContext outer = RepeatSynchronizationManager.GetContext(); RepeatStatus result; try { // This works with an asynchronous TaskExecutor: the // interceptors have to wait for the child processes. result = ExecuteInternal(callback); } finally { RepeatSynchronizationManager.Clear(); if (outer != null) { RepeatSynchronizationManager.Register(outer); } } 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); }
public ExecutingRunnable(RepeatCallback callback, IRepeatContext context, IResultQueue<IResultHolder> queue) { _callback = callback; Context = context; _queue = queue; }
/// <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; }
/// <summary> /// /// </summary> /// <param name="callback"></param> /// <returns></returns> private RepeatStatus ExecuteInternal(RepeatCallback callback) { // Reset the termination policy if there is one... IRepeatContext context = Start(); // Make sure if we are already marked complete before we start then no // processing takes place. bool running = !IsMarkedComplete(context); foreach (IRepeatListener interceptor in _listeners) { interceptor.Open(context); running = running && !IsMarkedComplete(context); if (!running) { break; } } // Return value, default is to allow continued processing. RepeatStatus result = RepeatStatus.Continuable; IRepeatInternalState state = CreateInternalState(context); // This is the list of exceptions thrown by all active callbacks ICollection <System.Exception> exceptions = state.GetExceptions(); // Keep a separate list of exceptions we handled that need to be // rethrown ICollection <System.Exception> deferred = new List <System.Exception>(); try { while (running) { #region WhileRunning /* * Run the before interceptors here, not in the task executor so * that they all happen in the same thread - it's easier for * tracking batch status, amongst other things. */ foreach (IRepeatListener interceptor in _listeners) { interceptor.Before(context); // Allow before interceptors to veto the batch by setting // flag. running = running && !IsMarkedComplete(context); } // Check that we are still running (should always be true) ... if (running) { #region Running try { result = GetNextResult(context, callback, state); ExecuteAfterInterceptors(context, result); } catch (System.Exception exception) { DoHandle(exception, context, deferred); } // N.B. the order may be important here: if (IsComplete(context, result) || IsMarkedComplete(context) || deferred.Any()) { running = false; } #endregion } #endregion } result = result.And(WaitForResults(state)); foreach (System.Exception exception in exceptions) { DoHandle(exception, context, deferred); } // Explicitly drop any references to internal state... // useless ? state = null; } /* * No need for explicit catch here - if the business processing threw an * exception it was already handled by the helper methods. An exception * here is necessarily fatal. */ finally { #region HandleFinally HandleFinally(deferred, _listeners, context); #endregion } return(result); }
public ExecutingRunnable(RepeatCallback callback, IRepeatContext context, IResultQueue <IResultHolder> queue) { _callback = callback; Context = context; _queue = queue; }
/// <summary> /// /// </summary> /// <param name="callback"></param> /// <returns></returns> private RepeatStatus ExecuteInternal(RepeatCallback callback) { // Reset the termination policy if there is one... IRepeatContext context = Start(); // Make sure if we are already marked complete before we start then no // processing takes place. bool running = !IsMarkedComplete(context); foreach (IRepeatListener interceptor in _listeners) { interceptor.Open(context); running = running && !IsMarkedComplete(context); if (!running) { break; } } // Return value, default is to allow continued processing. RepeatStatus result = RepeatStatus.Continuable; IRepeatInternalState state = CreateInternalState(context); // This is the list of exceptions thrown by all active callbacks ICollection<System.Exception> exceptions = state.GetExceptions(); // Keep a separate list of exceptions we handled that need to be // rethrown ICollection<System.Exception> deferred = new List<System.Exception>(); try { while (running) { #region WhileRunning /* * Run the before interceptors here, not in the task executor so * that they all happen in the same thread - it's easier for * tracking batch status, amongst other things. */ foreach (IRepeatListener interceptor in _listeners) { interceptor.Before(context); // Allow before interceptors to veto the batch by setting // flag. running = running && !IsMarkedComplete(context); } // Check that we are still running (should always be true) ... if (running) { #region Running try { result = GetNextResult(context, callback, state); ExecuteAfterInterceptors(context, result); } catch (System.Exception exception) { DoHandle(exception, context, deferred); } // N.B. the order may be important here: if (IsComplete(context, result) || IsMarkedComplete(context) || deferred.Any()) { running = false; } #endregion } #endregion } result = result.And(WaitForResults(state)); foreach (System.Exception exception in exceptions) { DoHandle(exception, context, deferred); } // Explicitly drop any references to internal state... // useless ? state = null; } /* * No need for explicit catch here - if the business processing threw an * exception it was already handled by the helper methods. An exception * here is necessarily fatal. */ finally { #region HandleFinally HandleFinally(deferred, _listeners, context); #endregion } return result; }
/// <summary> /// Get the next completed result, possibly executing several callbacks until /// one finally finishes. Normally a subclass would have to override both /// this method and <see cref="CreateInternalState"/> because the /// implementation of this method would rely on the details of the internal /// state. /// </summary> /// <param name="context"></param> /// <param name="callback"></param> /// <param name="state"></param> /// <returns></returns> /// <exception cref="Exception"> </exception> protected virtual RepeatStatus GetNextResult(IRepeatContext context, RepeatCallback callback, IRepeatInternalState state) { Update(context); if (Logger.IsDebugEnabled) { Logger.Debug("Repeat operation about to start at count={0}", context.GetStartedCount()); } return callback(context); //"DoInInteration" }
/// <summary> /// Execute the batch callback until the completion policy decides that we /// are finished. Wait for the whole batch to finish before returning even if /// the task executor is asynchronous. /// </summary> /// <param name="callback"></param> /// <returns></returns> public RepeatStatus Iterate(RepeatCallback callback) { IRepeatContext outer = RepeatSynchronizationManager.GetContext(); RepeatStatus result; try { // This works with an asynchronous TaskExecutor: the // interceptors have to wait for the child processes. result = ExecuteInternal(callback); } finally { RepeatSynchronizationManager.Clear(); if (outer != null) { RepeatSynchronizationManager.Register(outer); } } return result; }