Impossibility is not an option!
My blog about .Net programming, what is possible and what (sometimes) drives me crazy!

The Future Revealed

Friday, January 16, 2009 8:57 AM
Easy deferred execution

While programming, especially in GUI applications, you may have come across the problem of actions that take too long to run them in the foreground thread (aka GUI thread).
.Net brings a couple of different possibilities to circumvent this, for example the ThreadPool, BackGroundWorker or the Dispatcher class in WPF.  What they have in common is, that they clutter up your code.

Especially, if your are trying to separate your concerns you should think of a better solution.
One possible solution could be to use a construct that separates the concern of handling the background calculation and just gives you the result when it's ready.

An interesting approach was presented before by Ayende although in a different context (NHibernate) in his article Future<TNHibernateQuery>.
The following code is an adoption of this to non-NHibernate context
You can also find the code for it here: Future.cs and a test with example usage at: FutureTest.cs.
The 'Guard' class used in the code will be the topic of a other blog post. In the mean time, you can find it here: Gard.cs

    ///<summary>
    ///Class to move a calculation of into the background (<see cref="ThreadPool"/>). 
    ///</summary>
    ///<typeparam name="TResult">Type of the result.</typeparam>
    public class Future<TResult>
    {
        //---- Members -----------------------------------------------------
        private TResult _value;
        private Exception _exception;
        private readonly IAsyncResult _asyncResult;
        private readonly ManualResetEvent _lock;
        private readonly Action<Future<TResult>> _callback;

        //---------------------------------------------------------	
        ///<summary>
        ///Create new instance and start the evaluation of the value.
        ///If an exception happens, it will be caught and exposed through the <see cref="Exception"/> property.
        ///</summary>
        ///<param name="action">Delegate to the evaluation function.</param>
        public Future(Func<TResult> action) : this(action, future => { })
        {
        }

        //---------------------------------------------------------
        ///<summary>
        ///Create new instance and start the evaluation of the value.
        ///If an exception happens, it will be caught and exposed through the <see cref="Exception"/> property.
        ///</summary>
        ///<param name="action">Delegate to the evaluation function.</param>
        ///<param name="callback">Delegate to be called after the evaluation.</param>
        public Future(Func<TResult> action, Action<Future<TResult>> callback)
        {
            Guard.Against.Null.Argument(()=>callback);

            _lock = new ManualResetEvent(false);
            _callback = callback;
            _asyncResult = action.BeginInvoke(
                asyncResult =>
                    {
                        try
                        {
                            _value = action.EndInvoke(asyncResult);
                        }
                        catch (Exception ex)
                        {
                            _exception = ex;
                        }
                        finally
                        {
                            ((ManualResetEvent)asyncResult.AsyncState).Set();
                            try
                            {
                                _callback(this);
                            }
                            catch (Exception ex)
                            {
                                Debug.Fail("Error while executing callback on future!", ex.ToString());
                            } //ignore the failed callback
                 
                        }
                    },
                _lock);
            _callback = callback;
        }

        //---------------------------------------------------------
        ///<summary>
        ///Gets state of the calculation.
        ///</summary>
        public bool IsCompleted
        {
            get { return _asyncResult.IsCompleted && _lock.WaitOne(0, false); }
        }

        //---------------------------------------------------------
        ///<summary>
        ///Calculated value. If the value is not yet calculated,
        /// the calculation will be completed synchronously.
        ///</summary>
        public TResult Value
        {
            get
            {
                if (!IsCompleted)
                {
                    _asyncResult.AsyncWaitHandle.WaitOne();
                    _lock.WaitOne();
                }
                return _value;
            }
        }

        //---------------------------------------------------------
        /// <summary>
        /// Exception that was thrown if any.
        /// </summary>
        public Exception Exception
        {
            get { return _exception; }
        }

        //---------------------------------------------------------
        /// <summary>
        /// Determines if there was an exception while executing the
        /// evaluation.
        /// </summary>
        public bool HasThrownException
        {
            get { return _exception != null; }
        }

    }


Nothing Impossible
roboto

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Feedback

# re: The Future Revealed

Nice -- now here's a question:

Should it be the responsibility of the client to check for an exception or should getting the value rethrow it?

As a consumer, I might not want to clutter my code with checking the value for null then checking to see if there's an exception. 2/12/2009 4:45 PM | Paul Jackson

# re: The Future Revealed

Hi Paul,

That's a very valid point! Indeed, 'user experience' could be improved this way (and the interface reduced).
Thanks, roboto 2/16/2009 9:13 AM | roboto

Post a comment