1 /*
2  * Created on 18-Sep-2004
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package org.gudy.azureus2.core3.util;
21 
22 /**
23  * @author parg
24  *
25  */
26 public class
27 AESemaphore
28 	extends AEMonSem
29 {
30 	private int		dont_wait	= 0;
31 
32 	private int		total_reserve	= 0;
33 	private int		total_release	= 0;
34 
35 	private boolean	released_forever	= false;
36 
37 	protected Thread	latest_waiter;
38 
39 	public
AESemaphore( String _name )40 	AESemaphore(
41 		String		_name )
42 	{
43 		this( _name, 0 );
44 	}
45 
46 	public
AESemaphore( String _name, int count )47 	AESemaphore(
48 		String		_name,
49 		int			count )
50 	{
51 		super( _name, false );
52 
53 		dont_wait		= count;
54 		total_release	= count;
55 	}
56 
57 	public void
reserve()58 	reserve()
59 	{
60 		if ( !reserve(0)){
61 
62 			Debug.out( "AESemaphore: reserve completed without acquire [" + getString() + "]" );
63 		}
64 	}
65 
66 	public boolean
reserve( long millis )67 	reserve(
68 		long	millis )
69 	{
70 		return( reserveSupport( millis, 1 ) == 1 );
71 	}
72 
73 	public boolean
reserveIfAvailable()74 	reserveIfAvailable()
75 	{
76 		synchronized(this){
77 
78 			if ( released_forever || dont_wait > 0 ){
79 
80 				reserve();
81 
82 				return( true );
83 
84 			}else{
85 
86 				return( false );
87 			}
88 		}
89 	}
90 
91 	public int
reserveSet( int max_to_reserve, long millis )92 	reserveSet(
93 		int		max_to_reserve,
94 		long	millis )
95 	{
96 		return( reserveSupport( millis, max_to_reserve));
97 	}
98 
99 	public int
reserveSet( int max_to_reserve )100 	reserveSet(
101 		int	max_to_reserve )
102 	{
103 		return( reserveSupport( 0, max_to_reserve));
104 	}
105 
106 	protected int
reserveSupport( long millis, int max_to_reserve )107 	reserveSupport(
108 		long	millis,
109 		int		max_to_reserve )
110 	{
111 		if ( DEBUG ){
112 
113 			super.debugEntry();
114 		}
115 
116 		synchronized(this){
117 
118 			entry_count++;
119 
120 			//System.out.println( name + "::reserve");
121 
122 			if ( released_forever ){
123 
124 				return(1);
125 			}
126 
127 			if ( dont_wait == 0 ){
128 
129 				try{
130 					waiting++;
131 
132 					latest_waiter	= Thread.currentThread();
133 
134 					if ( waiting > 1 ){
135 
136 						// System.out.println( "AESemaphore: " + name + " contended" );
137 					}
138 
139 					if ( millis == 0 ){
140 
141 							// we can get spurious wakeups (see Object javadoc) so we need to guard against
142 							// their possibility
143 
144 						int	spurious_count	= 0;
145 
146 						while( true ){
147 
148 							wait();
149 
150 							if ( total_reserve == total_release ){
151 
152 								spurious_count++;
153 
154 								if ( spurious_count > 1024 ){
155 
156 									Debug.out( "AESemaphore: spurious wakeup limit exceeded" );
157 
158 									throw( new Throwable( "die die die" ));
159 
160 								}else{
161 
162 									// Debug.out("AESemaphore: spurious wakeup, ignoring" );
163 								}
164 							}else{
165 
166 								break;
167 							}
168 						}
169 					}else{
170 
171 							// we don't hugely care about spurious wakeups here, it'll just appear
172 							// as a failed reservation a bit early
173 
174 						wait(millis);
175 					}
176 
177 					if ( total_reserve == total_release ){
178 
179 							// here we have timed out on the wait without acquiring
180 
181 						waiting--;
182 
183 						return( 0 );
184 					}
185 
186 					total_reserve++;
187 
188 					return( 1 );
189 
190 				}catch( Throwable e ){
191 
192 					waiting--;
193 
194 					Debug.out( "**** semaphore operation interrupted ****" );
195 
196 					throw( new RuntimeException("Semaphore: operation interrupted", e ));
197 
198 				}finally{
199 
200 					latest_waiter = null;
201 				}
202 			}else{
203 				int	num_to_get = max_to_reserve>dont_wait?dont_wait:max_to_reserve;
204 
205 				dont_wait -= num_to_get;
206 
207 				total_reserve += num_to_get;
208 
209 				return( num_to_get );
210 			}
211 		}
212 	}
213 
214 	public void
release()215 	release()
216 	{
217 		try{
218 			synchronized(this){
219 
220 				//System.out.println( name + "::release");
221 
222 				total_release++;
223 
224 				if ( waiting != 0 ){
225 
226 					waiting--;
227 
228 					notify();
229 
230 				}else{
231 					dont_wait++;
232 				}
233 			}
234 		}finally{
235 
236 			if ( DEBUG ){
237 
238 				debugExit();
239 			}
240 		}
241 	}
242 
243 	public void
releaseAllWaiters()244 	releaseAllWaiters()
245 	{
246 		synchronized(this){
247 
248 			int	x	= waiting;
249 
250 			for ( int i=0;i<x;i++ ){
251 				release();
252 			}
253 		}
254 	}
255 
256 	public void
releaseForever()257 	releaseForever()
258 	{
259 		synchronized(this){
260 
261 			releaseAllWaiters();
262 
263 			released_forever	= true;
264 		}
265 	}
266 
267 	public boolean
isReleasedForever()268 	isReleasedForever()
269 	{
270 		synchronized(this){
271 
272 			return( released_forever );
273 		}
274 	}
275 
276 	public int
getValue()277 	getValue()
278 	{
279 		synchronized(this){
280 
281 			return( dont_wait - waiting );
282 		}
283 	}
284 
285 	public String
getString()286 	getString()
287 	{
288 		synchronized(this){
289 
290 			return( "value=" + dont_wait + ",waiting=" + waiting + ",res=" + total_reserve + ",rel=" + total_release );
291 		}
292 	}
293 }
294