Search in sources :

Example 1 with ReaperWorkerThread

use of com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread in project narayana by jbosstm.

the class TransactionReaper method doCancellations.

public final void doCancellations() {
    for (; ; ) {
        ReaperElement e;
        synchronized (_workQueue) {
            try {
                e = _workQueue.remove(0);
            } catch (IndexOutOfBoundsException ioobe) {
                break;
            }
        }
        if (tsLogger.logger.isTraceEnabled()) {
            tsLogger.logger.trace("Reaper Worker " + Thread.currentThread() + " attempting to cancel " + e._control.get_uid());
        }
        boolean cancelled = false;
        Exception exception = null;
        synchronized (e) {
            e._worker = Thread.currentThread();
            e._status = ReaperElement.CANCEL;
            e.notifyAll();
        }
        try {
            if (e._control.running()) {
                if (e._control.cancel() == ActionStatus.ABORTED) {
                    cancelled = true;
                    if (TxStats.enabled()) {
                        // note that we also count timeouts as application rollbacks via
                        // the stats unpdate in the TwoPhaseCoordinator cancel() method.
                        TxStats.getInstance().incrementTimeouts();
                    }
                    notifyListeners(e._control, true);
                }
            }
        } catch (Exception e1) {
            exception = e1;
        }
        synchronized (e) {
            if (e._status == ReaperElement.ZOMBIE) {
                // we need to decrement the zombie count and
                // force an immediate thread exit. the reaper
                // will have removed the entry from the
                // transactions list and started another
                // worker thread.
                ReaperWorkerThread worker = (ReaperWorkerThread) Thread.currentThread();
                worker.shutdown();
                synchronized (this) {
                    _zombieCount--;
                }
                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_13(Thread.currentThread().toString(), e._control.get_uid(), Integer.toString(_zombieCount));
                break;
            } else if (cancelled && e._status == ReaperElement.CANCEL_INTERRUPTED) {
                // ok the call to cancel() returned true but
                // we cannot trust it because the reaper sent
                // the thread an interrupt
                cancelled = false;
                e._status = ReaperElement.FAIL;
                e.notifyAll();
            } else {
                e._status = (cancelled ? ReaperElement.COMPLETE : ReaperElement.FAIL);
                e.notifyAll();
            }
        }
        if (cancelled) {
            tsLogger.i18NLogger.warn_coordinator_TransactionReaper_7(Thread.currentThread().toString(), e._control.get_uid());
        } else if (e._control.running()) {
            if (exception != null) {
                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_9(Thread.currentThread().toString(), e._control.get_uid(), exception);
            } else {
                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_8(Thread.currentThread().toString(), e._control.get_uid());
            }
            try {
                if (e._control.preventCommit()) {
                    // log a successful preventCommit()
                    tsLogger.i18NLogger.warn_coordinator_TransactionReaper_14(Thread.currentThread().toString(), e._control.get_uid());
                    notifyListeners(e._control, false);
                } else {
                    // log a failed preventCommit()
                    tsLogger.i18NLogger.warn_coordinator_TransactionReaper_15(Thread.currentThread().toString(), e._control.get_uid());
                }
            } catch (Exception e1) {
                // log an exception under preventCommit()
                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_16(Thread.currentThread().toString(), e._control.get_uid(), e1);
            }
        }
        removeElementReaper(e);
    }
}
Also used : ReaperWorkerThread(com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread) ReaperElement(com.arjuna.ats.internal.arjuna.coordinator.ReaperElement)

Example 2 with ReaperWorkerThread

use of com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread in project narayana by jbosstm.

the class TransactionReaper method check.

/**
 * process all entries in the timeout queue which have
 * expired. entries for newly expired transactions are passed
 * to a worker thread for cancellation and requeued for
 * subsequent progress checks. the worker is given a kick if
 * such checks find it is wedged.
 *
 * Timeout is given in milliseconds.
 */
public final void check() {
    if (tsLogger.logger.isTraceEnabled()) {
        tsLogger.logger.trace("TransactionReaper::check ()");
    }
    do {
        final ReaperElement reaperElement;
        synchronized (this) {
            final long now = System.currentTimeMillis();
            final long next = nextDynamicCheckTime.get();
            if (tsLogger.logger.isTraceEnabled()) {
                tsLogger.logger.trace("TransactionReaper::check - comparing " + Long.toString(next));
            }
            if (now < next) {
                break;
            }
            reaperElement = _reaperElements.getFirst();
            // then use compareAndSet? Although something will need to check before sleeping anyhow...
            if (reaperElement == null) {
                nextDynamicCheckTime.set(Long.MAX_VALUE);
                return;
            } else {
                final long nextTimeout = reaperElement.getAbsoluteTimeout();
                if (nextTimeout > now) {
                    nextDynamicCheckTime.set(nextTimeout);
                    // nothing to do yet.
                    return;
                }
            }
        }
        tsLogger.i18NLogger.warn_coordinator_TransactionReaper_18(reaperElement._control.get_uid(), reaperElement.statusName());
        synchronized (reaperElement) {
            switch(reaperElement._status) {
                case ReaperElement.RUN:
                    {
                        // this tx has just timed out. remove it from the
                        // TX list, update the timeout to take account of
                        // cancellation period and reinsert as a cancelled
                        // TX. this ensures we process it again if it does
                        // not get cancelled in time
                        reaperElement._status = ReaperElement.SCHEDULE_CANCEL;
                        reinsertElement(reaperElement, _cancelWaitPeriod);
                        if (tsLogger.logger.isTraceEnabled()) {
                            tsLogger.logger.trace("Reaper scheduling TX for cancellation " + reaperElement._control.get_uid());
                        }
                        synchronized (_workQueue) {
                            _workQueue.add(reaperElement);
                            _workQueue.notifyAll();
                        }
                    }
                    break;
                case ReaperElement.SCHEDULE_CANCEL:
                    {
                        // hmm, a worker is taking its time to
                        // start processing this scheduled entry.
                        // we may just be running slow ... but the
                        // worker may be wedged under a cancel for
                        // some other TX. add an extra delay to
                        // give the worker more time to complete
                        // its current task and progress this
                        // entry to the CANCEL state. if the
                        // worker *is* wedged then this will
                        // ensure the wedged TX entry comes to the
                        // front of the queue.
                        reinsertElement(reaperElement, _cancelWaitPeriod);
                        if (tsLogger.logger.isTraceEnabled()) {
                            tsLogger.logger.trace("Reaper deferring interrupt for TX scheduled for cancel " + reaperElement._control.get_uid());
                        }
                    }
                    break;
                case ReaperElement.CANCEL:
                    {
                        // ok, the worker must be wedged under a
                        // call to cancel() -- kick the thread and
                        // reschedule the element for a later
                        // check to ensure the thread responded to
                        // the kick
                        StringBuilder sb = new StringBuilder();
                        for (StackTraceElement element : reaperElement._worker.getStackTrace()) {
                            sb.append(element.toString());
                            sb.append("\n");
                        }
                        tsLogger.i18NLogger.wedged_reaperelement(sb.toString());
                        reaperElement._status = ReaperElement.CANCEL_INTERRUPTED;
                        reaperElement._worker.interrupt();
                        reinsertElement(reaperElement, _cancelFailWaitPeriod);
                        if (tsLogger.logger.isTraceEnabled()) {
                            tsLogger.logger.trace("TransactionReaper::check interrupting cancel in progress for " + reaperElement._control.get_uid());
                        }
                    }
                    break;
                case ReaperElement.CANCEL_INTERRUPTED:
                    {
                        // cancellation got truly wedged -- mark
                        // the element as a zombie so the worker
                        // exits when (if?) it wakes up and create
                        // a new worker thread to handle further
                        // cancellations. then mark the
                        // transaction as rollback only.
                        reaperElement._status = ReaperElement.ZOMBIE;
                        synchronized (this) {
                            _zombieCount++;
                            if (tsLogger.logger.isTraceEnabled()) {
                                tsLogger.logger.trace("Reaper " + Thread.currentThread() + " got a zombie " + reaperElement._worker + " (zombie count now " + _zombieCount + ") cancelling " + reaperElement._control.get_uid());
                            }
                            if (_zombieCount == _zombieMax) {
                                // log zombie overflow error call()
                                tsLogger.i18NLogger.error_coordinator_TransactionReaper_5(Integer.toString(_zombieCount));
                            }
                        }
                        _reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
                        _reaperWorkerThread.setDaemon(true);
                        _reaperWorkerThread.start();
                        // log a failed cancel()
                        tsLogger.i18NLogger.warn_coordinator_TransactionReaper_6(reaperElement._worker.toString(), reaperElement._control.get_uid());
                        // ok, since the worker was wedged we need to
                        // remove the entry from the timeouts and
                        // transactions lists then mark this tx as
                        // rollback only. we have to log a message
                        // whether we succeed, fail or get interrupted
                        removeElementReaper(reaperElement);
                        try {
                            if (reaperElement._control.preventCommit()) {
                                // log a successful preventCommit()
                                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_10(reaperElement._control.get_uid());
                                notifyListeners(reaperElement._control, false);
                            } else {
                                // log a failed preventCommit()
                                tsLogger.i18NLogger.warn_coordinator_TransactionReaper_11(reaperElement._control.get_uid());
                            }
                        } catch (Exception e1) {
                            // log an exception under preventCommit()
                            tsLogger.i18NLogger.warn_coordinator_TransactionReaper_12(reaperElement._control.get_uid(), e1);
                        }
                    }
                    break;
                case ReaperElement.FAIL:
                case ReaperElement.COMPLETE:
                    {
                        // ok, the worker should remove the tx
                        // from the transactions queue very soon
                        // but we need to progress to the next
                        // entry so we will steal in and do it
                        // first
                        removeElementReaper(reaperElement);
                    }
                    break;
            }
        }
    } while (true);
}
Also used : ReaperWorkerThread(com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread) ReaperElement(com.arjuna.ats.internal.arjuna.coordinator.ReaperElement)

Example 3 with ReaperWorkerThread

use of com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread in project narayana by jbosstm.

the class TransactionReaper method instantiate.

/**
 * Currently we let the reaper thread run at same priority as other threads.
 * Could get priority from environment.
 */
public static synchronized void instantiate() {
    if (TransactionReaper._theReaper == null) {
        if (tsLogger.logger.isTraceEnabled()) {
            tsLogger.logger.trace("TransactionReaper::instantiate()");
        }
        // default to dynamic mode
        TransactionReaper._dynamic = true;
        String mode = arjPropertyManager.getCoordinatorEnvironmentBean().getTxReaperMode();
        if (mode.compareTo(TransactionReaper.PERIODIC) == 0) {
            TransactionReaper._dynamic = false;
        }
        if (mode.compareTo(TransactionReaper.NORMAL) == 0) {
            TransactionReaper._dynamic = false;
            tsLogger.i18NLogger.warn_coordinator_TransactionReaper_19();
        }
        long checkPeriod = Long.MAX_VALUE;
        if (!TransactionReaper._dynamic) {
            checkPeriod = arjPropertyManager.getCoordinatorEnvironmentBean().getTxReaperTimeout();
        }
        TransactionReaper._theReaper = new TransactionReaper(checkPeriod);
        TransactionReaper._theReaper._cancelWaitPeriod = arjPropertyManager.getCoordinatorEnvironmentBean().getTxReaperCancelWaitPeriod();
        if (TransactionReaper._theReaper._cancelWaitPeriod < 10) {
            TransactionReaper._theReaper._cancelWaitPeriod = 10;
        }
        TransactionReaper._theReaper._cancelFailWaitPeriod = arjPropertyManager.getCoordinatorEnvironmentBean().getTxReaperCancelFailWaitPeriod();
        if (TransactionReaper._theReaper._cancelFailWaitPeriod < 10) {
            TransactionReaper._theReaper._cancelFailWaitPeriod = 10;
        }
        TransactionReaper._theReaper._zombieMax = arjPropertyManager.getCoordinatorEnvironmentBean().getTxReaperZombieMax();
        if (TransactionReaper._theReaper._zombieMax <= 0) {
            TransactionReaper._theReaper._zombieMax = 1;
        }
        _reaperThread = new ReaperThread(TransactionReaper._theReaper);
        // _reaperThread.setPriority(Thread.MIN_PRIORITY);
        _reaperThread.setDaemon(true);
        _reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
        _reaperWorkerThread.setDaemon(true);
        _reaperThread.start();
        _reaperWorkerThread.start();
    }
}
Also used : ReaperThread(com.arjuna.ats.internal.arjuna.coordinator.ReaperThread) ReaperWorkerThread(com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread)

Aggregations

ReaperWorkerThread (com.arjuna.ats.internal.arjuna.coordinator.ReaperWorkerThread)3 ReaperElement (com.arjuna.ats.internal.arjuna.coordinator.ReaperElement)2 ReaperThread (com.arjuna.ats.internal.arjuna.coordinator.ReaperThread)1