public Task Acquire(
     string incomingCallContext,
     ActorDirtyStateHandler handler,
     CancellationToken cancellationToken)
 {
     return(this.Acquire(
                incomingCallContext,
                handler,
                this.reentrancyMode,
                cancellationToken));
 }
        public async Task Acquire(
            string incomingCallContext,
            ActorDirtyStateHandler handler,
            ActorReentrancyMode actorReentrancyMode,
            CancellationToken cancellationToken)
        {
            // acquire the reentrancy lock
            await this.reentrantLock.WaitAsync(cancellationToken);

            try
            {
                // A new logical call context is appended to every outgoing method call.
                // The received callContext is of form callContext1callContext2callContext3...
                // thus to check if incoming call was made from the current calls in progress
                // we need to check if the incoming call context starts with the currentCallContext
                var startsWith = incomingCallContext.StartsWith(this.currentCallContext);

                if (startsWith)
                {
                    // the incoming call is part of the current call chain

                    // the messaging layer may deliver duplicate messages, therefore if the
                    // incomingCallContext is same as currentCallContext it is a duplicate message
                    // this is because every outgoing call from actors has a new callContext appended
                    // to the currentCallContext
                    if (incomingCallContext.Length == this.currentCallContext.Length)
                    {
                        throw new DuplicateMessageException(string.Format(CultureInfo.CurrentCulture,
                                                                          SR.ErrorDuplicateMessage, this.GetType()));
                    }

                    //
                    // this is a reentrant call
                    //

                    // if the reentrancy is disallowed, throw and exception
                    if (actorReentrancyMode == ActorReentrancyMode.Disallowed)
                    {
                        throw new ReentrancyModeDisallowedException(String.Format(SR.ReentrancyModeDisallowed, this.GetType()));
                    }

                    // if the actor is dirty, do not allow reentrant call to go through
                    // since its not expected that actor state be dirty in a reentrant call
                    // throw exception that flows back to caller.
                    if (this.owner.IsDirty)
                    {
                        throw new ReentrantActorInvalidStateException(
                                  string.Format(
                                      CultureInfo.CurrentCulture,
                                      SR.ReentrantActorDirtyState,
                                      this.owner.Id));
                    }

                    // the currentCallCount must not be zero here as the startsWith comparison can only
                    // be true if the incoming call is part of the current call chain
                    //
                    // we only allow one cycle in the reentrant call chain, so if this is a second reentrant call
                    // then reject it. this also ensures that if multiple calls are made from the actor in parallel
                    // and they reenter the actor only one is allowed
                    //
                    if (this.currentCallCount == 1)
                    {
                        this.currentCallCount++;
                        return;
                    }
                    else
                    {
                        throw new InvalidReentrantCallException(SR.InvalidReentrantCall);
                    }
                }
            }
            finally
            {
                this.reentrantLock.Release();
            }


            //
            // this is not a reentrant call, which means that the caller needs to wait
            // for its turn to execute this call
            //
            var timeout = this.GetTurnLockWaitTimeout();

            if (!await this.turnLock.WaitAsync(timeout, cancellationToken))
            {
                throw new ActorConcurrencyLockTimeoutException(
                          string.Format(
                              CultureInfo.CurrentCulture,
                              SR.ConcurrencyLockTimedOut,
                              this.owner.Id,
                              timeout));
            }

            // the caller has the turn lock
            try
            {
                // check  if the owner is dirty
                if (this.owner.IsDirty && handler != null)
                {
                    // call dirty state handler to handle it
                    await handler(this.owner);
                }

                // get the reentrancy lock and initialize it with the current call information
                // so that if this call were to generate reentrant calls they are allowed
                await this.reentrantLock.WaitAsync(cancellationToken);

                try
                {
                    this.currentCallContext = incomingCallContext;
                    this.currentCallCount   = 1;
                }
                finally
                {
                    this.reentrantLock.Release();
                }
            }
            catch
            {
                // dirty handler threw and exception, release the turn lock
                // and throw the exception back
                this.turnLock.Release();
                throw;
            }

            // release the reentrancy lock but continue to hold the turn lock and release
            // it through ReleaseContext method after this call invocation on the actor is completed

            // indicate that the turn based concurrency lock is acquired and proceed to
            // call the method on the actor
        }