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