001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.system.thread; 020 021import icy.system.IcyExceptionHandler; 022import icy.system.SystemUtil; 023 024import java.awt.EventQueue; 025import java.lang.reflect.InvocationTargetException; 026import java.util.concurrent.Callable; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.ExecutorService; 029import java.util.concurrent.Future; 030import java.util.concurrent.FutureTask; 031import java.util.concurrent.TimeUnit; 032 033import javax.swing.SwingUtilities; 034 035/** 036 * Thread utilities class. 037 * 038 * @author Stephane 039 */ 040public class ThreadUtil 041{ 042 /** 043 * This class is used to catch exception in the EDT. 044 */ 045 public static class CaughtRunnable implements Runnable 046 { 047 private final Runnable runnable; 048 049 public CaughtRunnable(Runnable runnable) 050 { 051 super(); 052 053 this.runnable = runnable; 054 } 055 056 @Override 057 public void run() 058 { 059 try 060 { 061 runnable.run(); 062 } 063 catch (Throwable t) 064 { 065 IcyExceptionHandler.handleException(t, true); 066 } 067 } 068 } 069 070 /** 071 * The minimum priority that a thread can have. 072 */ 073 public final static int MIN_PRIORITY = Thread.MIN_PRIORITY; 074 075 /** 076 * The default priority that is assigned to a thread. 077 */ 078 public final static int NORM_PRIORITY = Thread.NORM_PRIORITY; 079 080 /** 081 * The maximum priority that a thread can have. 082 */ 083 public final static int MAX_PRIORITY = Thread.MAX_PRIORITY; 084 085 // low priority background processor 086 private static final Processor bgProcessor; 087 // single Runnable / Callable instance processor 088 private static final InstanceProcessor instanceProcessors[]; 089 // low priority single Runnable / Callable instance processor 090 private static final InstanceProcessor bgInstanceProcessors[]; 091 092 static 093 { 094 if (SystemUtil.is32bits()) 095 { 096 int wantedThread = SystemUtil.getNumberOfCPUs(); 097 wantedThread = Math.max(wantedThread, 2); 098 099 // 32 bits JVM, limit the number of thread 100 bgProcessor = new Processor(Math.min(wantedThread, 8)); 101 instanceProcessors = new InstanceProcessor[Math.min(wantedThread, 4)]; 102 bgInstanceProcessors = new InstanceProcessor[Math.min(wantedThread, 4)]; 103 } 104 else 105 { 106 int wantedThread = SystemUtil.getNumberOfCPUs(); 107 wantedThread = Math.max(wantedThread, 4); 108 109 // 64 bits JVM, can have higher limit 110 bgProcessor = new Processor(Math.min(wantedThread, 16)); 111 instanceProcessors = new InstanceProcessor[Math.min(wantedThread, 8)]; 112 bgInstanceProcessors = new InstanceProcessor[Math.min(wantedThread, 8)]; 113 } 114 115 bgProcessor.setPriority(MIN_PRIORITY); 116 bgProcessor.setThreadName("Background processor"); 117 bgProcessor.setKeepAliveTime(3, TimeUnit.SECONDS); 118 119 for (int i = 0; i < instanceProcessors.length; i++) 120 { 121 // keep these thread active 122 instanceProcessors[i] = new InstanceProcessor(NORM_PRIORITY); 123 instanceProcessors[i].setThreadName("Background instance processor"); 124 instanceProcessors[i].setKeepAliveTime(3, TimeUnit.SECONDS); 125 bgInstanceProcessors[i] = new InstanceProcessor(MIN_PRIORITY); 126 bgInstanceProcessors[i].setThreadName("Background instance processor (low priority)"); 127 bgInstanceProcessors[i].setKeepAliveTime(3, TimeUnit.SECONDS); 128 } 129 } 130 131 /** 132 * Shutdown all background runner. 133 */ 134 public static void shutdown() 135 { 136 bgProcessor.shutdown(); 137 for (int i = 0; i < instanceProcessors.length; i++) 138 { 139 instanceProcessors[i].shutdown(); 140 bgInstanceProcessors[i].shutdown(); 141 } 142 } 143 144 /** 145 * Return true if all background runner are shutdown and terminated. 146 */ 147 public static boolean isShutdownAndTerminated() 148 { 149 for (int i = 0; i < instanceProcessors.length; i++) 150 { 151 if (!instanceProcessors[i].isTerminated()) 152 return false; 153 if (!bgInstanceProcessors[i].isTerminated()) 154 return false; 155 } 156 return bgProcessor.isTerminated(); 157 } 158 159 /** 160 * @return true if the current thread is an AWT event dispatching thread. 161 */ 162 public static boolean isEventDispatchThread() 163 { 164 return EventQueue.isDispatchThread(); 165 } 166 167 /** 168 * Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br> 169 * Any exception is automatically caught by Icy exception handler. 170 * 171 * @param wait 172 * If set to true, the method wait until completion, in this case you have to take 173 * attention to not cause any dead lock. 174 * @see #invokeLater(Runnable) 175 * @see #invokeNow(Runnable) 176 */ 177 public static void invoke(Runnable runnable, boolean wait) 178 { 179 if (wait) 180 invokeNow(runnable); 181 else 182 invokeLater(runnable); 183 } 184 185 /** 186 * @deprecated Use {@link #invokeNow(Runnable)} instead 187 */ 188 @Deprecated 189 public static void invokeAndWait(Runnable runnable) 190 { 191 invokeNow(runnable); 192 } 193 194 /** 195 * Invoke the specified <code>Runnable</code> on the AWT event dispatching thread now and wait 196 * until completion.<br> 197 * Any exception is automatically caught by Icy exception handler, if you want to catch them use 198 * {@link #invokeNow(Callable)} instead.<br> 199 * Use this method carefully as it may lead to dead lock. 200 */ 201 public static void invokeNow(Runnable runnable) 202 { 203 if (isEventDispatchThread()) 204 { 205 try 206 { 207 runnable.run(); 208 } 209 catch (Throwable t) 210 { 211 // the runnable thrown an exception 212 IcyExceptionHandler.handleException(t, true); 213 } 214 } 215 else 216 { 217 try 218 { 219 EventQueue.invokeAndWait(runnable); 220 } 221 catch (InvocationTargetException e) 222 { 223 // the runnable thrown an exception 224 IcyExceptionHandler.handleException(e.getTargetException(), true); 225 } 226 catch (InterruptedException e) 227 { 228 // interrupt exception 229 System.err.println("ThreadUtil.invokeNow(...) error :"); 230 IcyExceptionHandler.showErrorMessage(e, true); 231 } 232 } 233 } 234 235 /** 236 * Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br> 237 * If we already are on the EDT the <code>Runnable</code> is executed immediately else it will 238 * be executed later. 239 * 240 * @see #invokeLater(Runnable, boolean) 241 */ 242 public static void invokeLater(Runnable runnable) 243 { 244 invokeLater(runnable, false); 245 } 246 247 /** 248 * Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br> 249 * Depending the <code>forceLater</code> parameter the <code>Runnable</code> can be executed 250 * immediately if we are on the EDT. 251 * 252 * @param forceLater 253 * If <code>true</code> the <code>Runnable</code> is forced to execute later even if we 254 * are on the Swing EDT. 255 */ 256 public static void invokeLater(Runnable runnable, boolean forceLater) 257 { 258 final Runnable r = new CaughtRunnable(runnable); 259 260 if ((!forceLater) && isEventDispatchThread()) 261 r.run(); 262 else 263 EventQueue.invokeLater(r); 264 } 265 266 /** 267 * Invoke the specified <code>Callable</code> on the AWT event dispatching thread now and return 268 * the result.<br> 269 * The returned result can be <code>null</code> when a {@link Throwable} exception happen.<br> 270 * Use this method carefully as it may lead to dead lock. 271 * 272 * @throws InterruptedException 273 * if the current thread was interrupted while waiting 274 * @throws Exception 275 * if the computation threw an exception 276 */ 277 public static <T> T invokeNow(Callable<T> callable) throws Exception 278 { 279 if (SwingUtilities.isEventDispatchThread()) 280 return callable.call(); 281 282 final FutureTask<T> task = new FutureTask<T>(callable); 283 284 try 285 { 286 EventQueue.invokeAndWait(task); 287 } 288 catch (InvocationTargetException e) 289 { 290 if (e.getTargetException() instanceof Exception) 291 throw (Exception) e.getTargetException(); 292 293 throw new Exception(e.getTargetException()); 294 } 295 296 try 297 { 298 return task.get(); 299 } 300 catch (ExecutionException e) 301 { 302 if (e.getCause() instanceof Exception) 303 throw (Exception) e.getCause(); 304 305 throw new Exception(e.getCause()); 306 } 307 } 308 309 /** 310 * Invoke the specified {@link Callable} on the AWT event dispatching thread.<br> 311 * Depending the <code>forceLater</code> parameter the <code>Callable</code> can be executed 312 * immediately if we are on the EDT. 313 * 314 * @param forceLater 315 * If <code>true</code> the <code>Callable</code> is forced to execute later even if we 316 * are on the EDT. 317 */ 318 public static <T> Future<T> invokeLater(Callable<T> callable, boolean forceLater) 319 { 320 final FutureTask<T> task = new FutureTask<T>(callable); 321 invokeLater(task, forceLater); 322 return task; 323 } 324 325 /** 326 * Retrieve the instance processor (normal priority) to use for specified runnable. 327 */ 328 private static InstanceProcessor getInstanceProcessor(Runnable runnable) 329 { 330 // get processor index from the hash code 331 return instanceProcessors[runnable.hashCode() % instanceProcessors.length]; 332 } 333 334 /** 335 * Retrieve the instance processor (normal priority) to use for specified callable. 336 */ 337 private static InstanceProcessor getInstanceProcessor(Callable<?> callable) 338 { 339 // get processor index from the hash code 340 return instanceProcessors[callable.hashCode() % instanceProcessors.length]; 341 } 342 343 /** 344 * Retrieve the instance processor (low priority) to use for specified runnable. 345 */ 346 private static InstanceProcessor getBgInstanceProcessor(Runnable runnable) 347 { 348 // get processor index from the hash code 349 return bgInstanceProcessors[runnable.hashCode() % bgInstanceProcessors.length]; 350 } 351 352 /** 353 * Retrieve the instance processor (low priority) to use for specified callable. 354 */ 355 private static InstanceProcessor getBgInstanceProcessor(Callable<?> callable) 356 { 357 // get processor index from the hash code 358 return bgInstanceProcessors[callable.hashCode() % bgInstanceProcessors.length]; 359 } 360 361 /** 362 * @deprecated Use {@link #bgRun(Runnable)} instead and {@link #invokeNow(Runnable)} separately. 363 * @see #bgRun(Runnable) 364 */ 365 @Deprecated 366 public static boolean bgRun(Runnable runnable, boolean onEDT) 367 { 368 return (bgProcessor.submit(runnable, onEDT) != null); 369 } 370 371 /** 372 * @deprecated Use {@link #bgRun(Runnable)} instead and check for acceptance. 373 */ 374 @Deprecated 375 public static void bgRunWait(Runnable runnable) 376 { 377 while (!bgRun(runnable)) 378 ThreadUtil.sleep(1); 379 } 380 381 /** 382 * Adds background processing (low priority) of specified Runnable.<br> 383 * Returns <code>false</code> if background process queue is full.<br> 384 * Don't use this method for long process (more than 1 second) as the number of thread is 385 * limited and others processes may be executed too late. 386 */ 387 public static boolean bgRun(Runnable runnable) 388 { 389 return (bgProcessor.submit(true, runnable) != null); 390 } 391 392 /** 393 * @deprecated Use {@link #bgRun(Callable)} and {@link #invokeNow(Callable)} separately instead. 394 * @see #bgRun(Callable) 395 */ 396 @Deprecated 397 public static <T> Future<T> bgRun(Callable<T> callable, boolean onEDT) 398 { 399 return bgProcessor.submit(callable, onEDT); 400 } 401 402 /** 403 * Adds background processing (low priority) of specified Callable task.<br> 404 * Returns a Future representing the pending result of the task or <code>null</code> if 405 * background process queue is full.<br> 406 * Don't use this method for long process (more than 1 second) as the number of thread is 407 * limited and others processes may be executed too late. 408 */ 409 public static <T> Future<T> bgRun(Callable<T> callable) 410 { 411 return bgProcessor.submit(callable); 412 } 413 414 /** 415 * @deprecated Use {@link #runSingle(Runnable)} instead and {@link #invokeNow(Runnable)} separately. 416 * @see #bgRunSingle(Runnable) 417 */ 418 @Deprecated 419 public static boolean bgRunSingle(Runnable runnable, boolean onEDT) 420 { 421 final InstanceProcessor processor = getInstanceProcessor(runnable); 422 return (processor.submit(runnable, onEDT) != null); 423 } 424 425 /** 426 * @deprecated Use {@link #runSingle(Callable)} and {@link #invokeNow(Callable)} separately 427 * instead. 428 * @see #bgRunSingle(Callable) 429 */ 430 @Deprecated 431 public static <T> Future<T> bgRunSingle(Callable<T> callable, boolean onEDT) 432 { 433 final InstanceProcessor processor = getInstanceProcessor(callable); 434 return processor.submit(callable, onEDT); 435 } 436 437 /** 438 * Adds single processing (low priority) of specified Runnable.<br> 439 * If this <code>Runnable</code> instance is already pending in single processes queue then 440 * nothing is done.<br> 441 * Returns <code>false</code> if single processes queue is full.<br> 442 * Don't use this method for long process (more than 1 second) as the number of thread is 443 * limited and others processes may be executed too late. 444 */ 445 public static boolean bgRunSingle(Runnable runnable) 446 { 447 final InstanceProcessor processor = getBgInstanceProcessor(runnable); 448 return (processor.submit(true, runnable) != null); 449 } 450 451 /** 452 * Adds single processing (low priority) of specified Callable task.<br> 453 * If this <code>Callable</code> instance is already pending in single processes queue then 454 * nothing is done.<br> 455 * Returns a Future representing the pending result of the task or <code>null</code> if 456 * single processes queue is full.<br> 457 * Don't use this method for long process (more than 1 second) as the number of thread is 458 * limited and others processes may be executed too late. 459 */ 460 public static <T> Future<T> bgRunSingle(Callable<T> callable) 461 { 462 final InstanceProcessor processor = getBgInstanceProcessor(callable); 463 return processor.submit(callable); 464 } 465 466 /** 467 * Add single processing (normal priority) of specified Runnable.<br> 468 * If this <code>Runnable</code> instance is already pending in single processes queue then 469 * nothing is done.<br> 470 * Return <code>false</code> if single processes queue is full.<br> 471 * Don't use this method for long process (more than 1 second) as the number of thread is 472 * limited and others processes may be executed too late. 473 */ 474 public static boolean runSingle(Runnable runnable) 475 { 476 final InstanceProcessor processor = getInstanceProcessor(runnable); 477 return (processor.submit(true, runnable) != null); 478 } 479 480 /** 481 * Add single processing (normal priority) of specified Callable task.<br> 482 * If this <code>Callable</code> instance is already pending in single processes queue then 483 * nothing is done.<br> 484 * Return a Future representing the pending result of the task or <code>null</code> if 485 * single processes queue is full.<br> 486 * Don't use this method for long process (more than 1 second) as the number of thread is 487 * limited and others processes may be executed too late. 488 */ 489 public static <T> Future<T> runSingle(Callable<T> callable) 490 { 491 final InstanceProcessor processor = getInstanceProcessor(callable); 492 return processor.submit(callable); 493 } 494 495 /** 496 * Return true if the specified runnable is waiting to be processed in background processing. 497 */ 498 public static boolean hasWaitingBgTask(Runnable runnable) 499 { 500 return bgProcessor.getWaitingTasksCount(runnable) > 0; 501 } 502 503 /** 504 * Return true if the specified callable is waiting to be processed in background processing. 505 */ 506 public static boolean hasWaitingBgTask(Callable<?> callable) 507 { 508 return bgProcessor.getWaitingTasksCount(callable) > 0; 509 } 510 511 /** 512 * Return true if the specified runnable is waiting to be processed<br> 513 * in single scheme background processing (low priority). 514 */ 515 public static boolean hasWaitingBgSingleTask(Runnable runnable) 516 { 517 final InstanceProcessor processor = getBgInstanceProcessor(runnable); 518 return processor.hasWaitingTasks(runnable); 519 } 520 521 /** 522 * Return true if the specified callable is waiting to be processed<br> 523 * in single scheme background processing (low priority). 524 */ 525 public static boolean hasWaitingBgSingleTask(Callable<?> callable) 526 { 527 final InstanceProcessor processor = getBgInstanceProcessor(callable); 528 return processor.hasWaitingTasks(callable); 529 } 530 531 /** 532 * Return true if the specified runnable is waiting to be processed<br> 533 * in single scheme background processing (normal priority). 534 */ 535 public static boolean hasWaitingSingleTask(Runnable runnable) 536 { 537 final InstanceProcessor processor = getInstanceProcessor(runnable); 538 return processor.hasWaitingTasks(runnable); 539 } 540 541 /** 542 * Return true if the specified callable is waiting to be processed<br> 543 * in single scheme background processing (normal priority). 544 */ 545 public static boolean hasWaitingSingleTask(Callable<?> callable) 546 { 547 final InstanceProcessor processor = getInstanceProcessor(callable); 548 return processor.hasWaitingTasks(callable); 549 } 550 551 /** 552 * Return the number of active background tasks. 553 */ 554 public static int getActiveBgTaskCount() 555 { 556 return bgProcessor.getActiveCount(); 557 } 558 559 /** 560 * Create a thread pool with the given name.<br> 561 * The number of processing thread is automatically calculated given the number of core of the 562 * system. 563 * 564 * @see Processor#Processor(int, int, int) 565 */ 566 public static ExecutorService createThreadPool(String name) 567 { 568 final Processor result = new Processor(SystemUtil.getNumberOfCPUs()); 569 570 result.setThreadName(name); 571 572 return result; 573 } 574 575 /** 576 * Same as {@link Thread#sleep(long)} except Exception is caught and ignored. 577 */ 578 public static void sleep(long milli) 579 { 580 try 581 { 582 Thread.sleep(milli); 583 } 584 catch (InterruptedException e) 585 { 586 // have to interrupt the thread 587 Thread.currentThread().interrupt(); 588 } 589 } 590 591 /** 592 * Same as {@link Thread#sleep(long)} except Exception is caught and ignored. 593 */ 594 public static void sleep(int milli) 595 { 596 try 597 { 598 Thread.sleep(milli); 599 } 600 catch (InterruptedException e) 601 { 602 // have to interrupt the thread 603 Thread.currentThread().interrupt(); 604 } 605 } 606}