Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

...

Compliant Solution (ThreadPoolExecutor Hooks)

Wiki MarkupTask-specific recovery or clean-up actions can be performed by overriding the {{afterExecute()}} hook of the {{java.util.concurrent.ThreadPoolExecutor}} class. This hook is called either when a task concludes successfully by executing all statements in its {{run()}} method or when the task halts because of an exception. Some implementations may fail to catch {{java.lang.Error}}. (See [Bug ID 6450211|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6450211] for more information \ [[SDN 2008|AA. References#SDN 08]\].) When using this approach, substitute the executor service with a custom {{ThreadPoolExecutor}} that overrides the {{afterExecute()}} hook:

Code Block
bgColor#ccccff
final class PoolService {
  // The values have been hard-coded for brevity
  ExecutorService pool = new CustomThreadPoolExecutor(
      10, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
  // ...
}

class CustomThreadPoolExecutor extends ThreadPoolExecutor {
  // ... Constructor ...
  public CustomThreadPoolExecutor(
      int corePoolSize, int maximumPoolSize, long keepAliveTime, 
      TimeUnit unit, BlockingQueue<Runnable> workQueue) { 
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
  }


  @Override
  public void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    if (t != null) {
      // Exception occurred, forward to handler
    }
    // ... Perform task-specific clean-up actions
  }

  @Override
  public void terminated() {
    super.terminated();
    // ... Perform final clean-up actions
  }
}

...

Code Block
bgColor#ccccff
final class PoolService {
  private static final ThreadFactory factory =
      new ExceptionThreadFactory(new MyExceptionHandler());
  private static final ExecutorService pool =
      Executors.newFixedThreadPool(10, factory);

  public void doSomething() {
    pool.execute(new Task()); // Task is a runnable class
  }

  public static class ExceptionThreadFactory implements ThreadFactory  {
    private static final ThreadFactory defaultFactory =
        Executors.defaultThreadFactory();
    private final Thread.UncaughtExceptionHandler handler;

    public ExceptionThreadFactory(
        Thread.UncaughtExceptionHandler handler)
    {
      this.handler = handler;
    }

    @Override public Thread newThread(Runnable run) {
      Thread thread = defaultFactory.newThread(run);
      thread.setUncaughtExceptionHandler(handler);
      return thread;
    }
  }

  public static class MyExceptionHandler extends ExceptionReporter
      implements Thread.UncaughtExceptionHandler {
    // ...

    @Override public void uncaughtException(Thread thread, Throwable t) {
      // Recovery or logging code
    }
  }
}

...

The {{ExecutorService.submit()}} method can be used (in place of the {{execute()}} method) to submit a task to a thread pool and obtain a {{Future}} object. When the task is submitted via {{ExecutorService.submit()}}, thrown exceptions never reach the uncaught exception handler because the thrown exception is considered to be part of the return status and is consequently wrapped in an {{ExecutionException}} and rethrown by {{Future.get()}} \[ [Goetz 2006a|AA. References#Goetz 06]\].

Compliant Solution (Future<V> and submit())

...

MITRE CWE

CWE-392. Missing report of error condition

Bibliography

...

[[API 2006AA. References#API 06] ]

Interfaces ExecutorService, ThreadFactory; class Thread

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="efeb5522-f498-4284-9bcb-0f8a3a8c461a"><ac:plain-text-body><![CDATA[

[[Goetz 2006aAA. References#Goetz 06] ]

Chapter 7.3, Handling Abnormal Thread Termination ]]></ac:plain-text-body></ac:structured-macro>

...

      10. Thread Pools (TPS)