001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util;
011:
012: import org.mmbase.util.logging.Logger;
013: import org.mmbase.util.logging.Logging;
014:
015: /**
016: * Also called counting semaphores, Dijkstra semaphores are used to control access to
017: * a set of resources. A Dijkstra semaphore has a count associated with it and each
018: * acquire() call reduces the count. A thread that tries to acquire() a Dijkstra
019: * semaphore with a zero count blocks until someone else calls release() thus increasing
020: * the count.
021: * <b>When to use</b>
022: * Recommended when applications require a counting semaphore. Implementing
023: * a counting semaphore using wait()/notify() and counters within your application
024: * code makes your code less readable and quickly increases the complexity
025: * (especially when you have the need for multiple counting semaphores). Can also
026: * be used to port code from POSIX environment.
027: *
028: * @author Karthik Rangaraju
029: * @author Michiel Meeuwissen
030: * @since MMBase-1.6
031: * @version $Id: DijkstraSemaphore.java,v 1.8 2005/03/16 19:06:44 michiel Exp $
032: */
033: public class DijkstraSemaphore {
034:
035: private static final Logger log = Logging
036: .getLoggerInstance(DijkstraSemaphore.class);
037:
038: private int count;
039: private int maxCount;
040: private Object starvationLock = new Object();
041:
042: /**
043: * Creates a Dijkstra semaphore with the specified max count and initial count set
044: * to the max count (all resources released)
045: * @param pMaxCount is the max semaphores that can be acquired
046: */
047: public DijkstraSemaphore(int pMaxCount) {
048: this (pMaxCount, pMaxCount);
049: }
050:
051: /**
052: * Creates a Dijkstra semaphore with the specified max count and an initial count
053: * of acquire() operations that are assumed to have already been performed.
054: * @param pMaxCount is the max semaphores that can be acquired
055: * @param pInitialCount is the current count (setting it to zero means all semaphores
056: * have already been acquired). 0 <= pInitialCount <= pMaxCount
057: */
058: public DijkstraSemaphore(int pMaxCount, int pInitialCount) {
059: count = pInitialCount;
060: maxCount = pMaxCount;
061: }
062:
063: /**
064: * If the count is non-zero, acquires a semaphore and decrements the count by 1,
065: * otherwise blocks until a release() is executed by some other thread.
066: * @throws InterruptedException if the thread is interrupted when blocked
067: * @see #tryAcquire()
068: * @see #acquireAll()
069: */
070: public synchronized void acquire() throws InterruptedException {
071: // Using a spin lock to take care of rogue threads that can enter
072: // before a thread that has exited the wait state acquires the monitor
073: while (count == 0) {
074: long startwait = 0;
075: if (log.isDebugEnabled()) {
076: startwait = System.currentTimeMillis();
077: }
078: wait();
079: if (startwait != 0) {
080: log.debug("Waited "
081: + (System.currentTimeMillis() - startwait)
082: + " ms for a resource");
083: }
084: }
085: count--;
086: synchronized (starvationLock) {
087: if (count == 0) {
088: starvationLock.notify();
089: }
090: }
091: }
092:
093: /**
094: * Non-blocking version of acquire().
095: * @return true if semaphore was acquired (count is decremented by 1), false
096: * otherwise
097: */
098: public synchronized boolean tryAcquire() {
099: if (count != 0) {
100: count--;
101: synchronized (starvationLock) {
102: if (count == 0) {
103: starvationLock.notify();
104: }
105: }
106: return true;
107: } else {
108: return false;
109: }
110: }
111:
112: /**
113: * Releases a previously acquires semaphore and increments the count by one. Does not
114: * check if the thread releasing the semaphore was a thread that acquired the
115: * semaphore previously. If more releases are performed than acquires, the count is
116: * not increased beyond the max count specified during construction.
117: * @see #release(int pCount)
118: * @see #releaseAll()
119: */
120: public synchronized void release() {
121: count++;
122: if (count > maxCount) {
123: count = maxCount;
124: }
125: notify();
126: }
127:
128: /**
129: * Same as release() except that the count is increased by pCount instead of 1. The
130: * resulting count is capped at max count specified in the constructor
131: * @param pCount is the amount by which the counter should be incremented
132: * @see #release()
133: */
134: public synchronized void release(int pCount) {
135: while (count < maxCount && pCount != 0) {
136: release();
137: pCount--;
138: }
139: }
140:
141: /**
142: * Tries to acquire all the semaphores thus bringing the count to zero.
143: * @throws InterruptedException if the thread is interrupted when blocked on this call
144: * @see #acquire()
145: * @see #releaseAll()
146: */
147: public synchronized void acquireAll() throws InterruptedException {
148: while (count != 0) {
149: acquire();
150: }
151: }
152:
153: /**
154: * Releases all semaphores setting the count to max count.
155: * Warning: If this method is called by a thread that did not make a corresponding
156: * acquireAll() call, then you better know what you are doing!
157: * @see #acquireAll()
158: */
159: public synchronized void releaseAll() {
160: release(maxCount);
161: }
162:
163: /**
164: * This method blocks the calling thread until the count drops to zero.
165: * The method is not stateful and hence a drop to zero will not be recognized
166: * if a release happens before this call. You can use this method to implement
167: * threads that dynamically increase the resource pool or that log occurences
168: * of resource starvation. Also called a reverse-sensing semaphore
169: * @throws InterruptedException if the thread is interrupted while waiting
170: */
171: public void starvationCheck() throws InterruptedException {
172: synchronized (starvationLock) {
173: if (count != 0) {
174: starvationLock.wait();
175: }
176: }
177: }
178: }
|