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; }