1 /* 2 * Copyright 2006-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.osgi.extender.internal.util.concurrent; 18 19 import org.apache.commons.logging.Log; 20 import org.apache.commons.logging.LogFactory; 21 22 /** 23 * Simple counting class which can be incremented or decremented in a 24 * synchronized manner. This class can be used as a synchronization mechanism 25 * between threads mainly though {@link #waitForZero(long)} method. 26 * 27 * The main usage of the class is to allow a master thread, to know when other 28 * threads (slaves) have passed a certain point in execution. 29 * 30 * <p/> As opposed to a Barrier or a Semaphore, this class should be used only 31 * with 1 waiting thread (a master) and any number of slave threads. 32 * 33 * <pre style="code"> 34 * Thread 1: 35 * counter.increment(); 36 * thread2.start(); 37 * counter.increment(); 38 * thread3.start(); 39 * 40 * // wait 1 second for other threads to complete 41 * counter.waitForZero(1000); 42 * 43 * Thread 2: 44 * // do some work 45 * counter.decrement(); 46 * 47 * Thread 3: 48 * // do some work 49 * counter.decrement(); 50 * 51 * </pre> 52 * 53 * <p/> Mainly for usage inside the framework. All methods are thread-safe 54 * however for the master/slave pattern, synchronized blocks are recommended as 55 * multiple operations have to be executed at once. 56 * 57 * @author Costin Leau 58 * 59 */ 60 public class Counter { 61 62 private int counter = 0; 63 64 private static final Log log = LogFactory.getLog(Counter.class); 65 66 private final String name; 67 68 69 /** 70 * Create counter with a given name. 71 * 72 * @param name counter name 73 */ 74 public Counter(String name) { 75 this.name = name; 76 } 77 78 /** 79 * Increment the counter value. 80 */ 81 public synchronized void increment() { 82 counter++; 83 if (log.isTraceEnabled()) 84 log.trace("counter [" + name + "] incremented to " + counter); 85 } 86 87 /** 88 * Decrement the counter value. 89 */ 90 public synchronized void decrement() { 91 counter--; 92 if (log.isTraceEnabled()) 93 log.trace("counter [" + name + "] decremented to " + counter); 94 notifyAll(); 95 } 96 97 public synchronized boolean decrementAndWait(long timeToWait) { 98 decrement(); 99 if (counter > 0) 100 return waitForZero(timeToWait); 101 return true; 102 } 103 104 /** 105 * Check if the counter value is zero. 106 * 107 * @return true if value is equal or below zero, false otherwise. 108 */ 109 public synchronized boolean isZero() { 110 return is(0); 111 } 112 113 public synchronized boolean is(int value) { 114 return counter == value; 115 } 116 117 /** 118 * Return the counter value. 119 * 120 * @return the counter value. 121 */ 122 public synchronized int getValue() { 123 return counter; 124 } 125 126 public synchronized String toString() { 127 return "" + counter; 128 } 129 130 /** 131 * Specialized method which waits for 0. Identical to waitFor(0, waitTime). 132 * 133 * @see #waitFor(int, long) 134 * @param waitTime 135 * @return true if the waiting timed out, false otherwise 136 */ 137 public synchronized boolean waitForZero(long waitTime) { 138 return waitFor(0, waitTime); 139 } 140 141 /** 142 * Wait maximum the given amount of time, for the counter to reach the given 143 * value. This mechanism relies on {@link Object#wait(long)} and 144 * {@link Object#notify()} mechanism to work appropriately. Please see the 145 * class javadoc for more info. 146 * 147 * <p/> This method will stop waiting and return true if the thread is 148 * interrupted. 149 * 150 * @param value the value to wait for 151 * @param waitTime the time (in miliseconds) to wait for zero value 152 * @return true if the waiting timed out, false otherwise 153 */ 154 public synchronized boolean waitFor(int value, long waitTime) { 155 boolean timedout = false; 156 long remainingTime = waitTime; 157 long startTime = System.currentTimeMillis(); 158 159 while (counter > value && !timedout) { 160 // start waiting 161 try { 162 this.wait(remainingTime); 163 // compute the remaining time 164 remainingTime = waitTime - (System.currentTimeMillis() - startTime); 165 timedout = remainingTime <= 0; 166 } 167 catch (InterruptedException ex) { 168 timedout = true; 169 } 170 } 171 172 return timedout; 173 } 174 }