private void Release(int token, bool demandMatch = true) { void ThrowInvalidLockHolder() => throw new InvalidOperationException("Attempt to release a MutexSlim lock token that was not held"); // release the token (we can check for wrongness without needing the lock, note) Log($"attempting to release token {LockState.ToString(token)}"); if (Interlocked.CompareExchange(ref _token, LockState.ChangeState(token, LockState.Pending), token) != token) { Log($"token {LockState.ToString(token)} is invalid"); if (demandMatch) { ThrowInvalidLockHolder(); } return; } if (_mayHavePendingItems) { ActivateNextQueueItem(); } else { Log($"no pending items to activate"); } }
private void ActivateNextQueueItemWithValidatedToken(int token) { // see if we can nudge the next waiter lock (_queue) { if (Volatile.Read(ref _token) != token) { Throw.InvalidLockHolder(); } try { if (_queue.Count == 0) { Log($"no pending items to activate"); return; } // there's work to do; get a new token Log($"pending items: {_queue.Count}"); // we're expecting to activate; get a new token speculatively // (needs to be new so the old caller can't double-dispose and // release someone else's lock) Volatile.Write(ref _token, token = LockState.GetNextToken(token)); // try to give the new token to someone while (_queue.Count != 0) { var next = DequeueInsideLock(); if (next.TrySetResult(token)) { Log($"handed lock to {next}"); token = 0; // so we don't release the token return; } else { Log($"lock rejected by {next}"); } } } finally { if (token != 0) // nobody actually wanted it; return it { // (this could be the original token, or a new speculative token) Log("returning unwanted lock"); Volatile.Write(ref _token, LockState.ChangeState(token, LockState.Pending)); } SetNextAsyncTimeoutInsideLock(); } } }
/// <summary> /// Create a new MutexSlim instance /// </summary> /// <param name = "timeoutMilliseconds">Time to wait, in milliseconds - or zero for immediate-only</param> /// <param name="scheduler">The scheduler to use for async continuations, or the thread-pool if omitted</param> public MutexSlim(int timeoutMilliseconds, PipeScheduler scheduler = null) { if (timeoutMilliseconds < 0) { ThrowInvalidTimeout(); } TimeoutMilliseconds = timeoutMilliseconds; _scheduler = scheduler ?? PipeScheduler.ThreadPool; _token = LockState.ChangeState(0, LockState.Pending); // initialize as unowned void ThrowInvalidTimeout() => Throw.ArgumentOutOfRange(nameof(timeoutMilliseconds)); IsThreadPool = (object)_scheduler == (object)PipeScheduler.ThreadPool; }
private void ActivateNextQueueItem() { // see if we can nudge the next waiter lock (_queue) { if (_queue.Count == 0) { Log($"no pending items to activate"); _uncontested = true; return; } try { // there's work to do; try and get a new token Log($"pending items: {_queue.Count}"); int token = TryTakeLoopIfChanges(); if (token == 0) { // despite it being contested, somebody else // won the lock while we were busy thinking; // this is staggeringly rare, and means the // mutex isn't *absolteuly* fair, but it is // "fair enough" to be useful and practical return; } while (_queue.Count != 0) { var next = DequeueInsideLock(); if (next.TrySetResult(token)) { Log($"handed lock to {next}"); return; // so we don't release the token } else { Log($"lock rejected by {next}"); } } // nobody actually wanted it; return it Log("returning unwanted lock"); Volatile.Write(ref _token, LockState.ChangeState(token, LockState.Pending)); } finally { _uncontested = _queue.Count == 0; SetNextAsyncTimeoutInsideLock(); } } }
private void Release(int token, bool demandMatch = true) { // release the token (we can check for wrongness without needing the lock, note) Log($"attempting to release token {LockState.ToString(token)}"); if (Interlocked.CompareExchange(ref _token, LockState.ChangeState(token, LockState.Pending), token) != token) { Log($"token {LockState.ToString(token)} is invalid"); if (demandMatch) { Throw.InvalidLockHolder(); } return; } ActivateNextQueueItem(); }
public static bool TryCancel(ref int token) { int oldValue; do { // depends on the current state... oldValue = Volatile.Read(ref token); if (LockState.GetState(oldValue) != LockState.Pending) { // already fixed return(false); } // otherwise, attempt to change the field; in case of conflict; re-do from start } while (Interlocked.CompareExchange(ref token, LockState.ChangeState(oldValue, LockState.Canceled), oldValue) != oldValue); return(true); }
public static int GetResult(ref int token) { // if already complete: returns the token; otherwise, dooms the operation int oldValue, newValue; do { oldValue = Volatile.Read(ref token); if (LockState.GetState(oldValue) != LockState.Pending) { // value is already fixed; just return it return(oldValue); } // we don't ever want to report different values from GetResult, so // if you called GetResult prematurely: you doomed it to failure newValue = LockState.ChangeState(oldValue, LockState.Timeout); // if something changed while we were thinking, redo from start } while (Interlocked.CompareExchange(ref token, newValue, oldValue) != oldValue); return(newValue); }
private void ActivateNextQueueItem() { // see if we can nudge the next waiter lock (_queue) { try { int token; // if work to do, try and get a new token Log($"pending items: {_queue.Count}"); if (_queue.Count == 0 || (token = TryTakeLoopIfChanges()) == 0) { return; } while (_queue.Count != 0) { var next = DequeueInsideLock(); if (next.TrySetResult(token)) { Log($"handed lock to {next}"); return; // so we don't release the token } else { Log($"lock rejected by {next}"); } } // nobody actually wanted it; return it Log("returning unwanted lock"); Volatile.Write(ref _token, LockState.ChangeState(token, LockState.Pending)); } finally { FixMayHavePendingItemsInsideLock(); SetNextAsyncTimeoutInsideLock(); } } }