/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.extender.internal.util.concurrent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Simple counting class which can be incremented or decremented in a
* synchronized manner. This class can be used as a synchronization mechanism
* between threads mainly though {@link #waitForZero(long)} method.
*
* The main usage of the class is to allow a master thread, to know when other
* threads (slaves) have passed a certain point in execution.
*
* <p/> As opposed to a Barrier or a Semaphore, this class should be used only
* with 1 waiting thread (a master) and any number of slave threads.
*
* <pre style="code">
* Thread 1:
* counter.increment();
* thread2.start();
* counter.increment();
* thread3.start();
*
* // wait 1 second for other threads to complete
* counter.waitForZero(1000);
*
* Thread 2:
* // do some work
* counter.decrement();
*
* Thread 3:
* // do some work
* counter.decrement();
*
* </pre>
*
* <p/> Mainly for usage inside the framework. All methods are thread-safe
* however for the master/slave pattern, synchronized blocks are recommended as
* multiple operations have to be executed at once.
*
* @author Costin Leau
*
*/
public class Counter {
private int counter = 0;
private static final Log log = LogFactory.getLog(Counter.class);
private final String name;
/**
* Create counter with a given name.
*
* @param name counter name
*/
public Counter(String name) {
this.name = name;
}
/**
* Increment the counter value.
*/
public synchronized void increment() {
counter++;
if (log.isTraceEnabled())
log.trace("counter [" + name + "] incremented to " + counter);
}
/**
* Decrement the counter value.
*/
public synchronized void decrement() {
counter--;
if (log.isTraceEnabled())
log.trace("counter [" + name + "] decremented to " + counter);
notifyAll();
}
public synchronized boolean decrementAndWait(long timeToWait) {
decrement();
if (counter > 0)
return waitForZero(timeToWait);
return true;
}
/**
* Check if the counter value is zero.
*
* @return true if value is equal or below zero, false otherwise.
*/
public synchronized boolean isZero() {
return is(0);
}
public synchronized boolean is(int value) {
return counter == value;
}
/**
* Return the counter value.
*
* @return the counter value.
*/
public synchronized int getValue() {
return counter;
}
public synchronized String toString() {
return "" + counter;
}
/**
* Specialized method which waits for 0. Identical to waitFor(0, waitTime).
*
* @see #waitFor(int, long)
* @param waitTime
* @return true if the waiting timed out, false otherwise
*/
public synchronized boolean waitForZero(long waitTime) {
return waitFor(0, waitTime);
}
/**
* Wait maximum the given amount of time, for the counter to reach the given
* value. This mechanism relies on {@link Object#wait(long)} and
* {@link Object#notify()} mechanism to work appropriately. Please see the
* class javadoc for more info.
*
* <p/> This method will stop waiting and return true if the thread is
* interrupted.
*
* @param value the value to wait for
* @param waitTime the time (in miliseconds) to wait for zero value
* @return true if the waiting timed out, false otherwise
*/
public synchronized boolean waitFor(int value, long waitTime) {
boolean timedout = false;
long remainingTime = waitTime;
long startTime = System.currentTimeMillis();
while (counter > value && !timedout) {
// start waiting
try {
this.wait(remainingTime);
// compute the remaining time
remainingTime = waitTime - (System.currentTimeMillis() - startTime);
timedout = remainingTime <= 0;
}
catch (InterruptedException ex) {
timedout = true;
}
}
return timedout;
}
}