/*
* JBoss, Home of Professional Open Source
* Copyright 2007, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2007,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1998, 1999, 2000, 2001,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: ReaperElement.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.coordinator;
import java.util.concurrent.atomic.AtomicInteger;
import com.arjuna.ats.arjuna.coordinator.Reapable;
import com.arjuna.ats.arjuna.logging.tsLogger;
public class ReaperElement implements Comparable<ReaperElement>
{
/*
* Currently, once created the reaper object and thread stay around forever.
* We could destroy both once the list of transactions is null. Depends upon
* the relative cost of recreating them over keeping them around.
*/
public ReaperElement(Reapable control, int timeout)
{
if (tsLogger.logger.isTraceEnabled()) {
tsLogger.logger.trace("ReaperElement::ReaperElement ( " + control + ", "
+ timeout + " )");
}
_control = control;
_timeout = timeout;
_status = RUN;
_worker = null;
/*
* Given a timeout period in seconds, calculate its absolute value from
* the current time of day in milliseconds.
*/
_absoluteTimeoutMills = (timeout * 1000L) + System.currentTimeMillis();
// add additional variation to distinguish instances created in the same millisecond.
_bias = getBiasCounter();
}
public String toString ()
{
return "ReaperElement < "+_control+", "+_timeout+", "+statusName()+", "+_worker+" >";
}
/**
* Order by absoluteTimeout first, then by Uid.
* This is required so that the set maintained by the TransactionReaper
* is in timeout order for efficient processing.
*
* @param other the ReaperElement to compare
* @return 0 if equal, 1 if this is greater, -1 if this is smaller
*/
public int compareTo(ReaperElement other)
{
if(this == other) {
return 0;
}
if(_absoluteTimeoutMills == other._absoluteTimeoutMills) {
if (_bias == other._bias) {
if(_control.get_uid().equals(other._control.get_uid())) {
return 0;
} else if (_control.get_uid().greaterThan(other._control.get_uid())) {
return 1;
} else {
return -1;
}
} else {
return (_bias > other._bias) ? 1 : -1;
}
} else {
return (_absoluteTimeoutMills > other._absoluteTimeoutMills) ? 1 : -1;
}
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReaperElement that = (ReaperElement) o;
// could be made more efficient, but this is easier to maintain consistently with compareTo.
// note that the comparison includes _absoluteTimeoutMills which is mutable, so weird things
// may happen if trying to locate the obj in a collection whilst concurrently mutating it.
// fortunately such comparisons are generally done with the obj identity shortcut anyhow.
return (compareTo(that) == 0);
}
@Override
public int hashCode()
{
return _control.get_uid().hashCode();
}
public final Reapable _control;
private long _absoluteTimeoutMills;
private final int _bias;
// bias is used to distinguish/sort instances with the same _absoluteTimeoutMills
// as using Uid for this purpose is expensive. JBTM-611
private static final int MAX_BIAS = 1000000;
private static final AtomicInteger biasCounter = new AtomicInteger();
private static int getBiasCounter()
{
int value = 0;
do {
value = biasCounter.getAndIncrement();
if(value == MAX_BIAS) {
biasCounter.set(0);
}
} while(value >= MAX_BIAS);
return value; // range 0 to MAX_BIAS-1 inclusive.
}
public int _timeout;
/*
* status field to track the progress of the reaper worker which is
* attempting to cancel the associated TX. this is necessary to ensure
* that the the reaper only interrupts the worker in the windows where
* it may be exposed to being wedged by client code and is sure to be
* able to catch and handle an interrupt without dying. all accesses
* to this field must be synchronized on the containing element.
*
* this field is always initialised to RUN, indicating that
* the element is associated with a running transaction. the
* following transitions can occur, performed either by the
* reaper (R) or the worker (W)
*
* RUN --> SCHEDULE_CANCEL (R)
*
* SCHEDULE_CANCEL --> CANCEL (W)
*
* CANCEL --> COMPLETE (W)
*
* CANCEL --> FAIL (W)
*
* CANCEL --> CANCEL_INTERRUPTED (R)
*
* CANCEL_INTERRUPTED --> COMPLETE (W)
*
* CANCEL_INTERRUPTED --> FAIL (W)
*
* CANCEL_INTERRUPTED --> ZOMBIE (R)
*/
public int _status;
/*
* the reaper worker which is attempting to cancel the associated TX
*/
public Thread _worker;
/*
* status values for progressing reaper element from default
* RUN status through stages of cancellation from
* SCHEDULE_CANCEL to ZOMBIE. the reaper thread can only
* interrupt a reaper worker thread if it is wedged while
* the element is in state CANCEL.
*/
/*
* status of a reaper element for a TX which has not yet timed out
*/
public static final int RUN = 0;
/*
* status of a reaper element which has been queued for
* cancellation by a reaper worker
*/
public static final int SCHEDULE_CANCEL = 1;
/*
* status of a reaper element while the reaper worker is under
* a call to the TX cancel operation and, hence, potentially
* exposed to being wedged. the reaper may safely interrupt
* the worker when it is in this state
*/
public static final int CANCEL = 2;
/*
* status of a reaper element if the reaper thread has
* interrupted the worker during the CANCEL state. if the
* reaper discovers the thread is still in this state
* following a suitable delay then the thread is seriously
* wedged (possibly on a non-interruptible i/o) and requires
* notice of termination (by resetting the state to ZOMBIE)
* and replacement by a new worker thread.
*/
public static final int CANCEL_INTERRUPTED = 3;
/*
* status of a reaper element if the reaper worker has been
* unable to cancel the TX and has failed to mark it as
* rollback only. it is safe for the reaper to remove the
* element from the transactions list if it is in this state
* (modulo synchronization) although the worker should do so
* with minimal delay.
*/
public static final int FAIL = 4;
/*
* status of a reaper element if the reaper worker has been
* able to cancel the tx or has marked it as rollback only. it
* is safe for the reaper to remove the element from the
* transactions list if it is in this state (modulo
* synchronization) although the worker should do so with
* minimal delay.
*/
public static final int COMPLETE = 5;
/*
* status of a reaper element if the worker got so wedged it
* failed to respond to an interrupt either during
* cancellation or marking as rollback only. if the worker
* wakes up and finds the element in this state then it must
* exit. the reaper will have ensured that the failure to
* cancel and rollback the transaction has been logged and
* will have removed the element from the transactions list.
*/
public static final int ZOMBIE = 6;
/*
* convenience method to provide printable string for current status
* for use in debugging/logging. should only be called while
* synchronized.
*/
public final String statusName()
{
switch (_status)
{
case RUN:
return "RUN";
case SCHEDULE_CANCEL:
return "SCHEDULE_CANCEL";
case CANCEL:
return "CANCEL";
case CANCEL_INTERRUPTED:
return "CANCEL_INTERRUPTED";
case FAIL:
return "FAIL";
case COMPLETE:
return "COMPLETE";
case ZOMBIE:
return "ZOMBIE";
default:
return "UNKNOWN";
}
}
/**
* Returns absolute timeout in milliseconds
* @return The absolute timeout in millis
*/
public long getAbsoluteTimeout()
{
return _absoluteTimeoutMills;
}
/**
* Sets the absolute timeout.
*
* @param absoluteTimeout value in milliseconds
*/
public void setAbsoluteTimeout(long absoluteTimeout)
{
this._absoluteTimeoutMills = absoluteTimeout;
}
}