1 /*
2  * Created on Nov 9, 2007
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 
21 package org.gudy.azureus2.core3.util;
22 
23 import java.util.LinkedList;
24 
25 
26 public abstract class
27 AEThread2
28 {
29 	public static final boolean TRACE_TIMES = false;
30 
31 	private static final int MIN_RETAINED	= Math.max(Runtime.getRuntime().availableProcessors(),2);
32 	private static final int MAX_RETAINED	= Math.max(MIN_RETAINED*4, 16);
33 
34 	private static final int THREAD_TIMEOUT_CHECK_PERIOD	= 10*1000;
35 	private static final int THREAD_TIMEOUT					= 60*1000;
36 
37 	private static final LinkedList	daemon_threads = new LinkedList();
38 
39 	private static final class JoinLock {
40 		volatile boolean released = false;
41 	}
42 
43 	private static long	last_timeout_check;
44 
45 	private static long	total_starts;
46 	private static long	total_creates;
47 
48 
49 	private threadWrapper	wrapper;
50 
51 	private String				name;
52 	private boolean				daemon;
53 	private int					priority	= Thread.NORM_PRIORITY;
54 	private volatile JoinLock	lock		= new JoinLock();
55 
56 	public
AEThread2( String _name )57 	AEThread2(
58 		String		_name )
59 	{
60 		this( _name, true );
61 	}
62 
63 	public
AEThread2( String _name, boolean _daemon )64 	AEThread2(
65 		String		_name,
66 		boolean		_daemon )
67 	{
68 		name		= _name;
69 		daemon		= _daemon;
70 	}
71 
72 	/**
73 	 * multiple invocations of start() are possible, but discouraged if combined
74 	 * with other thread operations such as interrupt() or join()
75 	 */
76 	public void
start()77 	start()
78 	{
79 		JoinLock currentLock = lock;
80 		JoinLock newLock;
81 
82 		synchronized (currentLock)
83 		{
84 			// create new lock in case this is a restart, all old .join()s will be locked on the old thread and thus released by the old thread
85 			if(currentLock.released)
86 				newLock = lock = new JoinLock();
87 			else
88 				newLock = currentLock;
89 		}
90 
91 		if ( daemon ){
92 
93 			synchronized( daemon_threads ){
94 
95 				total_starts++;
96 
97 				if ( daemon_threads.isEmpty()){
98 
99 					total_creates++;
100 
101 					wrapper = new threadWrapper( name, true );
102 
103 				}else{
104 
105 					wrapper = (threadWrapper)daemon_threads.removeLast();
106 
107 					wrapper.setName( name );
108 				}
109 			}
110 		}else{
111 
112 			wrapper = new threadWrapper( name, false );
113 		}
114 
115 		if ( priority != wrapper.getPriority() ){
116 
117 			wrapper.setPriority( priority );
118 		}
119 
120 		wrapper.currentLock = newLock;
121 
122 		wrapper.start( this, name );
123 	}
124 
125 	public void
setPriority( int _priority )126 	setPriority(
127 		int		_priority )
128 	{
129 		priority	= _priority;
130 
131 		if ( wrapper != null ){
132 			wrapper.setPriority( priority );
133 		}
134 	}
135 
136 	public void
setName( String s )137 	setName(
138 		String	s )
139 	{
140 		name	= s;
141 
142 		if ( wrapper != null ){
143 
144 			wrapper.setName( name );
145 		}
146 	}
147 
148 	public String
getName()149 	getName()
150 	{
151 		return( name );
152 	}
153 
154 	public void
interrupt()155 	interrupt()
156 	{
157 		if ( wrapper == null ){
158 
159 			throw new IllegalStateException( "Interrupted before started!" );
160 
161 		}else{
162 
163 			wrapper.interrupt();
164 		}
165 	}
166 
167 	public boolean
isAlive()168 	isAlive() {
169 		return wrapper == null ? false : wrapper.isAlive();
170 	}
171 
172 	public boolean
isCurrentThread()173 	isCurrentThread()
174 	{
175 		return( wrapper == Thread.currentThread());
176 	}
177 
178 	public String
toString()179 	toString()
180 	{
181 		if ( wrapper == null ){
182 
183 			return( name + " [daemon=" + daemon + ",priority=" + priority + "]" );
184 
185 		}else{
186 
187 			return( wrapper.toString());
188 		}
189 	}
190 
191 	public abstract void
run()192 	run();
193 
194 	public static boolean
isOurThread( Thread thread )195 	isOurThread(
196 		Thread	thread )
197 	{
198 		return( AEThread.isOurThread( thread ));
199 	}
200 
201 	public static void
setOurThread()202 	setOurThread()
203 	{
204 		AEThread.setOurThread();
205 	}
206 
207 	public static void
setOurThread( Thread thread )208 	setOurThread(
209 		Thread	thread )
210 	{
211 		AEThread.setOurThread( thread );
212 	}
213 
214 	public static void
setDebug( Object debug )215 	setDebug(
216 		Object		debug )
217 	{
218 		Thread current = Thread.currentThread();
219 
220 		if ( current instanceof threadWrapper ){
221 
222 			((threadWrapper)current).setDebug( debug );
223 		}
224 	}
225 
226 		/**
227 		 * entry 0 is debug object, 1 is Long mono-time it was set
228 		 * @param t
229 		 * @return
230 		 */
231 
232 	public static Object[]
getDebug( Thread t )233 	getDebug(
234 		Thread		t )
235 	{
236 		if ( t instanceof threadWrapper ){
237 
238 			return(((threadWrapper)t).getDebug());
239 		}
240 
241 		return( null );
242 	}
243 
244 	protected static class
245 	threadWrapper
246 		extends Thread
247 	{
248 		private AESemaphore2	sem;
249 		private AEThread2		target;
250 		private JoinLock		currentLock;
251 
252 		private long		last_active_time;
253 
254 		private Object[]		debug;
255 
256 		protected
threadWrapper( String name, boolean daemon )257 		threadWrapper(
258 			String		name,
259 			boolean		daemon )
260 		{
261 			super( name );
262 
263 			setDaemon( daemon );
264 		}
265 
266 		public void
run()267 		run()
268 		{
269 			while( true ){
270 
271 				synchronized( currentLock ){
272 					try{
273 						if ( TRACE_TIMES ){
274 
275 							long 	start_time 	= SystemTime.getHighPrecisionCounter();
276 							long	start_cpu 	= AEJavaManagement.getThreadCPUTime();
277 
278 							try{
279 
280 								target.run();
281 
282 							}finally{
283 
284 								long	time_diff 	= ( SystemTime.getHighPrecisionCounter() - start_time )/1000000;
285 								long	cpu_diff	= ( AEJavaManagement.getThreadCPUTime() - start_cpu ) / 1000000;
286 
287 								if ( cpu_diff > 10 || time_diff > 10 ){
288 
289 									System.out.println( TimeFormatter.milliStamp() + ": Thread: " + target.getName() + ": " + cpu_diff + "/" + time_diff );
290 								}
291 							}
292 						}else{
293 
294 							target.run();
295 						}
296 
297 					}catch( Throwable e ){
298 
299 						DebugLight.printStackTrace(e);
300 
301 					}finally{
302 
303 						target = null;
304 
305 						debug	= null;
306 
307 						currentLock.released = true;
308 
309 						currentLock.notifyAll();
310 					}
311 				}
312 
313 				if ( isInterrupted() || !Thread.currentThread().isDaemon()){
314 
315 					break;
316 
317 				}else{
318 
319 					synchronized( daemon_threads ){
320 
321 						last_active_time	= SystemTime.getCurrentTime();
322 
323 						if ( 	last_active_time < last_timeout_check ||
324 								last_active_time - last_timeout_check > THREAD_TIMEOUT_CHECK_PERIOD ){
325 
326 							last_timeout_check	= last_active_time;
327 
328 							while( daemon_threads.size() > 0 && daemon_threads.size() > MIN_RETAINED ){
329 
330 								threadWrapper thread = (threadWrapper)daemon_threads.getFirst();
331 
332 								long	thread_time = thread.last_active_time;
333 
334 								if ( 	last_active_time < thread_time ||
335 										last_active_time - thread_time > THREAD_TIMEOUT ){
336 
337 									daemon_threads.removeFirst();
338 
339 									thread.retire();
340 
341 								}else{
342 
343 									break;
344 								}
345 							}
346 						}
347 
348 						if ( daemon_threads.size() >= MAX_RETAINED ){
349 
350 							return;
351 						}
352 
353 						daemon_threads.addLast( this );
354 
355 						setName( "AEThread2:parked[" + daemon_threads.size() + "]" );
356 
357 						// System.out.println( "AEThread2: queue=" + daemon_threads.size() + ",creates=" + total_creates + ",starts=" + total_starts );
358 					}
359 
360 					sem.reserve();
361 
362 					if ( target == null ){
363 
364 						break;
365 					}
366 				}
367 			}
368 		}
369 
370 		protected void
start( AEThread2 _target, String _name )371 		start(
372 			AEThread2	_target,
373 			String		_name )
374 		{
375 			target	= _target;
376 
377 			setName( _name );
378 
379 			if ( sem == null ){
380 
381 				 sem = new AESemaphore2( "AEThread2" );
382 
383 				 super.start();
384 
385 			}else{
386 
387 				sem.release();
388 			}
389 		}
390 
391 		protected void
retire()392 		retire()
393 		{
394 			sem.release();
395 		}
396 
397 		protected void
setDebug( Object d )398 		setDebug(
399 			Object	d )
400 		{
401 			debug	= new Object[]{ d, SystemTime.getMonotonousTime() };
402 		}
403 
404 		protected Object[]
getDebug()405 		getDebug()
406 		{
407 			return( debug );
408 		}
409 	}
410 
411 	public void
join()412 	join()
413 	{
414 		JoinLock currentLock = lock;
415 
416 			// sync lock will be blocked by the thread
417 
418 		synchronized( currentLock ){
419 
420 				// wait in case the thread is not running yet
421 
422 			while (!currentLock.released ){
423 
424 				try{
425 					currentLock.wait();
426 
427 				}catch( InterruptedException e ){
428 				}
429 			}
430 		}
431 	}
432 }
433