RFR: 8189596: AArch64: implementation for Thread-local handshakes

Erik Österlund erik.osterlund at oracle.com
Fri Nov 24 10:36:59 UTC 2017


Hi Andrew,

I am glad to see you also support the move to use Thread-local 
handshakes. Thank you for that.
I think your approach looks good. However, there is a certain race that 
I believe you are currently vulnerable to that could cause trouble for 
you. So I thought I'd point it out.

Specifically, when transitioning from native back to Java, there is a 
peculiar race condition on your platform unless more synchronization is 
applied. I would recommend loading the thread-local poll value when 
waking up from native with ldar instead to avoid subsequent checks for 
is_at_safepoint() on the thread waking up from native to provide the 
wrong result, which could cause MutexLockerEx that is conditionally 
taken only if we are not in a safepoint to hit the wrong path and not be 
taken as it still thinks you are in a safepoint.

The race is due to an interaction between the wake-up path of the native 
wrapper and the safepoint unsynchronizing path.

When SafepointSynchronize::end() starts, all JavaThreads except the ones 
in native and in blocked are blocked. Specifically there exists a race 
with the native wrapper trying to wake up from native while the 
SafepointSynchronizer is waking up from a safepoint.

The SafepointSynchronize::end() will conceptually perform the following 
important steps:

VM.1) SafepointSynchronize::_state = _not_synchronized
VM.2) For each thread: disarm local poll

Your native wrapper wakeup path filters slow paths on the local poll 
(without acquire() or loadload()), but no longer the global _state value.

So a JavaThread waking up from native could do:

JT.1) load local poll (with plain ldr), check if it is armed (it is not, 
because it observes the store from VM.1)
JT.2) release_store the thread state to Java with stlr
JT.3) enter arbitrary Java code
JT.4) make leaf call to the runtime from Java without thread transition
JT.5) Check if in a safepoint by loading SafepointSynchronize::_state

At JT.5, it is not guaranteed that the load observes the updated 
_not_synchronized value written in VM.1 (because the load in JT.1 and 
JT.5 may reorder). In this scenario the check to see whether you are in 
a safepoint might if very unlucky return true spuriously. From then on, 
bad things can happen.

By placing loading the local poll value with ldar *only* in the native 
wrapper wakeup function, you avoid these issues.
Another approach you may elect to use is to only in the native wrapper 
load both the thread-local poll value and the 
SafepointSynchronize::_state, when filtering slow paths to avoid this 
unfortunate race.

I have a bunch of other ideas as well exploiting dependent loads, but 
thought maybe I should start the conversation and see what you think 
before running off too much.

Again, very happy to see this move to use thread-local handshakes. 
Thanks for doing this.

Thanks,
/Erik

On 2017-11-23 15:12, Andrew Haley wrote:
> On 23/11/17 11:30, Andrew Haley wrote:
>> It was quite tricky to get this one right.
>>
>> Some notes:
>>
>> I deleted LIR_Assembler::poll_for_safepoint because it's dead code.
>>
>> I set ThreadLocalHandshakes to true.  This follows x86-64 and is
>> probably the right thing to do, but it does have the disadvantage that
>> it considerably extends the time to safepoint for interpreted code
>> because we only poll at branches.  I think this is probably a bad
>> idea, but again it's the same as x86-64.  OTOH, checking the safepoint
>> bit at every bytecode would seriously bloat the interpreter.  Oh dear.
>>
> http://cr.openjdk.java.net/~aph/8189596-1/
>



More information about the hotspot-dev mailing list