internal CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext) { CheckDisposed(); var tokenReg = new CancellationTokenRegistration(CustomInterlocked.Increment(ref currId), this); /* If the source is already canceled we execute the callback immediately * if not, we try to add it to the queue and if it is currently being processed * we try to execute it back ourselves to be sure the callback is ran */ if (IsCancellationRequested) { callback(); } else { callbacks.TryAdd(tokenReg, callback); if (IsCancellationRequested && callbacks.TryRemove(tokenReg, out callback)) { callback(); } } return(tokenReg); }
void CommitChangeToHandle(long stamp) { CustomInterlocked.Increment(ref used); var tmpHandle = Handle; if (tmpHandle != null) { // First in all case we carry the operation we were called for if ((stamp & 1) == 1) { tmpHandle.Set(); } else { tmpHandle.Reset(); } /* Then what may happen is that the two suboperations (state change and handle change) * overlapped with others. In our case it doesn't matter if the two suboperations aren't * executed together at the same time, the only thing we have to make sure of is that both * state and handle are synchronized on the last visible state change. * * For instance if S is state change and H is handle change, for 3 concurrent operations * we may have the following serialized timeline: S1 S2 H2 S3 H3 H1 * Which is perfectly fine (all S were converted to H at some stage) but in that case * we have a mismatch between S and H at the end because the last operations done were * S3/H1. We thus need to repeat H3 to get to the desired final state. */ int currentState; do { currentState = state; if (currentState != stamp && (stamp & 1) != (currentState & 1)) { if ((currentState & 1) == 1) { tmpHandle.Set(); } else { tmpHandle.Reset(); } } } while (currentState != state); } CustomInterlocked.Decrement(ref used); }