// Copyright (c) 2006 Dustin Sallings <dustin@spy.net> package net.spy.concurrent; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import net.spy.SpyObject; /** * Object that will wait until a predicate determines that the value has been * set to a particular value. * * Note that the predicate is not guaranteed to see every value change. It is * quite likely that changes will be missed when the value is changing rapidly. */ public class SynchronizationObject<T> extends SpyObject { private T theObject=null; /** * Construct a synchronization object on the given object. */ public SynchronizationObject(T o) { super(); theObject=o; } /** * Get the current value of this lock. */ public synchronized T get() { return theObject; } /** * Set a new value and signal anyone listening for a value change. */ public synchronized T set(T o) { T rv=theObject; theObject=o; notifyAll(); return rv; } /** * String this SynchronizationObject. */ @Override public String toString() { return "{SynchronizationObject obj=" + get() + "}"; } /** * Wait for the given predicate to become true in respect to the * contained object. * * @param p the predicate * @param timeout how long to wait for this condition to become true * @param timeunit the time unit for the timeout * @throws InterruptedException * @throws TimeoutException if a timeout occurs before the condition * becomes true */ public synchronized void waitUntilTrue(Predicate<T> p, long timeout, TimeUnit timeunit) throws InterruptedException, TimeoutException { assert p != null : "Null predicate"; assert timeout >= 0 : "Invalid timeout"; long now=System.currentTimeMillis(); long theEnd=now + timeunit.toMillis(timeout); // If theEnd is negative here, it's because we rolled over, likely // because the timeout was too far in the future. Might as well make // it effectively infinite. if(theEnd < 0) { theEnd=Long.MAX_VALUE; } while(!p.evaluate(theObject)) { if(now >= theEnd) { throw new TimeoutException(); } wait(timeunit.toMillis(theEnd - now)); now=System.currentTimeMillis(); } } /** * Wait for the contained object to become non-null. * * @param timeout how long to wait for this condition to become true * @param timeunit the time unit for the timeout * @throws InterruptedException * @throws TimeoutException if a timeout occurs before the condition * becomes true */ public void waitUntilNotNull(long timeout, TimeUnit timeunit) throws InterruptedException, TimeoutException { waitUntilTrue(new Predicate<T>() { public boolean evaluate(T o) { return o != null; } }, timeout, timeunit); } /** * Wait for the contained object to become equal to the provided value. * * If the provided value is null, then this also waits for the contained * object to become null. * * @param val the value to wait for * @param timeout how long to wait for this condition to become true * @param timeunit the time unit for the timeout * @throws InterruptedException * @throws TimeoutException if a timeout occurs before the condition * becomes true */ public void waitUntilEquals(final T val, long timeout, TimeUnit timeunit) throws InterruptedException, TimeoutException { waitUntilTrue(new Predicate<T>() { public boolean evaluate(T o) { return val == null ? o == null : val.equals(o); } }, timeout, timeunit); } /** * Synchronization object predicate for evaluation in waitUntilTrue. */ public static interface Predicate<T> { /** * Evaluate with the current object to determine whether the condition * is true. * * @param o the current object * @return whether the condition is met */ boolean evaluate(T o); } }