/// <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">&nbsp;</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">&nbsp;</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"
 }
Example #10
0
 /// <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;
 }