RFR(L) 8153224 Monitor deflation prolong safepoints

Daniel D. Daugherty daniel.daugherty at oracle.com
Mon Apr 15 17:15:11 UTC 2019


Just tracking to make sure I made all the changes...


On 4/9/19 11:53 AM, Daniel D. Daugherty wrote:
> Hi Carsten,
>
> Thanks for responding to Karen's code review comments.
>
> Karen,
>
> I have a query for you down at the end of my reply...
>
> More below...
>
> On 4/5/19 11:01 PM, Carsten Varming wrote:
>> Dear Karen,
>>
>> Please see inline answers.
>>
>> On Fri, Apr 5, 2019 at 4:59 PM Karen Kinnear 
>> <karen.kinnear at oracle.com <mailto:karen.kinnear at oracle.com>> wrote:
>>
>>
>>     4. In EnterI: if _owner == DEFLATER_MARKER there are guarantees
>>     that 0 < _count
>>     with comments that caller ensured _count <= 0
>>     In ReenterI: guarantee 0 <= _count, with comment not _count < 0
>>     — Am I missing something subtle here or should they be the same
>>     guarantees?
>>
>>
>> In ::enter _count is incremented when the thread is trying to acquire 
>> the monitor and decremented after the monitor has been acquired. The 
>> 0 < _count assertion is between those two point in the code. A thread 
>> acquiring a monitor and then calling wait will increment _count and 
>> then decrement _count as part of acquiring the monitor, thus _count 
>> can be 0 by the time the thread calls wait and when ReenterI is called.
>
> I had a similar answer and I'm planning to tweak the comments
> and the guarantees a bit in the next round of code review (CR1);
> please see my reply to Karen's CR for the proposed changes.

Done.


>
>>
>>     9. install_displaced_markword_in_object
>>     What happens if the cas_set_mark fails?
>>     I get that today this handles the race with enter and
>>     deflate_monitor_using_JT. If we remove
>>     the call from enter, is the expectation that we’ve blocked all
>>     others who did not set is_marked themselves?
>>     If we remove the call from enter would it make sense to ensure
>>     that the cas_set_mark succeeds here?
>>
>>
>> I designed my original patch such that no thread would ever wait for 
>> the the deflating thread to finish deflating a monitor. If you remove 
>> install_displaced_markword_in_object from enter, then the entering 
>> thread can end up busy waiting by continuously reading the monitor 
>> pointer from the object mark word and then realizing that the monitor 
>> is being deflated and it should retry by going back to reading the 
>> object mark word. This bad behavior is completely avoided by calling 
>> install_displaced_markword_in_object.
>
> Here's the code in question:
>
> src/hotspot/share/runtime/objectMonitor.cpp:
>
>> bool ObjectMonitor::enter(TRAPS) {
> <snip>
>>   // Prevent deflation. See ObjectSynchronizer::deflate_monitor() and 
>> is_busy().
>>   // Ensure the object-monitor relationship remains stable while 
>> there's contention.
>>   const jint count = Atomic::add(1, &_count);
>>   if (count <= 0 && _owner == DEFLATER_MARKER) {
>>     // Async deflation in progress. Help deflater thread install
>>     // the mark word (in case deflater thread is slow).
>>     install_displaced_markword_in_object();
>>     Self->_Stalled = 0;
>>     return false;  // Caller should retry. Never mind about _count as 
>> this monitor has been deflated.
>>   }
>
> Our thread (T-enter) observes that the ObjectMonitor is being deflated
> by T-deflate, calls install_displaced_markword_in_object() and returns
> false to the caller which causes a retry.
>
> Restoring the header/dmw from the ObjectMonitor to the object's header
> here isn't needed for correctness so it could be dropped (and would
> simplify the code). Your counterpoint is if we drop the call, then
> T-enter could do retry after retry if T-deflate is slow to get to its
> install_displaced_markword_in_object() call.
>
> If T-enter calls install_displaced_markword_in_object(), then T-enter
> will do a single retry because the object T-enter is trying to lock
> will no longer have an ObjectMonitor. Okay I finally grok it...
>
> I think we need to clarify the comment a bit:
>
> >   if (count <= 0 && _owner == DEFLATER_MARKER) {
> >     // Async deflation is in progress. Attempt to restore the
> >     // header/dmw to the object's header so that we only retry once
> >     // if the deflater thread happens to be slow.
> >     install_displaced_markword_in_object();

Done.


>
>> In my original patch no thread would ever wait for a deflating thread 
>> to finish. This property got lost in FastHashCode as that function 
>> evolved since I wrote my patch, but I think this property is worth 
>> preserving where possible. It might even be worth looking at 
>> FastHashCode to see if we can re-establish this property.
>
> Async Monitor Deflation causes races with FastHashCode() when the target
> object has an existing ObjectMonitor. Here's the base code:
>
>>   768   } else if (mark->has_monitor()) {
>>   769     monitor = mark->monitor();
>>   770     temp = monitor->header();
>> 771 assert(temp->is_neutral(), "invariant");
>>   772     hash = temp->hash();
>>   773     if (hash) {
>>   774       return hash;
>>   775     }
>>   776     // Skip to the following code to reduce code size
>
> The 'monitor' fetched on L769 is unstable due to Async Monitor
> Deflation and can cause an incorrect hash value to be returned.
> The solution is to protect the ObjectMonitor*:
>
>>   775   } else if (mark->has_monitor()) {
>> 776 ObjectMonitorHandle omh;
>> 777 if (!omh.save_om_ptr(obj, mark)) {
>> 778 // Lost a race with async deflation so try again.
>> 779 assert(AsyncDeflateIdleMonitors, "sanity check");
>> 780 goto Retry;
>> 781 }
>> 782 monitor = omh.om_ptr();
>>   783     temp = monitor->header();
>>   784     assert(temp->is_neutral(), "invariant: header=" INTPTR_FORMAT, p2i((address)temp));
>>   785     hash = temp->hash();
>>   786     if (hash != 0) {
>>   787       return hash;
>>   788     }
>>   789     // Skip to the following code to reduce code size
>
> where L776-L782 handle the protection duty and possible retry. So we
> have to protect the ObjectMonitor*, but, like enter(), we could call
> install_displaced_markword_in_object() when we retry which would limit
> T-hash to a single retry.

Done in save_om_ptr().


> ObjectSynchronizer::inflate() has a similar collision and retry issue:
>
>> 1456     // CASE: inflated
>> 1457     if (mark->has_monitor()) {
>> 1458 if (!omh_p->save_om_ptr(object, mark)) {
>> 1459 // Lost a race with async deflation so try again.
>> 1460 assert(AsyncDeflateIdleMonitors, "sanity check");
>> 1461 continue;
>> 1462 }
>
> In this situation, inflate() discovers that the object already has an
> ObjectMonitor; the object may not have had one when inflate() was
> called, but it has one now. That particular race predates this project.
>
> In any case, inflate() wants to return a stable ObjectMonitor* in the
> ObjectMonitorHandle, but if save_om_ptr() returns false, then inflate()
> has to retry. The only reason for save_om_ptr() to return false is due
> to a collision with Async Monitor Deflation. Like enter, we could call
> install_displaced_markword_in_object() when we retry which would limit
> inflate() to a single retry.

Done in save_om_ptr().


> Okay, I've evolved from thinking we could simplify the code by dropping
> install_displaced_markword_in_object() to thinking that I understand
> what install_displaced_markword_in_object() brings to the party. And now
> I'm proposing that we add 2 more install_displaced_markword_in_object()
> calls to limit retries on two more code paths.

So install_displaced_markword_in_object() is now called from:

   ObjectMonitor::enter()                         - no change
   ObjectMonitorHandle::save_om_ptr()             - on _owner == 
DEFLATER_MARKER detection
   ObjectSynchronizer::deflate_monitor_using_JT() - no change

Because of the new call in save_om_ptr(), calls to inflate() also pick up
the benefit of at most one retry. I've double checked the new loops and
the existing loops and I think all are covered.


Okay, I believe I've made all the changes for this set of comments
and replies...

Dan


More information about the hotspot-runtime-dev mailing list