/* * File: Sync.java Originally written by Doug Lea and released into the public * domain. This may be used for any purposes whatsoever without acknowledgment. * Thanks for the assistance and support of Sun Microsystems Labs, and everyone * contributing, testing, and using this code. History: Date Who What 11Jun1998 * dl Create public version 5Aug1998 dl Added some convenient time constants */ package org.castor.core.util.concurrent; /** * Main interface for locks, gates, and conditions. * <p> * Sync objects isolate waiting and notification for particular logical states, * resource availability, events, and the like that are shared across multiple * threads. Use of Syncs sometimes (but by no means always) adds flexibility and * efficiency compared to the use of plain java monitor methods and locking, and * are sometimes (but by no means always) simpler to program with. * <p> * Most Syncs are intended to be used primarily (although not exclusively) in * before/after constructions such as: * * <pre> * class X { * Sync gate; * * // ... * * public void m() { * try { * gate.acquire(); // block until condition holds * try { * // ... method body * } * finally { * gate.release() * } * } * catch (InterruptedException ex) { * // ... evasive action * } * }public void m2(Sync cond) { // use supplied condition * try { * if (cond.attempt(10)) { // try the condition for 10 ms * try { * // ... method body * } * finally { * cond.release() * } * } * } * catch (InterruptedException ex) { * // ... evasive action * } * }} * </pre> * * Syncs may be used in somewhat tedious but more flexible replacements for * built-in Java synchronized blocks. For example: * * <pre> * class HandSynched { * private double state_ = 0.0; * * private final Sync lock; // use lock type supplied in constructor * * public HandSynched(Sync l) { * lock = l; * } * * public void changeState(double d) { * try { * lock.acquire(); * try { * state_ = updateFunction(d); * } finally { * lock.release(); * } * } catch(InterruptedException ex) {} * } * * public double getState() { * double d = 0.0; * try { * lock.acquire(); * try { * d = accessFunction(state_); * } finally { * lock.release(); * } * } catch(InterruptedException ex) {} * return d; * } * * private double updateFunction(double d) { ... }private double accessFunction(double d) { ... }} * </pre> * * If you have a lot of such methods, and they take a common form, you can * standardize this using wrappers. Some of these wrappers are standardized in * LockedExecutor, but you can make others. For example: * * <pre> * class HandSynchedV2 { * private double state_ = 0.0; * * private final Sync lock; // use lock type supplied in constructor * * public HandSynchedV2(Sync l) { * lock = l; * } * * protected void runSafely(Runnable r) { * try { * lock.acquire(); * try { * r.run(); * } finally { * lock.release(); * } * } catch(InterruptedException ex) { // propagate without throwing * Thread.currentThread().interrupt(); * } * } * * public void changeState(double d) { * runSafely(new Runnable() { * public void run() { * state_ = updateFunction(d); * } * }); * } * // ... * } * </pre> * * <p> * One reason to bother with such constructions is to use deadlock- avoiding * back-offs when dealing with locks involving multiple objects. For example, * here is a Cell class that uses attempt to back-off and retry if two Cells are * trying to swap values with each other at the same time. * * <pre> * * class Cell { * long value; * Sync lock = ... // some sync implementation class * void swapValue(Cell other) { * for (;;) { * try { * lock.acquire(); * try { * if (other.lock.attempt(100)) { * try { * long t = value; * value = other.value; * other.value = t; * return; * } * finally { other.lock.release(); } * } * } * finally { lock.release(); } * } * catch (InterruptedException ex) { return; } * } * } * } * * </pre> * * <p> * Here is an even fancier version, that uses lock re-ordering upon conflict: * * <pre> * * class Cell { * long value; * Sync lock = ...; * private static boolean trySwap(Cell a, Cell b) { * a.lock.acquire(); * try { * if (!b.lock.attempt(0)) * return false; * try { * long t = a.value; * a.value = b.value; * b.value = t; * return true; * } * finally { other.lock.release(); } * } * finally { lock.release(); } * return false; * } * * void swapValue(Cell other) { * try { * while (!trySwap(this, other) && * !tryswap(other, this)) * Thread.sleep(1); * } * catch (InterruptedException ex) { return; } * } * } * * </pre> * * <p> * Interruptions are in general handled as early as possible. Normally, * InterruptionExceptions are thrown in acquire and attempt(msec) if * interruption is detected upon entry to the method, as well as in any later * context surrounding waits. However, interruption status is ignored in * release(); * <p> * Timed versions of attempt report failure via return value. If so desired, you * can transform such constructions to use exception throws via * * <pre> * if( !c.attempt(timeval)) throw new TimeoutException(timeval); * </pre> * * <p> * The TimoutSync wrapper class can be used to automate such usages. * <p> * All time values are expressed in milliseconds as longs, which have a maximum * value of Long.MAX_VALUE, or almost 300,000 centuries. It is not known whether * JVMs actually deal correctly with such extreme values. For convenience, some * useful time values are defined as static constants. * <p> * All implementations of the three Sync methods guarantee to somehow employ * Java <code>synchronized</code> methods or blocks, and so entail the memory * operations described in JLS chapter 17 which ensure that variables are loaded * and flushed within before/after constructions. * <p> * Syncs may also be used in spinlock constructions. Although it is normally * best to just use acquire(), various forms of busy waits can be implemented. * For a simple example (but one that would probably never be preferable to * using acquire()): * * <pre> * * class X { * Sync lock = ... * void spinUntilAcquired() throws InterruptedException { * // Two phase. * // First spin without pausing. * int purespins = 10; * for (int i = 0; i < purespins; ++i) { * if (lock.attempt(0)) * return true; * } * // Second phase - use timed waits * long waitTime = 1; // 1 millisecond * for (;;) { * if (lock.attempt(waitTime)) * return true; * else * waitTime = waitTime * 3 / 2 + 1; // increase 50% * } * } * } * * </pre> * * <p> * In addition pure synchronization control, Syncs may be useful in any context * requiring before/after methods. For example, you can use an ObservableSync * (perhaps as part of a LayeredSync) in order to obtain callbacks before and * after each method invocation for a given class. * <p> * <p>[ <a * href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> * Introduction to this package. </a>] */ public interface Sync { /** * Wait (possibly forever) until successful passage. Fail only upon * interuption. Interruptions always result in `clean' failures. On failure, * you can be sure that it has not been acquired, and that no corresponding * release should be performed. Conversely, a normal return guarantees that * the acquire was successful. */ public void acquire() throws InterruptedException; /** * Wait at most msecs to pass; report whether passed. * <p> * The method has best-effort semantics: The msecs bound cannot be * guaranteed to be a precise upper bound on wait time in Java. * Implementations generally can only attempt to return as soon as possible * after the specified bound. Also, timers in Java do not stop during * garbage collection, so timeouts can occur just because a GC intervened. * So, msecs arguments should be used in a coarse-grained manner. Further, * implementations cannot always guarantee that this method will return at * all without blocking indefinitely when used in unintended ways. For * example, deadlocks may be encountered when called in an unintended * context. * <p> * * @param msecs * the number of milleseconds to wait. An argument less than or * equal to zero means not to wait at all. However, this may * still require access to a synchronization lock, which can * impose unbounded delay if there is a lot of contention among * threads. * @return true if acquired */ public boolean attempt(long msecs) throws InterruptedException; /** * Potentially enable others to pass. * <p> * Because release does not raise exceptions, it can be used in `finally' * clauses without requiring extra embedded try/catch blocks. But keep in * mind that as with any java method, implementations may still throw * unchecked exceptions such as Error or NullPointerException when faced * with uncontinuable errors. However, these should normally only be caught * by higher-level error handlers. */ public void release(); /** One second, in milliseconds; convenient as a time-out value * */ public static final long ONE_SECOND = 1000; /** One minute, in milliseconds; convenient as a time-out value * */ public static final long ONE_MINUTE = 60 * ONE_SECOND; /** One hour, in milliseconds; convenient as a time-out value * */ public static final long ONE_HOUR = 60 * ONE_MINUTE; /** One day, in milliseconds; convenient as a time-out value * */ public static final long ONE_DAY = 24 * ONE_HOUR; /** One week, in milliseconds; convenient as a time-out value * */ public static final long ONE_WEEK = 7 * ONE_DAY; /** One year in milliseconds; convenient as a time-out value * */ // Not that it matters, but there is some variation across // standard sources about value at msec precision. // The value used is the same as in java.util.GregorianCalendar public static final long ONE_YEAR = (long)(365.2425 * ONE_DAY); /** One century in milliseconds; convenient as a time-out value * */ public static final long ONE_CENTURY = 100 * ONE_YEAR; }