private void _BackgroundWorkerRunWorkerCompleted(
            object sender,
            RunWorkerCompletedEventArgs e)
        {
            BackgroundWorker bw = sender as BackgroundWorker;

            try
            {
                AsyncSolveContext ctx = null;
                if (_workers.TryGetValue(bw, out ctx))
                {
                    _HandleWorkerCompleted(e, ctx);
                }
                else
                {
                    // normally haveWorker = false only if operation was cancelled
                    Debug.Assert(e.Cancelled);
                }
            }
            finally
            {
                bw.Dispose(); // actually does nothing
                _workers.Remove(bw);
            }
        }
        private void _HandleWorkerCompleted(
            RunWorkerCompletedEventArgs e,
            AsyncSolveContext ctx)
        {
            Guid id = ctx.OperationID;

            // NOTE: don't need to handle Cancelled status here, we raise SolveCompleted
            // event immediately after setting worker to cancellation state
            if (e.Cancelled)
            {
                return;
            }

            if (e.Error != null)
            {
                // operation failed
                _NotifyAsyncSolveCompleted(e.Error, false, id);

                return;
            }

            var operation      = ctx.Task;
            var resultProvider = (Func <SolveTaskResult>)e.Result;

            bool      isConverted = false;
            Exception error       = null;
            var       result      = _ConvertResult(resultProvider, out error);

            if (result != null)
            {
                // check if we need to run next task.
                var nextTask = result.NextTask;
                if (nextTask != null)
                {
                    // run next task with the same operation id.
                    isConverted = _TryRunAsync(nextTask, id, out error);
                }
                else
                {
                    // converted successfully, provide the result
                    _NotifyAsyncSolveCompleted(null, false, id, result.SolveResult);
                    isConverted = true;
                }
            }

            if (!isConverted)
            {
                // conversion failed, set error
                _NotifyAsyncSolveCompleted(error, false, id);
            }
        }
        private void _RunAsync(IAsyncSolveTask task, Guid operationID)
        {
            Debug.Assert(task != null);

            var context = new AsyncSolveContext
            {
                Task        = task,
                OperationID = operationID,
            };

            var worker = new BackgroundWorker();

            worker.WorkerSupportsCancellation = true;
            worker.DoWork             += _BackgroundWorkerDoWork;
            worker.RunWorkerCompleted += _BackgroundWorkerRunWorkerCompleted;

            _workers.Add(worker, context);

            worker.RunWorkerAsync(task);
        }
        private void _RunAsync(IAsyncSolveTask task, Guid operationID)
        {
            Debug.Assert(task != null);

            var context = new AsyncSolveContext
            {
                Task = task,
                OperationID = operationID,
            };

            var worker = new BackgroundWorker();
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += _BackgroundWorkerDoWork;
            worker.RunWorkerCompleted += _BackgroundWorkerRunWorkerCompleted;

            _workers.Add(worker, context);

            worker.RunWorkerAsync(task);
        }
        private void _HandleWorkerCompleted(
            RunWorkerCompletedEventArgs e,
            AsyncSolveContext ctx)
        {
            Guid id = ctx.OperationID;

            // NOTE: don't need to handle Cancelled status here, we raise SolveCompleted
            // event immediately after setting worker to cancellation state
            if (e.Cancelled)
            {
                return;
            }

            if (e.Error != null)
            {
                // operation failed
                _NotifyAsyncSolveCompleted(e.Error, false, id);

                return;
            }

            var operation = ctx.Task;
            var resultProvider = (Func<SolveTaskResult>)e.Result;

            bool isConverted = false;
            Exception error = null;
            var result = _ConvertResult(resultProvider, out error);
            if (result != null)
            {
                // check if we need to run next task.
                var nextTask = result.NextTask;
                if (nextTask != null)
                {
                    // run next task with the same operation id.
                    isConverted = _TryRunAsync(nextTask, id, out error);
                }
                else
                {
                    // converted successfully, provide the result
                    _NotifyAsyncSolveCompleted(null, false, id, result.SolveResult);
                    isConverted = true;
                }
            }

            if (!isConverted)
            {
                // conversion failed, set error
                _NotifyAsyncSolveCompleted(error, false, id);
            }
        }