[concurrency-interest] ForkJoin refresh

Peter Levart peter.levart at gmail.com
Thu Feb 6 10:56:41 EST 2020

On 2/6/20 1:31 PM, Doug Lea wrote:
> On 2/5/20 10:15 AM, Peter Levart wrote:
>> Would something like the following be more "precise" in targeting the
>> task and only the task and not any other code that executes after the
>> task in the same thread? At least stray interrupts would not be possible
>> as a consequence of cancel(true) for some unrelated task. Should anyone
>> interrupt the pool thread outside of normal task execution, the
>> interrupted status would not propagate to task code nor would it be lost
>> after the task has finished.
> Thanks for taking a look at this. I'm not sure your suggested changes
> are preferable though. Interrupt status for interruptible tasks is
> cleared at end, before running next task, which reduces the impact of
> other slow threads needlessly interrupting the task.
It may reduce the impact, but it can't guarantee that it won't happen. 
There is a theoretical race in cancel(true):

1455                 if (mayInterruptIfRunning && (t = runner) != null) {
1456                     try {
1457                         t.interrupt();
1458                     } catch (Throwable ignore) {
1459                     }
1460                 }

...between the read of the 'runner' field and the call to interrupt() 
the 't' thread may already transition to running another unrelated task 
which will get a stray interrupt then...

If stray interrupts are not problematic, then what about the race that 
causes a missing interrupt at the start of execution of the task:

1436             public final boolean exec() {
1437                 Thread.interrupted();
1438                 runner = Thread.currentThread();
1439                 try {
1440                     result = callable.call();
1441                     return true;

The task might already be executing (just entering exec method), but has 
not yet assigned the 'runner' field. A concurrent thread with access to 
the task may call cancel(true) which will not interrupt the task, 
because it observes the runner still being null. So the task will 
execute user code to the end although user code might have checks to 
detect interrupts. In my 2nd message I proposed to add a check after 
assigning the runner field:

             runner = Thread.currentThread();
             try {
                 // check for concurrent cancel() that just missed the 
runner field
                 if (isCancelled()) {
                     throw new CancellationException();
                 } else {
                     result = callable.call();
                 return true;

The cancelation and the read of 'runner' field in cancel() are reversed, 
so this should guarantee that either interrupt is observable by user 
code or the task ends execution before it enters user code (Callable):

1452             public final boolean cancel(boolean 
mayInterruptIfRunning) {
1453                 Thread t;
1454                 boolean stat = super.cancel(false);
1455                 if (mayInterruptIfRunning && (t = runner) != null) {
1456                     try {
1457                         t.interrupt();

Regards, Peter

>   I can't think of
> cases where it helps to conditionalize this on whether any occurred?
> To better explain though, I'll add to internal documentation that the
> only occurrence of Thread.interrupt inside FJ code itself is during
> termination, in which case tasks are or will be cancelled anyway.
> -Doug

More information about the Concurrency-interest mailing list