public async Task <bool> RunImpl(Func <Task> factory, Func <SerialExecutionBucket, Task> releaseBucketTask, CancellationToken cancellationToken, bool isAsync)
        {
            if (_released)
            {
                return(false);
            }

            Interlocked.Increment(ref this._waitingCount);
            try
            {
                using (isAsync ? await _bucketLockAsync.LockAsync(cancellationToken)
                               : _bucketLockAsync.Lock(cancellationToken))
                {
                    if (_released)
                    {
                        return(false);
                    }

                    if (isAsync)
                    {
                        await factory();
                    }
                    else
                    {
                        SyncTaskHelper.ValidateSyncTask(factory());
                    }

                    return(true);
                }
            }
            finally
            {
                Interlocked.Decrement(ref this._waitingCount);

                if (!_released && _waitingCount == 0)
                {
                    //no cancellation token used when releasing.
                    using (isAsync ? await _bucketLockAsync.LockAsync()
                                   : _bucketLockAsync.Lock())
                    {
                        if (!_released && _waitingCount == 0)
                        {
                            if (isAsync)
                            {
                                await releaseBucketTask(this);
                            }
                            else
                            {
                                SyncTaskHelper.ValidateSyncTask(releaseBucketTask(this));
                            }

                            _released = true;
                        }
                    }
                }
            }
        }
        private async Task RunImpl(Guid resource, Func <Task> factory, CancellationToken cancellationToken, bool isAsync)
        {
            SerialExecutionBucket bucket = null;
            bool run = false;

            while (!run)
            {
                using (isAsync ? await _lockAsync.LockAsync(cancellationToken)
                               : _lockAsync.Lock(cancellationToken))
                {
                    bucket = _buckets.ContainsKey(resource) ? _buckets[resource]
                                        : (_buckets[resource] = SerialExecutionBucket.Create(resource));
                }

                run = isAsync ? await bucket.RunImpl(factory, ReleaseBucketTask, cancellationToken, isAsync : isAsync)
                               : SyncTaskHelper.ValidateSyncTask(bucket.RunImpl(factory, ReleaseBucketSync, cancellationToken, isAsync: isAsync));
            }
        }
 /// <summary>
 /// Provides execution of actions in a serial manner (one at a time) for each resource. Actions for different resources will run in parallel.
 /// The caller gets blocked till the action executes.
 /// </summary>
 /// <param name="resource">The resource id.</param>
 /// <param name="action">An action factory.</param>
 /// <param name="cancellationToken">(optional) Allows cancellation when waiting to run. Not after the action started.</param>
 public void Run(Guid resource, Action action, CancellationToken cancellationToken = default(CancellationToken))
 {
     SyncTaskHelper.ValidateSyncTask(RunImpl(resource, WrapAction(action), cancellationToken, isAsync: false));
 }