public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) { CancellationTokenSource state = new CancellationTokenSource(); if (token1.CanBeCanceled) { state.m_linkingRegistrations = new List <CancellationTokenRegistration>(); state.m_linkingRegistrations.Add(token1.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, state)); } if (token2.CanBeCanceled) { if (state.m_linkingRegistrations == null) { state.m_linkingRegistrations = new List <CancellationTokenRegistration>(); } state.m_linkingRegistrations.Add(token2.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, state)); } return(state); }
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, (object)cancellationTokenSource); } if (canBeCanceled) { int index = 1; if (cancellationTokenSource.m_linkingRegistrations == null) { cancellationTokenSource.m_linkingRegistrations = new CancellationTokenRegistration[1]; index = 0; } cancellationTokenSource.m_linkingRegistrations[index] = token2.InternalRegisterWithoutEC(CancellationTokenSource.s_LinkedTokenCancelDelegate, (object)cancellationTokenSource); } return(cancellationTokenSource); }
/// <summary> /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a /// 32-bit signed integer to measure the time interval, while observing a <see /// cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to /// observe.</param> /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise, /// false.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a /// negative number other than -1, which represents an infinite time-out.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The maximum number of waiters has been exceeded. /// </exception> /// <exception cref="T:System.Threading.OperationCanceledException"><paramref /// name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); // an early convenience check if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); } if (!IsSet) { if (millisecondsTimeout == 0) { // For 0-timeouts, we just return immediately. return(false); } // We spin briefly before falling back to allocating and/or waiting on a true event. uint startTime = 0; bool bNeedTimeoutAdjustment = false; int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. if (millisecondsTimeout != Timeout.Infinite) { // We will account for time spent spinning, so that we can decrement it from our // timeout. In most cases the time spent in this section will be negligible. But // we can't discount the possibility of our thread being switched out for a lengthy // period of time. The timeout adjustments only take effect when and if we actually // decide to block in the kernel below. startTime = TimeoutHelper.GetTime(); bNeedTimeoutAdjustment = true; } // Spin int spinCount = SpinCount; var spinner = new SpinWait(); while (spinner.Count < spinCount) { spinner.SpinOnce(SpinWait.Sleep1ThresholdForSpinBeforeWait); if (IsSet) { return(true); } if (spinner.Count >= 100 && spinner.Count % 10 == 0) // check the cancellation token if the user passed a very large spin count { cancellationToken.ThrowIfCancellationRequested(); } } // Now enter the lock and wait. EnsureLockObjectCreated(); // We must register and deregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCallback, this)) { lock (m_lock) { // Loop to cope with spurious wakeups from other waits being canceled while (!IsSet) { // If our token was canceled, we must throw and exit. cancellationToken.ThrowIfCancellationRequested(); //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled) if (bNeedTimeoutAdjustment) { realMillisecondsTimeout = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (realMillisecondsTimeout <= 0) { return(false); } } // There is a race condition that Set will fail to see that there are waiters as Set does not take the lock, // so after updating waiters, we must check IsSet again. // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange // operation which provides a full memory barrier. // If we see IsSet=false, then we are guaranteed that Set() will see that we are // waiting and will pulse the monitor correctly. Waiters = Waiters + 1; if (IsSet) //This check must occur after updating Waiters. { Waiters--; //revert the increment. return(true); } // Now finally perform the wait. try { // ** the actual wait ** if (!Monitor.Wait(m_lock, realMillisecondsTimeout)) { return(false); //return immediately if the timeout has expired. } } finally { // Clean up: we're done waiting. Waiters = Waiters - 1; } // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) } } } } // automatically disposes (and deregisters) the callback return(true); //done. The wait was satisfied. }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!this.IsSet) { if (millisecondsTimeout == 0) { return(false); } uint startTime = 0; bool flag = false; int millisecondsTimeout1 = millisecondsTimeout; if (millisecondsTimeout != -1) { startTime = TimeoutHelper.GetTime(); flag = true; } int num1 = 10; int num2 = 5; int num3 = 20; int spinCount = this.SpinCount; for (int index = 0; index < spinCount; ++index) { if (this.IsSet) { return(true); } if (index < num1) { if (index == num1 / 2) { Thread.Yield(); } else { Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << index)); } } else if (index % num3 == 0) { Thread.Sleep(1); } else if (index % num2 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (index >= 100 && index % 10 == 0) { cancellationToken.ThrowIfCancellationRequested(); } } this.EnsureLockObjectCreated(); using (cancellationToken.InternalRegisterWithoutEC(ManualResetEventSlim.s_cancellationTokenCallback, (object)this)) { lock (this.m_lock) { while (!this.IsSet) { cancellationToken.ThrowIfCancellationRequested(); if (flag) { millisecondsTimeout1 = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (millisecondsTimeout1 <= 0) { return(false); } } this.Waiters = this.Waiters + 1; if (this.IsSet) { this.Waiters = this.Waiters - 1; return(true); } try { if (!Monitor.Wait(this.m_lock, millisecondsTimeout1)) { return(false); } } finally { this.Waiters = this.Waiters - 1; } } } } } return(true); }
/// <summary> /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a /// 32-bit signed integer to measure the time interval, while observing a <see /// cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to /// observe.</param> /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise, /// false.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a /// negative number other than -1, which represents an infinite time-out.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The maximum number of waiters has been exceeded. /// </exception> /// <exception cref="T:System.Threading.OperationCanceledException"><paramref /// name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); // an early convenience check if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!IsSet) { if (millisecondsTimeout == 0) { // For 0-timeouts, we just return immediately. return false; } // We spin briefly before falling back to allocating and/or waiting on a true event. uint startTime = 0; bool bNeedTimeoutAdjustment = false; int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. if (millisecondsTimeout != Timeout.Infinite) { // We will account for time spent spinning, so that we can decrement it from our // timeout. In most cases the time spent in this section will be negligible. But // we can't discount the possibility of our thread being switched out for a lengthy // period of time. The timeout adjustments only take effect when and if we actually // decide to block in the kernel below. startTime = TimeoutHelper.GetTime(); bNeedTimeoutAdjustment = true; } //spin int HOW_MANY_SPIN_BEFORE_YIELD = 10; int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5; int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20; int spinCount = SpinCount; for (int i = 0; i < spinCount; i++) { if (IsSet) { return true; } else if (i < HOW_MANY_SPIN_BEFORE_YIELD) { if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2) { SpinWait.Yield(); } else { SpinWait.Spin(PlatformHelper.ProcessorCount * (4 << i)); } } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0) { Helpers.Sleep(1); } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0) { Helpers.Sleep(0); } else { SpinWait.Yield(); } if (i >= 100 && i % 10 == 0) // check the cancellation token if the user passed a very large spin count cancellationToken.ThrowIfCancellationRequested(); } // Now enter the lock and wait. EnsureLockObjectCreated(); // We must register and deregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCallback, this)) { lock (m_lock) { // Loop to cope with spurious wakeups from other waits being canceled while (!IsSet) { // If our token was canceled, we must throw and exit. cancellationToken.ThrowIfCancellationRequested(); //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled) if (bNeedTimeoutAdjustment) { realMillisecondsTimeout = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (realMillisecondsTimeout <= 0) return false; } // There is a race that Set will fail to see that there are waiters as Set does not take the lock, // so after updating waiters, we must check IsSet again. // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange // operation which provides a full memory barrier. // If we see IsSet=false, then we are guaranteed that Set() will see that we are // waiting and will pulse the monitor correctly. Waiters = Waiters + 1; if (IsSet) //This check must occur after updating Waiters. { Waiters--; //revert the increment. return true; } // Now finally perform the wait. try { // ** the actual wait ** if (!m_condition.Wait(realMillisecondsTimeout)) return false; //return immediately if the timeout has expired. } finally { // Clean up: we're done waiting. Waiters = Waiters - 1; } // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) } } } } // automatically disposes (and deregisters) the callback return true; //done. The wait was satisfied. }
/// <summary> /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, /// using a 32-bit signed integer to measure the time interval, /// while observing a <see cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to /// wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param> /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, /// which represents an infinite time-out.</exception> /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); // Validate input if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( "totalMilliSeconds", millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool waitSuccessful = false; Task <bool> asyncWaitTask = null; bool lockTaken = false; //Register for cancellation outside of the main lock. //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObj and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive, but only up to the first planned yield. // This additional amount of spinwaiting in addition // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. // SpinWait spin = new SpinWait(); while (m_currentCount == 0 && !spin.NextSpinWillYield) { spin.SpinOnce(); } // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters. try { } finally { m_lock.Acquire(); lockTaken = true; if (lockTaken) { m_waitCount++; } } // If there are any async waiters, for fairness we'll get in line behind // then by translating our synchronous wait into an asynchronous one that we // then block on (once we've released the lock). if (m_asyncHead != null) { Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration OperationCanceledException oce = null; if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired try { waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException e) { oce = e; } } // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { waitSuccessful = true; m_currentCount--; } else if (oce != null) { throw oce; } // Exposing wait handle which is lazily initialized if needed if (m_waitHandle != null && m_currentCount == 0) { m_waitHandle.Reset(); } } } finally { // Release the lock if (lockTaken) { m_waitCount--; m_lock.Release(); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } // If we had to fall back to asynchronous waiting, block on it // here now that we've released the lock, and return its // result when available. Otherwise, this was a synchronous // wait, and whether we successfully acquired the semaphore is // stored in waitSuccessful. return((asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful); }
public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) { CancellationTokenSource state = new CancellationTokenSource(); if (token1.CanBeCanceled) { state.m_linkingRegistrations = new List<CancellationTokenRegistration>(); state.m_linkingRegistrations.Add(token1.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, state)); } if (token2.CanBeCanceled) { if (state.m_linkingRegistrations == null) { state.m_linkingRegistrations = new List<CancellationTokenRegistration>(); } state.m_linkingRegistrations.Add(token2.InternalRegisterWithoutEC(s_LinkedTokenCancelDelegate, state)); } return state; }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.CheckDispose(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("totalMilliSeconds", millisecondsTimeout, SemaphoreSlim.GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } cancellationToken.ThrowIfCancellationRequested(); uint startTime = 0U; if (millisecondsTimeout != -1 && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool result = false; Task <bool> task = null; bool flag = false; CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(SemaphoreSlim.s_cancellationTokenCanceledEventHandler, this); try { SpinWait spinWait = default(SpinWait); while (this.m_currentCount == 0 && !spinWait.NextSpinWillYield) { spinWait.SpinOnce(); } try { } finally { Monitor.Enter(this.m_lockObj, ref flag); if (flag) { this.m_waitCount++; } } if (this.m_asyncHead != null) { task = this.WaitAsync(millisecondsTimeout, cancellationToken); } else { OperationCanceledException ex = null; if (this.m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } try { result = this.WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException ex2) { ex = ex2; } } if (this.m_currentCount > 0) { result = true; this.m_currentCount--; } else if (ex != null) { throw ex; } if (this.m_waitHandle != null && this.m_currentCount == 0) { this.m_waitHandle.Reset(); } } } finally { if (flag) { this.m_waitCount--; Monitor.Exit(this.m_lockObj); } cancellationTokenRegistration.Dispose(); } if (task == null) { return(result); } return(task.GetAwaiter().GetResult()); }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!this.IsSet) { if (millisecondsTimeout == 0) { return(false); } uint startTime = 0U; bool flag = false; int num = millisecondsTimeout; if (millisecondsTimeout != -1) { startTime = TimeoutHelper.GetTime(); flag = true; } int num2 = 10; int num3 = 5; int num4 = 20; int spinCount = this.SpinCount; for (int i = 0; i < spinCount; i++) { if (this.IsSet) { return(true); } if (i < num2) { if (i == num2 / 2) { Thread.Yield(); } else { Thread.SpinWait(4 << i); } } else if (i % num4 == 0) { Thread.Sleep(1); } else if (i % num3 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (i >= 100 && i % 10 == 0) { cancellationToken.ThrowIfCancellationRequested(); } } this.EnsureLockObjectCreated(); using (cancellationToken.InternalRegisterWithoutEC(ManualResetEventSlim.s_cancellationTokenCallback, this)) { object @lock = this.m_lock; lock (@lock) { while (!this.IsSet) { cancellationToken.ThrowIfCancellationRequested(); if (flag) { num = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (num <= 0) { return(false); } } this.Waiters++; if (this.IsSet) { int waiters = this.Waiters; this.Waiters = waiters - 1; return(true); } try { if (!Monitor.Wait(this.m_lock, num)) { return(false); } } finally { this.Waiters--; } } } } return(true); } return(true); }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!this.IsSet) { if (millisecondsTimeout == 0) { return false; } uint startTime = 0u; bool flag = false; int num = millisecondsTimeout; if (millisecondsTimeout != -1) { startTime = TimeoutHelper.GetTime(); flag = true; } int num2 = 10; int num3 = 5; int num4 = 20; int spinCount = this.SpinCount; for (int i = 0; i < spinCount; i++) { if (this.IsSet) { return true; } if (i < num2) { if (i == num2 / 2) { Thread.Sleep(0); } else { Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i)); } } else { if (i % num4 == 0) { Thread.Sleep(1); } else { Thread.Sleep(0); //if (i % num3 == 0) //{ // Thread.Sleep(0); //} //else //{ // Thread.Sleep(0); //} } } if (i >= 100 && i % 10 == 0) { cancellationToken.ThrowIfCancellationRequested(); } } this.EnsureLockObjectCreated(); using (cancellationToken.InternalRegisterWithoutEC(ManualResetEventSlim.s_cancellationTokenCallback, this)) { lock (this.m_lock) { while (!this.IsSet) { cancellationToken.ThrowIfCancellationRequested(); if (flag) { num = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (num <= 0) { bool result = false; return result; } } this.Waiters++; if (this.IsSet) { this.Waiters--; bool result = true; return result; } try { if (!Monitor.Wait(this.m_lock, num)) { bool result = false; return result; } } finally { this.Waiters--; } } } } return true; } return true; }
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; }
/// <summary> /// Creates the linked token source. /// </summary> /// <param name="token1">The token1.</param> /// <param name="token2">The token2.</param> /// <returns></returns> public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) { var state = new CancellationTokenSource(); var canBeCanceled = token2.CanBeCanceled; if (token1.CanBeCanceled) { state._linkingRegistrations = new CancellationTokenRegistration[canBeCanceled ? 2 : 1]; state._linkingRegistrations[0] = token1.InternalRegisterWithoutEC(_LinkedTokenCancelDelegate, state); } if (canBeCanceled) { var index = 1; if (state._linkingRegistrations == null) { state._linkingRegistrations = new CancellationTokenRegistration[1]; index = 0; } state._linkingRegistrations[index] = token2.InternalRegisterWithoutEC(_LinkedTokenCancelDelegate, state); } return state; }