private void doRunFinalization(
      final AtomicReference<TransactionResult<T>> transactionId,
      final boolean innerCall,
      final TransactionResult.Reason reason,
      final T transactionResult,
      final ContextCallback<T> callback) {

    repetitiousTaskManager.cancelTask(transactionId);

    synchronized (mutex) {

      // this code may be called from three points:
      // 1. On latch TIMEOUT. In that case first coming thread sets the default return value, runs
      // finalization routine and awaits it finishes.
      // 2. On shutdown call. In that case calling thread set the default return value, runs
      // finalization routine and returns.
      // 3. On release call. In that case calling thread set the return value equals to the value
      // passed by calling thread, runs finalization routine and returns.

      final boolean trnsIdEmpty =
          transactionId.compareAndSet(
              null,
              transactionResult == null
                  ? new TransactionResultImpl<T>(initialValue, reason)
                  : new TransactionResultImpl<T>(transactionResult, reason));

      if (trnsIdEmpty) {

        // flag whether we go through 1. scenario

        // create new finalization routine
        final FutureTask<Object> futureTask =
            new FutureTask<Object>(
                new Runnable() {
                  public void run() {
                    // run outer finalization routine
                    // Thread.currentThread().setName("Transaction finalizator");
                    try {
                      callback.finalizeTransaction(transactionId.get());
                    } catch (Exception e) {
                      e.printStackTrace();
                    } finally {
                      // let this iteration awaiting threads to be free and continue execution
                      cleanUpFinalization(transactionId);
                    }
                  }
                },
                null);

        // hold and execute new finalization routine
        finalizationMap.put(transactionId, futureTask);
        // TransactionUtils.getExecutorService().execute(futureTask);
        TransactionUtils.invokeLaterSmart(
            new TransactionRunnable("SynchronizationContext.doRunFinalization()") {
              public void run() {
                if (!futureTask.isCancelled() && !futureTask.isDone()) {
                  futureTask.run();
                  futureTask.cancel(true);
                }
              }
            });

        // set free waiting thread and initiate new iteration
        doReleaseThreads();
      }
    }

    // here one more synchronization point for awaiting threads from this iteration
    // this time threads waiting till the end of finalization routine.
    // All outer threads (shutdown(), release()) do not block here
    if (innerCall) {
      boolean needCleanupFinalization = false;

      // for all separate transaction we have separate transactionId.
      //noinspection SynchronizationOnLocalVariableOrMethodParameter
      synchronized (transactionId) {
        long startWait = System.currentTimeMillis();

        // waiting here until finalization task is done. Finalization task clears the map by itself
        // threads do not wait more then FINALIZATION_TIMEOUT*2
        while (finalizationMap.containsKey(transactionId)) {
          try {
            assert !TransactionUtils.isTransactionExecutionThread()
                : "Task queue blocking task detected";
            transactionId.wait(FINALIZATION_TIMEOUT);
          } catch (InterruptedException e) {
            Thread.interrupted();
          }

          if ((System.currentTimeMillis() - startWait) >= FINALIZATION_TIMEOUT) {
            needCleanupFinalization = true;
            break;
          }
        }
      }

      // if something happens to finalization routine (helper theread abruptly dead or something
      // like that) we make map clean up.
      // and try to notify all awaitng threads
      if (needCleanupFinalization) {
        Logger.log(
            "Warning!!! transaction finalization took  too long time. More than "
                + FINALIZATION_TIMEOUT
                + " millis");
        cleanUpFinalization(transactionId);
      }
    }
  }
  private TransactionResult<T> doAppend(final TimeoutUnit timeoutUnit, final boolean block) {
    Logger.log(
        "ReentrantSynchronizationContext",
        "doAppend#timeoutUnit = "
            + timeoutUnit
            + ", block = "
            + block
            + ", done.get() = "
            + done.get());
    TransactionResult<T> retValue = null;

    // possible to proceed only if there were no shutdown call on this instance
    if (!done.get()) {

      // latch for calling thread
      CountDownLatch localBarrier;

      // if the calling thread is a first on in this iteration the flag will get 'true' value
      boolean needInitiateTransaction;

      // each iteration has it's own instance of threadLocalTransactionId. Also it holds the current
      // iteration transaction value.
      // null means default value should be used
      final AtomicReference<TransactionResult<T>> threadLocalTransactionId;

      // nobody can go through if the same monitor is taken in other place. Probably in finalization
      // routine
      Logger.log("ReentrantSynchronizationContext", "doAppend#before sync");
      synchronized (mutex) {
        Logger.log("ReentrantSynchronizationContext", "doAppend#after sync");
        // each calling thread would have it's own latch.
        // It's supposed that some other awaking activity would countdown all the latches from list.
        localBarrier = new CountDownLatch(1);

        // list of all latches and associated threads. Used to release all threads at once for this
        // transaction iteration
        awaitingThreads.add(localBarrier);
        // barierList.add(localBarrier);

        // see description above
        needInitiateTransaction = false;
        if (transactionInProgress.compareAndSet(false, true)) {
          // if we first time in this iteration in this peace of code we mark that new transaction
          // shoud be started
          needInitiateTransaction = true;
          // also we prepare new transaction id. null means we don't have outer call to release()
          transactionId.set(new AtomicReference<TransactionResult<T>>(null));
        }
        // each thread must hold transaction id it it's own heap
        threadLocalTransactionId = transactionId.get();
      }

      // call outer initialization routine
      if (needInitiateTransaction) {
        Logger.log("ReentrantSynchronizationContext", "initiateTransaction#start");
        callback.initiateTransaction();
        Logger.log("ReentrantSynchronizationContext", "initiateTransaction#end");
      }

      if (block) {
        TransactionResult.Reason reason = TransactionResult.Reason.OUTER_INTERRUPT;
        try {
          // make thread to wait until transaction finished or TIMEOUT EXPIRES
          if (timeoutUnit != null) {
            Logger.log(
                "ReentrantSynchronizationContext", "start await, timeoutUnit = " + timeoutUnit);
            localBarrier.await(
                timeoutUnit.getTimeout(),
                timeoutUnit.getTimeoutUnit() == null
                    ? TimeUnit.MILLISECONDS
                    : timeoutUnit.getTimeoutUnit());
          } else {
            localBarrier.await();
          }

          reason =
              localBarrier.getCount() > 0
                  ? TransactionResult.Reason.TIMEOUT
                  : TransactionResult.Reason.OUTER_INTERRUPT;

          Logger.log("ReentrantSynchronizationContext", "end await, reason = " + reason);
        } catch (InterruptedException e) {
          // set thread flag
          Thread.interrupted();
          reason = TransactionResult.Reason.OUTER_INTERRUPT;
        } finally {
          // try to run finalization routine
          final TransactionResult<T> transactionResult = threadLocalTransactionId.get();
          Logger.log("ReentrantSynchronizationContext", "start tr. finalization");
          runFinalization(
              threadLocalTransactionId,
              true,
              reason,
              transactionResult == null ? null : transactionResult.getValue(),
              callback);
          Logger.log("ReentrantSynchronizationContext", "end tr. finalization");
          retValue = threadLocalTransactionId.get();
          Logger.log(
              "ReentrantSynchronizationContext", "start tr. finalization, retValue = " + retValue);
        }
      } else {
        if (needInitiateTransaction) {
          boolean needTimeout =
              !(timeoutUnit == null
                  || timeoutUnit.getTimeout() == null
                  || timeoutUnit.getTimeoutUnit() == null);

          if (needTimeout) {
            long timeoutInMillis = timeoutUnit.getTimeoutUnit().toMillis(timeoutUnit.getTimeout());

            repetitiousTaskManager.startDelayedTask(
                threadLocalTransactionId,
                new RepetitiousTaskManager.Repeater<AtomicReference<TransactionResult<T>>>() {
                  @Override
                  public void onRepeat(
                      AtomicReference<TransactionResult<T>> key, Shutdownable shutdownable) {
                    Logger.log(TAG, String.format("onRepeat"));
                    final TransactionResult<T> transactionResult = key.get();
                    runFinalization(
                        key,
                        true,
                        TransactionResult.Reason.TIMEOUT,
                        transactionResult == null ? null : transactionResult.getValue(),
                        callback);
                    shutdownable.shutdown();
                  }
                },
                timeoutInMillis);
          }
        }
      }
    } else {
      throw new IllegalStateException("Context already shutdown.");
    }

    return retValue;
  }