RFR(L) 8153224 Monitor deflation prolong safepoints (CR7/v2.07/10-for-jdk14)
David Holmes
david.holmes at oracle.com
Wed Nov 6 05:24:32 UTC 2019
Hi Dan,
Trimming ...
On 6/11/2019 1:36 am, Daniel D. Daugherty wrote:
>
> And my brain returns to the more fundamental question of why do we have
> OrderAccess::storeload() at L1045 and OrderAccess::fence() at L1225?
> Both sites are trying to separate the release_store(&_owner, NULL) from
> a subsequent load. In the first case:
>
> 1044 OrderAccess::release_store(&_owner, (void*)NULL); // drop the
> lock
> 1045 OrderAccess::storeload(); // See if we
> need to wake a successor
> <snip>
> 1047 if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
>
> In the second case:
>
> 1224 OrderAccess::release_store(&_owner, (void*)NULL);
> 1225 OrderAccess::fence(); // ST
> _owner vs LD in unpark()
> <snip>
> 1229 Trigger->unpark();
>
> The code has been this way for a very long time, but why?
The devil is in the detail and this is a very complex piece of code with
very complex usage protocols. Generally speaking when you release the
lock you want a fence() to ensure immediate visibility to other threads
that the lock is now free. That happens in paths that will complete the
monitor operation quickly e.g. in ExitEpilog. In other places after
releasing the lock we may have more work to do - like wake a successor -
so we only need the storeload() after the release_store to make sure we
don't read the queue heads until after we've released the lock; the code
that follows that can then have additional memory sync operations (like
an atomic op) that implicitly achieve the effects of the fence.
> Of course, this question about the baseline code is still a sidebar.
> An interesting sidebar, but...
>
>
> For Async Monitor Deflation, I think we need fence() at both locations
> for proper interaction with the deflater thread.
Possibly. The devil is in the detail of the actual code paths.
>> As per previous discussion I think you still need a release_store of
>> _owner (at least in the case where you are releasing the monitor).
>
> I've made a mistake with this encapsulation. I made it look like a
> general setter of a new value. In reality, both callers specify
> new_value == NULL so we don't actually need the new_value parameter.
>
> I think it needs to be something like this:
>
> 124 // Clear _owner field; current value must match old_value.
> 125 inline void ObjectMonitor::clear_owner_from(void* old_value) {
> 126 void* prev = _owner;
> 127 ADIM_guarantee(prev == old_value, "unexpected prev owner="
> INTPTR_FORMAT
> 128 ", expected=" INTPTR_FORMAT, p2i(prev),
> p2i(old_value));
> 129 OrderAccess::release_store(&_owner, (void*)NULL);
> 130 OrderAccess::fence();
> 131 log_trace(monitorinflation, owner)("clear_owner_from(): mid="
> INTPTR_FORMAT
> 132 ", prev=" INTPTR_FORMAT,
> p2i(this), p2i(prev));
> 133 }
When factored out like this you are forced to use the heaviest memory
barrier needed by a given callsite and then apply it to all - ie using
the fence() always when it might not be needed.
I'm really not a fan of this kind of factoring out in complex lock-free
code as it hides the details of the memory sync operations. You could
make this more obvious in the naming e.g.
inline void ObjectMonitor::release_clear_owner_and_fence(void*
expected_owner)
or if the fence is not always needed you could parameterise the final
memory barrier as well.
> Thanks for sticking with this part of the review.
>
>
>> That's it on this thread. I still have to look at version 2.08 in full.
>
> I look forward to your feedback!
Hopefully tomorrow. Trying to track down a memory corruption issue.
Cheers,
David
More information about the hotspot-runtime-dev
mailing list