Improving the speed of Thread interrupt checking

Charles Oliver Nutter headius at headius.com
Fri May 10 14:24:47 PDT 2013


You need CAS because one form of the interrupt check clears it and another
does not. So the get + check + set of interrupt status needs to be atomic,
or another thread could jump in and change it during that process.

If it were just being read, then sure...it could simply be volatile. But
since there's a non-atomic operation in there, a race might be possible.

I just took a deeper look at the intrinsic, to see if it avoids the
lock...but unfortunately it does not. It adds fast paths for when the
thread is not interrupted *and* clearing is not requested (Thread.interrupt
clears, Thread#isInterrupted does not). So the typical use case of calling
Thread.interrupt() to get and clear interrupt status still follows the
slow, locking path all the time.

We are mitigating this in our code by using Thread#isInterrupted()
(th.isInterrupted on a Thread object) to do the frequent checks, and then
using Thread.interrupted to clear it only when it has been set. I think
this will be ok, but the slow path still seems like it could benefit from a
CAS impl instead of a lock.

- Charlie


On Fri, May 10, 2013 at 11:17 AM, Alexander Turner
<nerdscentral at gmail.com>wrote:

> Charles,
>
> Why bother even using CAS?
>
> Thread A is monitoring Thread B. Thread B cooperatively checks to see if
> it should die.
>
> Therefore, you only need B to know when A has told it to shut down.
>
> Therefore, all you need is a volatile boolean. A volatile boolean is very
> much faster than a full CAS operation.
> http://nerds-central.blogspot.co.uk/2011/11/atomicinteger-volatile-synchronized-and.html
>
> Best wishes - AJ
>
>
> On 10 May 2013 17:03, Charles Oliver Nutter <headius at headius.com> wrote:
>
>> This isn't strictly language-related, but I thought I'd post here before
>> I start pinging hotspot folks directly...
>>
>> We are looking at adding interrupt checking to our regex engine, Joni, so
>> that long-running (or never-terminating) expressions could be terminated
>> early. To do this we're using Thread.interrupt.
>>
>> Unfortunately our first experiments with it have shown that interrupt
>> checking is rather expensive; having it in the main instruction loop slowed
>> down a 16s benchmark to 68s. We're reducing that checking by only doing it
>> every N instructions now, but I figured I'd look into why it's so slow.
>>
>> Thread.isInterrupted does currentThread().interrupted(), both of which
>> are native calls. They end up as intrinsics and/or calling
>> JVM_CurrentThread and JVM_IsInterrupted. The former is not a
>> problem...accesses threadObj off the current thread (presumably from env)
>> and twiddles handle lifetime a bit. The latter, however, has to acquire a
>> lock to ensure retrieval and clearing are atomic.
>>
>> So then it occurred to me...why does it have to acquire a lock at all? It
>> seems like a get + CAS to clear would prevent accidentally clearing another
>> thread's re-interrupt. Some combination of CAS operations could avoid the
>> case where two threads both check interrupt status at the same time.
>>
>> I would expect the CAS version would have lower overhead than the hard
>> mutex acquisition.
>>
>> Does this seem reasonable?
>>
>> - Charlie
>>
>> _______________________________________________
>> mlvm-dev mailing list
>> mlvm-dev at openjdk.java.net
>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>
>>
>
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20130510/3bddf8ff/attachment.html 


More information about the mlvm-dev mailing list