#if MONO using System; using System.Collections.Generic; using System.Runtime; using System.Runtime.InteropServices; using System.Security.Permissions; namespace System.Threading { /// Signals to a that it should be canceled. [__DynamicallyInvokable, ComVisible(false)] [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] public class CancellationTokenSource : IDisposable { private static readonly CancellationTokenSource _staticSource_Set = new CancellationTokenSource(true); private static readonly CancellationTokenSource _staticSource_NotCancelable = new CancellationTokenSource(false); private static readonly int s_nLists = (PlatformHelper.ProcessorCount > 24) ? 24 : PlatformHelper.ProcessorCount; private volatile ManualResetEvent m_kernelEvent; private volatile SparselyPopulatedArray[] m_registeredCallbacksLists; private volatile int m_state; private volatile int m_threadIDExecutingCallbacks = -1; private bool m_disposed; private CancellationTokenRegistration[] m_linkingRegistrations; private static readonly Action s_LinkedTokenCancelDelegate = new Action(CancellationTokenSource.LinkedTokenCancelDelegate); private volatile CancellationCallbackInfo m_executingCallback; private volatile Timer m_timer; private static readonly TimerCallback s_timerCallback = new TimerCallback(CancellationTokenSource.TimerCallbackLogic); private const int CANNOT_BE_CANCELED = 0; private const int NOT_CANCELED = 1; private const int NOTIFYING = 2; private const int NOTIFYINGCOMPLETE = 3; /// Gets whether cancellation has been requested for this . /// Whether cancellation has been requested for this . [__DynamicallyInvokable] public bool IsCancellationRequested { [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return this.m_state >= 2; } } internal bool IsCancellationCompleted { get { return this.m_state == 3; } } internal bool IsDisposed { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return this.m_disposed; } } internal int ThreadIDExecutingCallbacks { get { return this.m_threadIDExecutingCallbacks; } set { this.m_threadIDExecutingCallbacks = value; } } /// Gets the associated with this . /// The associated with this . /// The token source has been disposed. [__DynamicallyInvokable] public CancellationToken Token { [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { this.ThrowIfDisposed(); return new CancellationToken(this); } } internal bool CanBeCanceled { [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return this.m_state != 0; } } internal WaitHandle WaitHandle { get { this.ThrowIfDisposed(); if (this.m_kernelEvent != null) { return this.m_kernelEvent; } ManualResetEvent manualResetEvent = new ManualResetEvent(false); if (Interlocked.CompareExchange(ref this.m_kernelEvent, manualResetEvent, null) != null) { ((IDisposable)manualResetEvent).Dispose(); } if (this.IsCancellationRequested) { this.m_kernelEvent.Set(); } return this.m_kernelEvent; } } internal CancellationCallbackInfo ExecutingCallback { get { return this.m_executingCallback; } } /// Initializes the . [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public CancellationTokenSource() { this.m_state = 1; } [__DynamicallyInvokable] public CancellationTokenSource(TimeSpan delay) { long num = (long)delay.TotalMilliseconds; if (num < -1L || num > 2147483647L) { throw new ArgumentOutOfRangeException("delay"); } this.InitializeWithTimer((int)num); } [__DynamicallyInvokable] public CancellationTokenSource(int millisecondsDelay) { if (millisecondsDelay < -1) { throw new ArgumentOutOfRangeException("millisecondsDelay"); } this.InitializeWithTimer(millisecondsDelay); } /// Communicates a request for cancellation. /// This has been disposed. /// An aggregate exception containing all the exceptions thrown by the registered callbacks on the associated . [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public void Cancel() { this.Cancel(false); } /// Communicates a request for cancellation. /// Specifies whether exceptions should immediately propagate. /// This has been disposed. /// An aggregate exception containing all the exceptions thrown by the registered callbacks on the associated . [__DynamicallyInvokable] public void Cancel(bool throwOnFirstException) { this.ThrowIfDisposed(); this.NotifyCancellation(throwOnFirstException); } [__DynamicallyInvokable] public void CancelAfter(TimeSpan delay) { long num = (long)delay.TotalMilliseconds; if (num < -1L || num > 2147483647L) { throw new ArgumentOutOfRangeException("delay"); } this.CancelAfter((int)num); } [__DynamicallyInvokable] public void CancelAfter(int millisecondsDelay) { this.ThrowIfDisposed(); if (millisecondsDelay < -1) { throw new ArgumentOutOfRangeException("millisecondsDelay"); } if (this.IsCancellationRequested) { return; } if (this.m_timer == null) { Timer timer = new Timer(CancellationTokenSource.s_timerCallback, this, -1, -1); if (Interlocked.CompareExchange(ref this.m_timer, timer, null) != null) { timer.Dispose(); } } try { this.m_timer.Change(millisecondsDelay, -1); } catch (ObjectDisposedException) { } } /// Releases all resources used by the current instance of the class. [__DynamicallyInvokable] public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } [__DynamicallyInvokable] protected virtual void Dispose(bool disposing) { if (disposing) { if (this.m_disposed) { return; } if (this.m_timer != null) { this.m_timer.Dispose(); } CancellationTokenRegistration[] linkingRegistrations = this.m_linkingRegistrations; if (linkingRegistrations != null) { this.m_linkingRegistrations = null; for (int i = 0; i < linkingRegistrations.Length; i++) { linkingRegistrations[i].Dispose(); } } this.m_registeredCallbacksLists = null; if (this.m_kernelEvent != null) { this.m_kernelEvent.Close(); this.m_kernelEvent = null; } this.m_disposed = true; } } [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] internal void ThrowIfDisposed() { if (this.m_disposed) { CancellationTokenSource.ThrowObjectDisposedException(); } } private static void ThrowObjectDisposedException() { throw new ObjectDisposedException(null, "CancellationTokenSource_Disposed"); } internal static CancellationTokenSource InternalGetStaticSource(bool set) { if (!set) { return CancellationTokenSource._staticSource_NotCancelable; } return CancellationTokenSource._staticSource_Set; } internal CancellationTokenRegistration InternalRegister(Action callback, object stateForCallback, SynchronizationContext targetSyncContext, ExecutionContext executionContext) { this.ThrowIfDisposed(); if (!this.IsCancellationRequested) { int num = Thread.CurrentThread.ManagedThreadId % CancellationTokenSource.s_nLists; CancellationCallbackInfo cancellationCallbackInfo = new CancellationCallbackInfo(callback, stateForCallback, targetSyncContext, executionContext, this); SparselyPopulatedArray[] array = this.m_registeredCallbacksLists; if (array == null) { SparselyPopulatedArray[] array2 = new SparselyPopulatedArray[CancellationTokenSource.s_nLists]; array = Interlocked.CompareExchange[]>(ref this.m_registeredCallbacksLists, array2, null); if (array == null) { array = array2; } } SparselyPopulatedArray sparselyPopulatedArray = Volatile.Read>(ref array[num]); if (sparselyPopulatedArray == null) { SparselyPopulatedArray value = new SparselyPopulatedArray(4); Interlocked.CompareExchange>(ref array[num], value, null); sparselyPopulatedArray = array[num]; } SparselyPopulatedArrayAddInfo registrationInfo = sparselyPopulatedArray.Add(cancellationCallbackInfo); CancellationTokenRegistration result = new CancellationTokenRegistration(cancellationCallbackInfo, registrationInfo); if (!this.IsCancellationRequested) { return result; } if (!result.TryDeregister()) { return result; } } callback(stateForCallback); return default(CancellationTokenRegistration); } /// Creates a that will be in the canceled state when any of the source tokens are in the canceled state. /// A that is linked to the source tokens. /// The first to observe. /// The second to observe. /// A associated with one of the source tokens has been disposed. /// If any of the tokens cannot be canceled, they will not be linked. The returned source will be cancelable.-or-If any of the tokens are already canceled then the linked token will be returned in canceled state. [__DynamicallyInvokable] public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); bool canBeCanceled = token2.CanBeCanceled; if (token1.CanBeCanceled) { cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[canBeCanceled ? 2 : 1]; cancellationTokenSource.m_linkingRegistrations[0] = token1.InternalRegisterWithoutEC(CancellationTokenSource.s_LinkedTokenCancelDelegate, cancellationTokenSource); } if (canBeCanceled) { int num = 1; if (cancellationTokenSource.m_linkingRegistrations == null) { cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[1]; num = 0; } cancellationTokenSource.m_linkingRegistrations[num] = token2.InternalRegisterWithoutEC(CancellationTokenSource.s_LinkedTokenCancelDelegate, cancellationTokenSource); } return cancellationTokenSource; } /// Creates a that will be in the canceled state when any of the source tokens are in the canceled state. /// A that is linked to the source tokens. /// The instances to observe. /// A associated with one of the source tokens has been disposed. /// /// is null. /// If any of the tokens cannot be canceled, they will not be linked. The returned source will be cancelable.-or-If any of the tokens are already canceled then the linked token will be returned in canceled state. [__DynamicallyInvokable] public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens) { if (tokens == null) { throw new ArgumentNullException("tokens"); } if (tokens.Length == 0) { throw new ArgumentException("CancellationToken_CreateLinkedToken_TokensIsEmpty"); } CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; for (int i = 0; i < tokens.Length; i++) { if (tokens[i].CanBeCanceled) { cancellationTokenSource.m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(CancellationTokenSource.s_LinkedTokenCancelDelegate, cancellationTokenSource); } } return cancellationTokenSource; } internal void WaitForCallbackToComplete(CancellationCallbackInfo callbackInfo) { SpinWait spinWait = default(SpinWait); while (this.ExecutingCallback == callbackInfo) { spinWait.SpinOnce(); } } private static void LinkedTokenCancelDelegate(object source) { CancellationTokenSource cancellationTokenSource = source as CancellationTokenSource; cancellationTokenSource.Cancel(); } private CancellationTokenSource(bool set) { this.m_state = (set ? 3 : 0); } private void InitializeWithTimer(int millisecondsDelay) { this.m_state = 1; this.m_timer = new Timer(CancellationTokenSource.s_timerCallback, this, millisecondsDelay, -1); } private static void TimerCallbackLogic(object obj) { CancellationTokenSource cancellationTokenSource = (CancellationTokenSource)obj; if (!cancellationTokenSource.IsDisposed) { try { cancellationTokenSource.Cancel(); } catch (ObjectDisposedException) { if (!cancellationTokenSource.IsDisposed) { throw; } } } } private void NotifyCancellation(bool throwOnFirstException) { if (this.IsCancellationRequested) { return; } if (Interlocked.CompareExchange(ref this.m_state, 2, 1) == 1) { Timer timer = this.m_timer; if (timer != null) { timer.Dispose(); } this.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; if (this.m_kernelEvent != null) { this.m_kernelEvent.Set(); } this.ExecuteCallbackHandlers(throwOnFirstException); } } private void ExecuteCallbackHandlers(bool throwOnFirstException) { List list = null; SparselyPopulatedArray[] registeredCallbacksLists = this.m_registeredCallbacksLists; if (registeredCallbacksLists == null) { Interlocked.Exchange(ref this.m_state, 3); return; } try { for (int i = 0; i < registeredCallbacksLists.Length; i++) { SparselyPopulatedArray sparselyPopulatedArray = Volatile.Read>(ref registeredCallbacksLists[i]); if (sparselyPopulatedArray != null) { for (SparselyPopulatedArrayFragment sparselyPopulatedArrayFragment = sparselyPopulatedArray.Tail; sparselyPopulatedArrayFragment != null; sparselyPopulatedArrayFragment = sparselyPopulatedArrayFragment.Prev) { for (int j = sparselyPopulatedArrayFragment.Length - 1; j >= 0; j--) { this.m_executingCallback = sparselyPopulatedArrayFragment[j]; if (this.m_executingCallback != null) { CancellationCallbackCoreWorkArguments cancellationCallbackCoreWorkArguments = new CancellationCallbackCoreWorkArguments(sparselyPopulatedArrayFragment, j); try { if (this.m_executingCallback.TargetSyncContext != null) { this.m_executingCallback.TargetSyncContext.Send(new SendOrPostCallback(this.CancellationCallbackCoreWork_OnSyncContext), cancellationCallbackCoreWorkArguments); this.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; } else { this.CancellationCallbackCoreWork(cancellationCallbackCoreWorkArguments); } } catch (Exception item) { if (throwOnFirstException) { throw; } if (list == null) { list = new List(); } list.Add(item); } } } } } } } finally { this.m_state = 3; this.m_executingCallback = null; Thread.MemoryBarrier(); } if (list != null) { throw new AggregateException(list); } } private void CancellationCallbackCoreWork_OnSyncContext(object obj) { this.CancellationCallbackCoreWork((CancellationCallbackCoreWorkArguments)obj); } private void CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args) { CancellationCallbackInfo cancellationCallbackInfo = args.m_currArrayFragment.SafeAtomicRemove(args.m_currArrayIndex, this.m_executingCallback); if (cancellationCallbackInfo == this.m_executingCallback) { if (cancellationCallbackInfo.TargetExecutionContext != null) { cancellationCallbackInfo.CancellationTokenSource.ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; } cancellationCallbackInfo.ExecuteCallback(); } } } } #endif