/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2004
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY 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 along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.ACS.MasterComponentImpl;
import java.util.logging.Logger;
/**
* Helper class that allows to synchronize the sending of events with the
* state changes due to previous events.
* Note that state changes occur asynchronously when triggered by activity states.
* <p>
* This class is abstracted from concrete state change notification APIs so that
* subclasses can be implemented as such listeners; they must call {@link #stateChangedNotify()}
* upon notification by the state machine.
*
* @author hsommer
* created Apr 30, 2004 9:50:34 AM
*/
public class StateChangeSemaphore
{
private volatile int notifCount = 0;
private volatile int minCount;
private final Logger logger;
public StateChangeSemaphore(Logger logger) {
if (logger == null) {
throw new IllegalArgumentException("logger must not be null");
}
this.logger = logger;
}
/**
* To be called from state change listener method of subclass.
* <p>
* This call is expected to come from a different thread than the one that calls <code>waitForStateChanges</code>.
* Both methods are synchronized, but there will not be a deadlock, since <code>waitForStateChanges</code>
* yields ownership of the monitor when calling {@link Object#wait() wait}.
*/
protected synchronized void stateChangedNotify() {
logger.finest("stateChangedNotify: called in thread '" + Thread.currentThread().getName() + "'.");
notifCount++;
if (notifCount >= minCount) {
// release thread that blocks on waitForStateChanges
notifyAll();
}
}
/**
* Blocks until the specified number of state change notifications have been received
* since last call to {@link #reset()}.
* <p>
* Does not block at all if at least <code>count</code> state change notifications
* have been received since the last call to <code>reset</code>. Otherwise only blocks until
* the missing number of notifications has arrived.
* Before returning, this method subtracts <code>count</code> from the internal counter for state change notifications,
* which allows a client to catch up with fast firing event notifications by calling this method several times.
* <p>
* Note that the "synchronized" modifier is required in order for the client thread
* to obtain ownership of this semaphore (i.e. its monitor),
* without which the thread synchronization would not work.
* See {@link Object#notify()} for an explanation.
*
* @param count number of state change notifications that must be received
* since last call to {@link #reset()}
* so that this call will return
* @throws InterruptedException
*/
public synchronized void waitForStateChanges(int count) throws InterruptedException {
logger.finest("waitForStateChanges: called in thread '" + Thread.currentThread().getName() + "'.");
minCount = count;
// was stateChangedNotify called sufficiently often already?
while (notifCount < count) {
logger.finest("waitForStateChanges: waiting for " + (count - notifCount) + " state change notification(s)");
// releases current thread's ownership of this StateChangeSemaphore's monitor, and blocks until other thread calls notify
wait();
// now our thread has re-obtained ownership of this StateChangeSemaphore's monitor, so the field notifCount is again ours.
}
notifCount = notifCount - count;
logger.finest("waitForStateChanges: state change notification counter down at " + notifCount);
}
/**
* Resets the state change notification count.
* Must be called prior to sending a state event.
*/
public synchronized void reset() {
notifCount = 0;
}
}