// Token: 0x06004044 RID: 16452 RVA: 0x000EF724 File Offset: 0x000ED924
        private bool TrySetFromTask(Task task, bool lookForOce)
        {
            if (AsyncCausalityTracer.LoggingOn)
            {
                AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, base.Id, CausalityRelation.Join);
            }
            bool result = false;

            switch (task.Status)
            {
            case TaskStatus.RanToCompletion:
            {
                Task <TResult> task2 = task as Task <TResult>;
                if (AsyncCausalityTracer.LoggingOn)
                {
                    AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, base.Id, AsyncCausalityStatus.Completed);
                }
                if (Task.s_asyncDebuggingEnabled)
                {
                    Task.RemoveFromActiveTasks(base.Id);
                }
                result = base.TrySetResult((task2 != null) ? task2.Result : default(TResult));
                break;
            }

            case TaskStatus.Canceled:
                result = base.TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
                break;

            case TaskStatus.Faulted:
            {
                ReadOnlyCollection <ExceptionDispatchInfo> exceptionDispatchInfos = task.GetExceptionDispatchInfos();
                ExceptionDispatchInfo      exceptionDispatchInfo;
                OperationCanceledException ex;
                if (lookForOce && exceptionDispatchInfos.Count > 0 && (exceptionDispatchInfo = exceptionDispatchInfos[0]) != null && (ex = (exceptionDispatchInfo.SourceException as OperationCanceledException)) != null)
                {
                    result = base.TrySetCanceled(ex.CancellationToken, exceptionDispatchInfo);
                }
                else
                {
                    result = base.TrySetException(exceptionDispatchInfos);
                }
                break;
            }
            }
            return(result);
        }
예제 #2
0
        /// <summary>Initializes a new continuation.</summary>
        /// <param name="task">The task to be activated.</param>
        /// <param name="options">The continuation options.</param>
        /// <param name="scheduler">The scheduler to use for the continuation.</param>
        internal StandardTaskContinuation(Task task, TaskContinuationOptions options, TaskScheduler scheduler)
        {
            Contract.Requires(task != null, "TaskContinuation ctor: task is null");
            Contract.Requires(scheduler != null, "TaskContinuation ctor: scheduler is null");
            m_task          = task;
            m_options       = options;
            m_taskScheduler = scheduler;
            if (AsyncCausalityTracer.LoggingOn)
            {
                AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, m_task.Id, "Task.ContinueWith: " + ((Delegate)task.m_action).Method.Name, 0);
            }

            if (Task.s_asyncDebuggingEnabled)
            {
                Task.AddToActiveTasks(m_task);
            }
        }
예제 #3
0
        /// <summary>Initializes a new continuation.</summary>
        /// <param name="task">The task to be activated.</param>
        /// <param name="options">The continuation options.</param>
        /// <param name="scheduler">The scheduler to use for the continuation.</param>
        internal StandardTaskContinuation(Task task, TaskContinuationOptions options, TaskScheduler scheduler)
        {
            Debug.Assert(task != null, "TaskContinuation ctor: task is null");
            Debug.Assert(scheduler != null, "TaskContinuation ctor: scheduler is null");
            m_task          = task;
            m_options       = options;
            m_taskScheduler = scheduler;
            if (AsyncCausalityTracer.LoggingOn)
            {
                AsyncCausalityTracer.TraceOperationCreation(m_task, "Task.ContinueWith: " + task.m_action.Method.Name);
            }

            if (Task.s_asyncDebuggingEnabled)
            {
                Task.AddToActiveTasks(m_task);
            }
        }
예제 #4
0
        void ExecuteWithThreadLocal(ref Task currentTaskSlot)
        {
            // Remember the current task so we can restore it after running, and then
            Task previousTask = currentTaskSlot;

            try
            {
                // place the current task into TLS.
                currentTaskSlot = this;

                ExecutionContext ec = CapturedContext;
                if (ec == null)
                {
                    // No context, just run the task directly.
                    Execute();
                }
                else
                {
                    // Run the task.  We need a simple shim that converts the
                    // object back into a Task object, so that we can Execute it.

                    // Lazily initialize the callback delegate; benign ----
                    var callback = s_ecCallback;
                    if (callback == null)
                    {
                        s_ecCallback = callback = new ContextCallback(ExecutionContextCallback);
                    }
#if PFX_LEGACY_3_5
                    ExecutionContext.Run(ec, callback, this);
#else
                    ExecutionContext.Run(ec, callback, this, true);
#endif
                }

                if (AsyncCausalityTracer.LoggingOn)
                {
                    AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
                }

                Finish(true);
            }
            finally
            {
                currentTaskSlot = previousTask;
            }
        }
예제 #5
0
 internal static void TraceOperationRelation(CausalityTraceLevel traceLevel, int taskId, CausalityRelation relation)
 {
     try
     {
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.ETW) != (AsyncCausalityTracer.Loggers) 0)
         {
             TplEtwProvider.Log.TraceOperationRelation(taskId, relation);
         }
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.CausalityTracer) != (AsyncCausalityTracer.Loggers) 0)
         {
             AsyncCausalityTracer.s_TracerFactory.TraceOperationRelation((CausalityTraceLevel)traceLevel, CausalitySource.Library, AsyncCausalityTracer.s_PlatformId, AsyncCausalityTracer.GetOperationId((uint)taskId), (CausalityRelation)relation);
         }
     }
     catch (Exception ex)
     {
         AsyncCausalityTracer.LogAndDisable(ex);
     }
 }
예제 #6
0
 internal static void TraceOperationCreation(CausalityTraceLevel traceLevel, int taskId, string operationName, ulong relatedContext)
 {
     try
     {
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.ETW) != (AsyncCausalityTracer.Loggers) 0)
         {
             TplEtwProvider.Log.TraceOperationBegin(taskId, operationName, (long)relatedContext);
         }
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.CausalityTracer) != (AsyncCausalityTracer.Loggers) 0)
         {
             AsyncCausalityTracer.s_TracerFactory.TraceOperationCreation((CausalityTraceLevel)traceLevel, CausalitySource.Library, AsyncCausalityTracer.s_PlatformId, AsyncCausalityTracer.GetOperationId((uint)taskId), operationName, relatedContext);
         }
     }
     catch (Exception ex)
     {
         AsyncCausalityTracer.LogAndDisable(ex);
     }
 }
예제 #7
0
 internal static void TraceSynchronousWorkCompletion(CausalityTraceLevel traceLevel, CausalitySynchronousWork work)
 {
     try
     {
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.ETW) != (AsyncCausalityTracer.Loggers) 0)
         {
             TplEtwProvider.Log.TraceSynchronousWorkEnd(work);
         }
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.CausalityTracer) != (AsyncCausalityTracer.Loggers) 0)
         {
             AsyncCausalityTracer.s_TracerFactory.TraceSynchronousWorkCompletion((CausalityTraceLevel)traceLevel, CausalitySource.Library, (CausalitySynchronousWork)work);
         }
     }
     catch (Exception ex)
     {
         AsyncCausalityTracer.LogAndDisable(ex);
     }
 }
예제 #8
0
 internal static void TraceSynchronousWorkStart(CausalityTraceLevel traceLevel, int taskId, CausalitySynchronousWork work)
 {
     try
     {
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.ETW) != (AsyncCausalityTracer.Loggers) 0)
         {
             TplEtwProvider.Log.TraceSynchronousWorkBegin(taskId, work);
         }
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.CausalityTracer) != (AsyncCausalityTracer.Loggers) 0)
         {
             AsyncCausalityTracer.s_TracerFactory.TraceSynchronousWorkStart((CausalityTraceLevel)traceLevel, CausalitySource.Library, AsyncCausalityTracer.s_PlatformId, AsyncCausalityTracer.GetOperationId((uint)taskId), (CausalitySynchronousWork)work);
         }
     }
     catch (Exception ex)
     {
         AsyncCausalityTracer.LogAndDisable(ex);
     }
 }
예제 #9
0
 internal static void TraceOperationCompletion(CausalityTraceLevel traceLevel, int taskId, AsyncCausalityStatus status)
 {
     try
     {
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.ETW) != (AsyncCausalityTracer.Loggers) 0)
         {
             TplEtwProvider.Log.TraceOperationEnd(taskId, status);
         }
         if ((AsyncCausalityTracer.f_LoggingOn & AsyncCausalityTracer.Loggers.CausalityTracer) == (AsyncCausalityTracer.Loggers) 0)
         {
             return;
         }
         AsyncCausalityTracer.s_TracerFactory.TraceOperationCompletion((Windows.Foundation.Diagnostics.CausalityTraceLevel)traceLevel, CausalitySource.Library, AsyncCausalityTracer.s_PlatformId, AsyncCausalityTracer.GetOperationId((uint)taskId), (Windows.Foundation.Diagnostics.AsyncCausalityStatus)status);
     }
     catch (Exception ex)
     {
         AsyncCausalityTracer.LogAndDisable(ex);
     }
 }
 // Token: 0x0600403F RID: 16447 RVA: 0x000EF5B8 File Offset: 0x000ED7B8
 public UnwrapPromise(Task outerTask, bool lookForOce) : base(null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent)
 {
     this._lookForOce = lookForOce;
     this._state      = 0;
     if (AsyncCausalityTracer.LoggingOn)
     {
         AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, base.Id, "Task.Unwrap", 0UL);
     }
     if (Task.s_asyncDebuggingEnabled)
     {
         Task.AddToActiveTasks(this);
     }
     if (outerTask.IsCompleted)
     {
         this.ProcessCompletedOuterTask(outerTask);
         return;
     }
     outerTask.AddCompletionAction(this);
 }
 // Token: 0x0600415C RID: 16732 RVA: 0x000F2FD8 File Offset: 0x000F11D8
 protected override void OnEventCommand(EventCommandEventArgs command)
 {
     if (command.Command == EventCommand.Enable)
     {
         AsyncCausalityTracer.EnableToETW(true);
     }
     else if (command.Command == EventCommand.Disable)
     {
         AsyncCausalityTracer.EnableToETW(false);
     }
     if (base.IsEnabled(EventLevel.Informational, (EventKeywords)128L))
     {
         ActivityTracker.Instance.Enable();
     }
     else
     {
         this.TasksSetActivityIds = base.IsEnabled(EventLevel.Informational, (EventKeywords)65536L);
     }
     this.Debug           = base.IsEnabled(EventLevel.Informational, (EventKeywords)131072L);
     this.DebugActivityId = base.IsEnabled(EventLevel.Informational, (EventKeywords)262144L);
 }
예제 #12
0
        static AsyncCausalityTracer()
        {
            if (!Environment.IsWinRTSupported)
            {
                return;
            }
            string activatableClassId = "Windows.Foundation.Diagnostics.AsyncCausalityTracer";
            Guid   iid     = new Guid(1350896422, (short)9854, (short)17691, (byte)168, (byte)144, (byte)171, (byte)106, (byte)55, (byte)2, (byte)69, (byte)238);
            object factory = (object)null;

            try
            {
                if (UnsafeNativeMethods.RoGetActivationFactory(activatableClassId, ref iid, out factory) < 0 || factory == null)
                {
                    return;
                }
                AsyncCausalityTracer.s_TracerFactory = (IAsyncCausalityTracerStatics)factory;
                AsyncCausalityTracer.s_TracerFactory.add_TracingStatusChanged(new EventHandler <TracingStatusChangedEventArgs>(AsyncCausalityTracer.TracingStatusChangedHandler));
            }
            catch (Exception ex)
            {
                AsyncCausalityTracer.LogAndDisable(ex);
            }
        }
예제 #13
0
        private bool TrySetFromTask(Task task, bool lookForOce)
        {
            if (AsyncCausalityTracer.LoggingOn)
            {
                AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
            }
            bool flag = false;

            switch (task.Status)
            {
            case TaskStatus.RanToCompletion:
                Task <TResult> task1 = task as Task <TResult>;
                if (AsyncCausalityTracer.LoggingOn)
                {
                    AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
                }
                if (Task.s_asyncDebuggingEnabled)
                {
                    Task.RemoveFromActiveTasks(this.Id);
                }
                flag = this.TrySetResult(task1 != null ? task1.Result : default(TResult));
                break;

            case TaskStatus.Canceled:
                flag = this.TrySetCanceled(task.CancellationToken, (object)task.GetCancellationExceptionDispatchInfo());
                break;

            case TaskStatus.Faulted:
                ReadOnlyCollection <ExceptionDispatchInfo> exceptionDispatchInfos = task.GetExceptionDispatchInfos();
                ExceptionDispatchInfo      exceptionDispatchInfo;
                OperationCanceledException canceledException;
                flag = !lookForOce || exceptionDispatchInfos.Count <= 0 || ((exceptionDispatchInfo = exceptionDispatchInfos[0]) == null || (canceledException = exceptionDispatchInfo.SourceException as OperationCanceledException) == null) ? this.TrySetException((object)exceptionDispatchInfos) : this.TrySetCanceled(canceledException.CancellationToken, (object)exceptionDispatchInfo);
                break;
            }
            return(flag);
        }
예제 #14
0
        static AsyncCausalityTracer()
        {
            if (!Environment.IsWinRTSupported)
            {
                return;
            }
            string activatableClassId = "Windows.Foundation.Diagnostics.AsyncCausalityTracer";
            Guid   guid = new Guid(1350896422, 9854, 17691, 168, 144, 171, 106, 55, 2, 69, 238);
            object obj  = null;

            try
            {
                int num = Microsoft.Win32.UnsafeNativeMethods.RoGetActivationFactory(activatableClassId, ref guid, out obj);
                if (num >= 0 && obj != null)
                {
                    AsyncCausalityTracer.s_TracerFactory = (IAsyncCausalityTracerStatics)obj;
                    EventRegistrationToken eventRegistrationToken = AsyncCausalityTracer.s_TracerFactory.add_TracingStatusChanged(new EventHandler <TracingStatusChangedEventArgs>(AsyncCausalityTracer.TracingStatusChangedHandler));
                }
            }
            catch (Exception ex)
            {
                AsyncCausalityTracer.LogAndDisable(ex);
            }
        }
예제 #15
0
        /// <summary>
        /// Get callbacks when the ETW sends us commands`
        /// </summary>
        protected override void OnEventCommand(EventCommandEventArgs command)
        {
            // To get the AsyncCausality events, we need to inform the AsyncCausalityTracer
            if (command.Command == EventCommand.Enable)
            {
                AsyncCausalityTracer.EnableToETW(true);
            }
            else if (command.Command == EventCommand.Disable)
            {
                AsyncCausalityTracer.EnableToETW(false);
            }

            if (IsEnabled(EventLevel.Informational, Keywords.TasksFlowActivityIds))
            {
                ActivityTracker.Instance.Enable();
            }
            else
            {
                TasksSetActivityIds = IsEnabled(EventLevel.Informational, Keywords.TasksSetActivityIds);
            }

            Debug           = IsEnabled(EventLevel.Informational, Keywords.Debug);
            DebugActivityId = IsEnabled(EventLevel.Informational, Keywords.DebugActivityId);
        }
예제 #16
0
        /// <summary>Invokes the continuation for the target completion task.</summary>
        /// <param name="completedTask">The completed task.</param>
        /// <param name="canInlineContinuationTask">Whether the continuation can be inlined.</param>
        internal override void Run(Task completedTask, bool canInlineContinuationTask)
        {
            Debug.Assert(completedTask != null);
            Debug.Assert(completedTask.IsCompleted, "ContinuationTask.Run(): completedTask not completed");

            Task?continuationTask = m_task;

            Debug.Assert(continuationTask != null);
            m_task = null;

            // Check if the completion status of the task works with the desired
            // activation criteria of the TaskContinuationOptions.
            TaskContinuationOptions options = m_options;
            bool isRightKind =
                completedTask.IsCompletedSuccessfully ?
                (options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
                (completedTask.IsCanceled ?
                 (options & TaskContinuationOptions.NotOnCanceled) == 0 :
                 (options & TaskContinuationOptions.NotOnFaulted) == 0);

            // If the completion status is allowed, run the continuation.
            if (isRightKind)
            {
                // If the task was cancel before running (e.g a ContinueWhenAll with a cancelled caancelation token)
                // we will still flow it to ScheduleAndStart() were it will check the status before running
                // We check here to avoid faulty logs that contain a join event to an operation that was already set as completed.
                if (!continuationTask.IsCanceled && AsyncCausalityTracer.LoggingOn)
                {
                    // Log now that we are sure that this continuation is being ran
                    AsyncCausalityTracer.TraceOperationRelation(continuationTask, CausalityRelation.AssignDelegate);
                }
                continuationTask.m_taskScheduler = m_taskScheduler;

                // Either run directly or just queue it up for execution, depending
                // on whether synchronous or asynchronous execution is wanted.
                if (canInlineContinuationTask &&                                   // inlining is allowed by the caller
                    (options & TaskContinuationOptions.ExecuteSynchronously) != 0) // synchronous execution was requested by the continuation's creator
                {
                    InlineIfPossibleOrElseQueue(continuationTask, needsProtection: true);
                }
                else
                {
                    try { continuationTask.ScheduleAndStart(needsProtection: true); }
                    catch (TaskSchedulerException)
                    {
                        // No further action is necessary -- ScheduleAndStart() already transitioned the
                        // task to faulted.  But we want to make sure that no exception is thrown from here.
                    }
                }
            }
            else
            {
                // Otherwise, the final state of this task does not match the desired continuation activation criteria; cancel it to denote this.
                Task.ContingentProperties?cp = continuationTask.m_contingentProperties;  // no need to volatile read, as we only care about the token, which is only assignable at construction
                if (cp is null || cp.m_cancellationToken == default)
                {
                    // With no cancellation token, use an optimized path that doesn't need to account for concurrent completion.
                    // This is primarily valuable for continuations created with TaskContinuationOptions.NotOn* options, where
                    // we'll cancel the continuation if it's not needed.
                    continuationTask.InternalCancelContinueWithInitialState();
                }
예제 #17
0
        /// <summary>Completes the task from the completed asynchronous operation.</summary>
        /// <param name="asyncInfo">The asynchronous operation.</param>
        /// <param name="getResultsFunction">A function used to retrieve the TResult from the async operation; may be null.</param>
        /// <param name="asyncStatus">The status of the asynchronous operation.</param>
        private void Complete(IAsyncInfo asyncInfo, Func <IAsyncInfo, TResult> getResultsFunction, AsyncStatus asyncStatus)
        {
            if (asyncInfo == null)
            {
                throw new ArgumentNullException(nameof(asyncInfo));
            }
            Contract.EndContractBlock();

            if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled)
            {
                System.Threading.Tasks.Task.RemoveFromActiveTasks(base.Task.Id);
            }

            try
            {
                Debug.Assert(asyncInfo.Status == asyncStatus,
                             "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?");

                // Assuming a correct underlying implementation, the task should not have been
                // completed yet.  If it is completed, we shouldn't try to do any further work
                // with the operation or the task, as something is horked.
                bool taskAlreadyCompleted = Task.IsCompleted;

                Debug.Assert(!taskAlreadyCompleted, "Expected the task to not yet be completed.");

                if (taskAlreadyCompleted)
                {
                    throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                }

                // Clean up our registration with the cancellation token, noting that we're now in the process of cleaning up.
                CancellationTokenRegistration ctr;
                lock (StateLock)
                {
                    _completing = true;
                    ctr         = _ctr; // under lock to avoid torn reads
                    _ctr        = default(CancellationTokenRegistration);
                }
                ctr.Unregister(); // It's ok if we end up unregistering a not-initialized registration; it'll just be a nop.

                try
                {
                    // Find out how the async operation completed.  It must be in a terminal state.
                    bool terminalState = asyncStatus == AsyncStatus.Completed ||
                                         asyncStatus == AsyncStatus.Canceled ||
                                         asyncStatus == AsyncStatus.Error;

                    Debug.Assert(terminalState, "The async operation should be in a terminal state.");

                    if (!terminalState)
                    {
                        throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                    }

                    // Retrieve the completion data from the IAsyncInfo.
                    TResult   result = default(TResult);
                    Exception error  = null;
                    if (asyncStatus == AsyncStatus.Error)
                    {
                        error = asyncInfo.ErrorCode;

                        // Defend against a faulty IAsyncInfo implementation:
                        if (error == null)
                        {
                            Debug.Assert(false, "IAsyncInfo.Status == Error, but ErrorCode returns a null Exception (implying S_OK).");
                            error = new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                        }
                        else
                        {
                            error = RestrictedErrorInfoHelper.AttachRestrictedErrorInfo(asyncInfo.ErrorCode);
                        }
                    }
                    else if (asyncStatus == AsyncStatus.Completed && getResultsFunction != null)
                    {
                        try
                        {
                            result = getResultsFunction(asyncInfo);
                        }
                        catch (Exception resultsEx)
                        {
                            // According to the WinRT team, this can happen in some egde cases, such as marshalling errors in GetResults.
                            error       = resultsEx;
                            asyncStatus = AsyncStatus.Error;
                        }
                    }

                    // Nothing to retrieve for a canceled operation or for a completed operation with no result.

                    // Complete the task based on the previously retrieved results:
                    bool success = false;

                    switch (asyncStatus)
                    {
                    case AsyncStatus.Completed:
                        if (AsyncCausalityTracer.LoggingOn)
                        {
                            AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, base.Task.Id, AsyncCausalityStatus.Completed);
                        }
                        success = base.TrySetResult(result);
                        break;

                    case AsyncStatus.Error:
                        Debug.Assert(error != null, "The error should have been retrieved previously.");
                        success = base.TrySetException(error);
                        break;

                    case AsyncStatus.Canceled:
                        success = base.TrySetCanceled(_ct.IsCancellationRequested ? _ct : new CancellationToken(true));
                        break;
                    }

                    Debug.Assert(success, "Expected the outcome to be successfully transfered to the task.");
                }
                catch (Exception exc)
                {
                    // This really shouldn't happen, but could in a variety of misuse cases
                    // such as a faulty underlying IAsyncInfo implementation.
                    Debug.Assert(false, string.Format("Unexpected exception in Complete: {0}", exc.ToString()));

                    if (AsyncCausalityTracer.LoggingOn)
                    {
                        AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, base.Task.Id, AsyncCausalityStatus.Error);
                    }

                    // For these cases, store the exception into the task so that it makes its way
                    // back to the caller.  Only if something went horribly wrong and we can't store the exception
                    // do we allow it to be propagated out to the invoker of the Completed handler.
                    if (!base.TrySetException(exc))
                    {
                        Debug.Assert(false, "The task was already completed and thus the exception couldn't be stored.");
                        throw;
                    }
                }
            }
            finally
            {
                // We may be called on an STA thread which we don't own, so make sure that the RCW is released right
                // away. Otherwise, if we leave it up to the finalizer, the apartment may already be gone.
                if (Marshal.IsComObject(asyncInfo))
                {
                    Marshal.ReleaseComObject(asyncInfo);
                }
            }
        } // private void Complete(..)