// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== /*============================================================ ** ** Class: Progress ** ** Microsoft ** ** ** Purpose: Event-based implementation of IProgress. ** ** ===========================================================*/ using System; using System.Threading; using System.Diagnostics; using System.Diagnostics.Contracts; namespace System { /// /// Provides an IProgress{T} that invokes callbacks for each reported progress value. /// /// Specifies the type of the progress report value. /// /// Any handler provided to the constructor or event handlers registered with /// the event are invoked through a /// instance captured /// when the instance is constructed. If there is no current SynchronizationContext /// at the time of construction, the callbacks will be invoked on the ThreadPool. /// public class Progress : IProgress { /// The synchronization context captured upon construction. This will never be null. private readonly SynchronizationContext m_synchronizationContext; /// The handler specified to the constructor. This may be null. private readonly Action m_handler; /// A cached delegate used to post invocation to the synchronization context. private readonly SendOrPostCallback m_invokeHandlers; /// Initializes the . public Progress() { // Capture the current synchronization context. "current" is determined by CurrentNoFlow, // which doesn't consider the sync ctx flown with an ExecutionContext, avoiding // sync ctx reference identity issues where the sync ctx for one thread could be Current on another. // If there is no current context, we use a default instance targeting the ThreadPool. m_synchronizationContext = SynchronizationContext.CurrentNoFlow ?? ProgressStatics.DefaultContext; Contract.Assert(m_synchronizationContext != null); m_invokeHandlers = new SendOrPostCallback(InvokeHandlers); } /// Initializes the with the specified callback. /// /// A handler to invoke for each reported progress value. This handler will be invoked /// in addition to any delegates registered with the event. /// Depending on the instance captured by /// the at construction, it's possible that this handler instance /// could be invoked concurrently with itself. /// /// The is null (Nothing in Visual Basic). public Progress(Action handler) : this() { if (handler == null) throw new ArgumentNullException("handler"); m_handler = handler; } /// Raised for each reported progress value. /// /// Handlers registered with this event will be invoked on the /// captured when the instance was constructed. /// public event EventHandler ProgressChanged; /// Reports a progress change. /// The value of the updated progress. protected virtual void OnReport(T value) { // If there's no handler, don't bother going through the sync context. // Inside the callback, we'll need to check again, in case // an event handler is removed between now and then. Action handler = m_handler; EventHandler changedEvent = ProgressChanged; if (handler != null || changedEvent != null) { // Post the processing to the sync context. // (If T is a value type, it will get boxed here.) m_synchronizationContext.Post(m_invokeHandlers, value); } } /// Reports a progress change. /// The value of the updated progress. void IProgress.Report(T value) { OnReport(value); } /// Invokes the action and event callbacks. /// The progress value. private void InvokeHandlers(object state) { T value = (T)state; Action handler = m_handler; EventHandler changedEvent = ProgressChanged; if (handler != null) handler(value); if (changedEvent != null) changedEvent(this, value); } } /// Holds static values for . /// This avoids one static instance per type T. internal static class ProgressStatics { /// A default synchronization context that targets the ThreadPool. internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); } }