示例#1
0
        public async Task <T> LockAsync <T>(Key key, Func <CancellationToken, Task <T> > callback, CancellationToken cancellationToken)
        {
            if (callback == null)
            {
                throw new ArgumentNullException(nameof(callback));
            }

            using var state = new LockState(key, _identifierGenerator.GetUniqueKey(15), cancellationToken);
            try
            {
                await _scriptLibrary.SubscribeAsync(state).ConfigureAwait(false);

                try
                {
                    if (await _scriptLibrary.GetLockOrAddToQueue(state.Parameters).ConfigureAwait(false))
                    {
                        state.SetWithKey();
                    }

                    //we start protecting after the first manipulation in redis, to properly hit the expiration if needed
                    using (var protector = new LockProtector(_scriptLibrary, state))
                    {
                        await state.WaitingTask.ConfigureAwait(false);

                        if (state.Token.IsCancellationRequested)
                        {
                            throw new TaskCanceledException();
                        }
                        return(await callback(state.Token).ConfigureAwait(false));
                    }
                }
                finally
                {
                    await _scriptLibrary.FreeLockAndPop(state.Parameters).ConfigureAwait(false);
                }
            }
            finally
            {
                await _scriptLibrary.UnSubscribeAsync(state).ConfigureAwait(false);
            }
        }
示例#2
0
        //for testing purposes
        internal async Task EnsureNoDeadLockAsync()
        {
            if (_state.State == State.Done)
            {
                return;
            }

            //this will be repeated until disposed
            while (true)
            {
                try
                {
                    //we wait for the time it takes the key to expire
                    await Task.Delay(_state.Key.RedisKeyExpiration, _state.Token).ConfigureAwait(false);
                }
                catch (TaskCanceledException)
                {
                    //if the token gets canceled everything went well
                    return;
                }

                if (_state.State == State.Done)
                {
                    return;
                }

                try
                {
                    //let's see what's in redis at this point
                    var result = await _scriptLibrary.GetKeySituation(_state.Parameters).ConfigureAwait(false);

                    switch (result)
                    {
                    //lock in waiting list
                    case 0:
                        //we keep waiting
                        break;

                    //we owns the lock
                    case 1:
                        //this means the task has been completed while we were looking
                        //we set the result and keep protecting the callback
                        _state.SetWithKey();
                        break;

                    //lock is not found
                    case 2:
                        switch (_state.State)
                        {
                        case State.WaitingForKey:
                            //keys have expired, probably due to remote failure, we need to restart the lock process
                            if (await _scriptLibrary.GetLockOrAddToQueue(_state.Parameters).ConfigureAwait(false))
                            {
                                _state.SetWithKey();
                            }
                            break;

                        case State.WithKey:
                            //we thought we had the key but didn't
                            //we stop everything
                            _state.SetDone();
                            return;        //no need to wait for delay cancellation

                        case State.Done: return;
                        }
                        break;

                    default:
                        _state.SetDone(new NotImplementedException("Unexpected Redis state"));
                        return;
                    }
                }
                catch (Exception e)
                {
                    //Redis failed, we need to stop waiting for the notification
                    _state.SetDone(e);
                    return;
                }
            }
        }