コード例 #1
0
        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");
            }
        }
コード例 #2
0
        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();
                }
            }
        }
コード例 #3
0
        /// <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;
        }
コード例 #4
0
        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();
                }
            }
        }
コード例 #5
0
        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();
        }
コード例 #6
0
            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);
            }
コード例 #7
0
            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);
            }
コード例 #8
0
        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();
                }
            }
        }