Description
I've read a code in {lib,spec}/concurrent/executor
a bit. It looks like people loves java way: the abstraction layer is ideal, documentation is great, tests are good and implementation is poor.
Here we have the implementation:
def run_task(pool, task, args)
task.call(*args)
pool.worker_task_completed
rescue => ex
# let it fail
log DEBUG, ex
rescue Exception => ex
log ERROR, ex
pool.worker_died(self)
throw :stop
end
We shouldn't catch Exception
. We should work only with StandardError
. System error should be able to kill this worker and all pool infrastructure.
For example: pool shouldn't be able to recreate workers if malloc failed in any worker, user should be able to interrupt ruby program with SIGINT and pool shouldn't stop him.
We could ignore user's StandardError
from task.call(*args)
, but we couldn't ignore any errors from pool.worker_task_completed
. pool.worker_task_completed
should left this trap.
I saw issue #616:
You'll notice that our API is exactly the same (in fact, on JRuby) our TPE is just a thin wrapper around Java's). The reason that neither Java's thread pools nor ours have exception handling within the thread pool is because that's not the appropriate place to put it.
@jdantonio, This planet are not rotating around java. The old java spec is not a holy thing, that couldn't be changed.
If you absolutely insist on posting jobs directly to a thread pool rather than using our high-level abstractions (which I strongly discourage) then just create a job wrapper. You can find examples of job wrappers in all our high-level abstractions, Rails ActiveJob, Sucker Punch, and other libraries which use our thread pools.
All of the high-level abstractions in this library (Promise, Actor, etc.) all post jobs to the global thread pool and all provide exception handling. Simply pick the abstraction that best fits your use case and use it.
People wanted to modify the default behaviour of thread pool. They didn't want to use your special high level job wrappers.
Ruby Thread
had the same issue, but people just fixed it without panic 12-15 years ago.
Here we can see Thread.abort_on_exception. It is false
by default and parent's thread will ignore any child's error. If it is true
child's errors will be re-raised in parent thread.
User is still able to create it's own job wrappers.
Some people (me too) loves this option because it makes api cleaner and code become more readable.
We can add :ABORT_ON_EXCEPTION => true/false
option into thread pool and this issue will be fixed properly.