/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Threading; using Dbg = System.Management.Automation.Diagnostics; namespace System.Management.Automation.Runspaces { /// /// Base class for AsyncResult objects that are returned by various /// Async operations supported by RunspacePool , PowerShell types /// internal class AsyncResult : IAsyncResult { #region Private Data private ManualResetEvent _completedWaitHandle; // exception occured in the async thread. // user supplied state object // Invoke on thread (remote debugging support). private AutoResetEvent _invokeOnThreadEvent; private WaitCallback _invokeCallback; private object _invokeCallbackState; #endregion #region Constructor /// /// Constructor /// /// /// Instace Id of the object creating this instance /// /// /// A AsyncCallback to call once the async operation completes. /// /// /// A user supplied state object /// internal AsyncResult(Guid ownerId, AsyncCallback callback, object state) { Dbg.Assert(Guid.Empty != ownerId, "ownerId cannot be empty"); OwnerId = ownerId; Callback = callback; AsyncState = state; } #endregion #region IAsync Overrides /// /// This always returns false /// public bool CompletedSynchronously { get { return false; } } /// /// Gets an indication whether the asynchronous operation has completed. /// public bool IsCompleted { get; private set; } /// /// This is not supported and returns null. /// public object AsyncState { get; } /// /// Gets a System.Threading.WaitHandle that is used to wait for an asynchronous /// operation to complete. /// public WaitHandle AsyncWaitHandle { get { if (null == _completedWaitHandle) { lock (SyncObject) { if (null == _completedWaitHandle) { _completedWaitHandle = new ManualResetEvent(IsCompleted); } } } return _completedWaitHandle; } } #endregion #region properties / methods /// /// Instance Id of the object owning this async result. /// internal Guid OwnerId { get; } /// /// Gets the exception that occurred while processing the /// async operation. /// internal Exception Exception { get; private set; } /// /// User supplied callback. /// internal AsyncCallback Callback { get; } /// /// SyncObject /// internal object SyncObject { get; } = new object(); /// /// Marks the async operation as completed. /// /// /// Exception occured. null if no exception occured /// internal void SetAsCompleted(Exception exception) { //Dbg.Assert(!isCompleted, "AsynResult already completed"); if (IsCompleted) { return; } lock (SyncObject) { if (IsCompleted) { return; } else { Exception = exception; IsCompleted = true; // release the threads waiting on this operation. SignalWaitHandle(); } } // call the user supplied callback if (null != Callback) { Callback(this); } } /// /// Release the asyncResult without calling the callback. /// internal void Release() { if (!IsCompleted) { IsCompleted = true; SignalWaitHandle(); } } #endregion #region internal methods /// /// Signal wait handle of this async result. /// internal void SignalWaitHandle() { lock (SyncObject) { if (null != _completedWaitHandle) { _completedWaitHandle.Set(); } } } /// /// Wait for the operation to complete and throw the exception if any. /// internal void EndInvoke() { _invokeOnThreadEvent = new AutoResetEvent(false); // Start the thread wait loop. WaitHandle[] waitHandles = new WaitHandle[2] { AsyncWaitHandle, _invokeOnThreadEvent }; bool waiting = true; while (waiting) { int waitIndex = WaitHandle.WaitAny(waitHandles); if (waitIndex == 0) { waiting = false; } else { // Invoke callback on thread. try { _invokeCallback(_invokeCallbackState); } catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); } } } AsyncWaitHandle.Dispose(); _completedWaitHandle = null; // Allow early GC _invokeOnThreadEvent.Dispose(); _invokeOnThreadEvent = null; // Allow early GC // Operation is done: if an exception occured, throw it if (null != Exception) { throw Exception; } } /// /// Use blocked thread to invoke callback delegate. /// /// Callback delegate /// Callback state internal bool InvokeCallbackOnThread(WaitCallback callback, object state) { if (callback == null) { throw new PSArgumentNullException("callback"); } _invokeCallback = callback; _invokeCallbackState = state; // Signal thread to run callback. if (_invokeOnThreadEvent != null) { _invokeOnThreadEvent.Set(); return true; } return false; } #endregion } }