RFR: 8231289: Disentangle JvmtiRawMonitor from ObjectMonitor and clean it up
serguei.spitsyn at oracle.com
serguei.spitsyn at oracle.com
Thu Oct 3 17:15:56 UTC 2019
On 10/3/19 03:13, David Holmes wrote:
> Hi Dan,
>
> On 3/10/2019 3:20 am, Daniel D. Daugherty wrote:
>> Sorry for the delay in reviewing this one... I've been playing
>> whack-a-mole
>> with Robbin's MoCrazy test and my AsyncMonitorDeflation bits...
>
> No problem - your contribution made the wait worthwhile :)
>
>> On 9/24/19 1:09 AM, David Holmes wrote:
>>> Bug: https://bugs.openjdk.java.net/browse/JDK-8231289
>>> webrev: http://cr.openjdk.java.net/~dholmes/8231289/webrev/
>>
>> src/hotspot/share/prims/jvmtiEnv.cpp
>> Thanks for removing the PROPER_TRANSITIONS stuff. That was old
>> and crufty stuff.
>>
>> src/hotspot/share/prims/jvmtiEnvBase.cpp
>> No comments.
>>
>> src/hotspot/share/prims/jvmtiRawMonitor.cpp
>> L39: new (ResourceObj::C_HEAP, mtInternal)
>> GrowableArray<JvmtiRawMonitor*>(1,true);
>> nit - need a space between ',' and 'true'.
>>
>> Update: leave for your follow-up bug.
>
> Fixed now so I don't forget later. :)
>
>> src/hotspot/share/prims/jvmtiRawMonitor.hpp
>> No comments.
>>
>> src/hotspot/share/runtime/objectMonitor.hpp
>> Glad I added those 'protected for JvmtiRawMonitor' in one
>> of my recent cleanup bugs. Obviously I'll have to merge
>> with Async Monitor Deflation. :-)
>>
>> src/hotspot/share/runtime/thread.cpp
>> No comments.
>>
>> src/hotspot/share/runtime/thread.hpp
>> No comments.
>>
>> src/hotspot/share/services/threadService.cpp
>> L397: waitingToLockMonitor = jt->current_pending_monitor();
>> L398: if (waitingToLockMonitor == NULL) {
>> L399: // we can only be blocked on a raw monitor if not
>> blocked on an ObjectMonitor
>> L400: waitingToLockRawMonitor =
>> jt->current_pending_raw_monitor();
>> L401: }
>>
>> JVM/TI has this event handler:
>>
>> typedef void (JNICALL *jvmtiEventMonitorContendedEnter)
>> (jvmtiEnv *jvmti_env,
>> JNIEnv* jni_env,
>> jthread thread,
>> jobject object);
>>
>> This event handler is called after
>> set_current_pending_monitor()
>> and if the event handler uses a RawMonitor, then it possible
>> for
>> for the thread to show up as blocked on both a Java monitor and
>> a JVM/TI RawMonitor.
>
> Oh that is interesting - good catch! So that means the current code is
> broken because the raw monitor will replace the ObjectMonitor as the
> pending monitor and then set it back to NULL, thus losing the fact the
> thread is actually pending on the ObjectMonitor. And of course while
> the pending monitor is the raw monitor that totally messes up the
> deadlock detection as the ObjectMonitor is missing from consideration. :(
If I remember correctly it is a scenario where this issue also comes to
the picture:
https://bugs.openjdk.java.net/browse/JDK-8033399
I do not really understand how shared ParkEvent can be used/consumed by
both ObjectMonitor and RawMonitor on the same thread.
But we observed and investigated this problem several years ago and Dan
finally filed this enhancement.
Thanks,
Serguei
> This also probably means that you can have a pending raw monitor at
> the same time as you have a "Blocker" as I'm pretty sure there are
> various JVM TI event handlers that may execute between the Blocker
> being set and the actual park. So that would be an additional breakage
> in the existing code.
>
> Back to my code and I have two problems. The second, which is easy to
> address, is the deadlock printing code. I'll hoist the
> waitingToLockRawMonitor chunk to the top so it is executed independent
> of the waitingToLockMonitor value (which remains in an if/else
> relationship with the waitingToLockBlocker). But now that we might
> print two "records" at a time I have to make additional changes to get
> meaningful output for the current thread (which is handled as a common
> code after the if/else block to finish whichever record was being
> printed). Also I can no longer use "continue" as the 3 outcomes are
> not mutually exclusive - so this could get a bit messy. :(
>
> So definitely a v2 webrev on the way.
>
> But before that I need to solve my first problem - and I don't know
> how. Now that it is apparent that a thread can be blocked on both a
> raw monitor and an ObjectMonitor at the same time, I have no idea how
> to actually account for this in the deadlock detection code. That code
> has a while loop that expects to at most find either a locked
> ObjectMonitor or j.u.c Blocker, and it adds the owner thread to the
> cycle detection, then moves on. But now I can have two different owner
> threads in the same loop iteration. I don't know how to account for that.
>
> Given that it seems to me that the current code is already broken if
> we encounter these conditions, then perhaps all I can do is handle the
> other cases, where the blocking reasons are mutually exclusive, and
> not try to fix things? i.e. leave lines #434 to #440 as they are in
> webrev v1 - which implies no change to line #398 except the comment
> ... ??
>
>> test/hotspot/jtreg/vmTestbase/nsk/jvmti/RawMonitorWait/rawmnwait005/rawmnwait005.cpp
>>
>> No comments.
>>
>>
>> Thumbs up! The only non-nit I have is the setting of
>> waitingToLockRawMonitor
>> on L400 and the corresponding comment on L399. Everything else is a nit.
>>
>> I don't need to see a new webrev.
>
> If only that were true :(
>
> Thanks,
> David
>
>> Thanks for tackling this disentangle issue!
>>
>> Dan
>>
>>
>>>
>>> The earlier attempt to rewrite JvmtiRawMonitor as a simple wrapper
>>> around PlatformMonitor proved not so simple and ultimately had too
>>> many issues due to the need to support Thread.interrupt.
>>>
>>> I'd previously stated in the bug report:
>>>
>>> "In the worst-case I suppose we could just copy ObjectMonitor to a
>>> new class and have JvmtiRawMonitor continue to extend that (with
>>> some additional minor adjustments) - or even just inline it all as
>>> needed."
>>>
>>> but hadn't looked at it in detail. Richard Reingruber did look at it
>>> and pointed out that it is actually quite simple - we barely use any
>>> actual code from ObjectMonitor, mainly just the state. So thanks
>>> Richard! :)
>>>
>>> So this change basically copies or moves anything needed by
>>> JvmtiRawMonitor from ObjectMonitor, breaking the connection between
>>> the two. We also copy and simplify ObjectWaiter, turning it into a
>>> QNode internal class. There is then a lot of cleanup that was
>>> applied (and a lot more that could still be done):
>>>
>>> - Removed the never implemented/used PROPER_TRANSITIONS ifdefs
>>> - Fixed the disconnect between the types of non-JavaThreads expected
>>> by the upper layer code and lower layer code
>>> - cleaned up and simplified return codes
>>> - consolidated code that is identical for JavaThreads and
>>> non-JavaThreads (e.g. notify/notifyAll).
>>> - removed used of TRAPS/THREAD where not appropriate and replaced
>>> with "Thread * Self" in the style of the rest of the code
>>> - changed recursions to be int rather than intptr_t (a "fixme" in
>>> the ObjectMonitor code)
>>>
>>>
>>> I have not changed the many style flaws with this code:
>>> - Capitalized names
>>> - extra spaces before ;
>>> - ...
>>>
>>> but could do so if needed. I wanted to try and keep it more obvious
>>> that the fundamental functional code is actually unmodified.
>>>
>>> There is one aspect that requires further explanation: the notion of
>>> current pending monitor. The "current pending monitor" is stored in
>>> the Thread and used by a number of introspection APIs for things
>>> like finding monitors, doing deadlock detection, etc. The
>>> JvmtiRawMonitor code would also set/clear itself as "current pending
>>> monitor". Most uses of the current pending monitor actually,
>>> explicitly or implicitly, ignore the case when the monitor is a
>>> JvmtiRawMonitor (observed by the fact the mon->object() query
>>> returns NULL). The exception to that is deadlock detection where raw
>>> monitors are at least partially accounted for. To preserve that I
>>> added the notion of "current pending raw monitor" and updated the
>>> deadlock detection code to use that.
>>>
>>> The test:
>>>
>>>
>>> test/hotspot/jtreg/vmTestbase/nsk/jvmti/RawMonitorWait/rawmnwait005/rawmnwait005.cpp
>>>
>>>
>>> was updated because I'd noticed previously that it was the only test
>>> that used interrupt with raw monitors, but was in fact broken: the
>>> test thread is a daemon thread so the main thread could terminate
>>> the VM immediately after the interrupt() call, thus you would never
>>> know if the interruption actually worked as expected.
>>>
>>> Testing:
>>> - tiers 1 - 3
>>> - vmTestbase/nsk/monitoring/ (for deadlock detection**)
>>> - vmTestbase/nsk/jdwp
>>> - vmTestbase/nsk/jdb/
>>> - vmTestbase/nsk/jdi/
>>> - vmTestbase/nsk/jvmti/
>>> - serviceability/jvmti/
>>> - serviceability/jdwp
>>> - JDK: java/lang/management
>>>
>>> ** There are no existing deadlock related tests involving
>>> JvmtiRawMonitor. It would be interesting/useful to add them to the
>>> existing nsk/monitoring tests that cover synchronized and JNI
>>> locking. But it's a non-trivial enhancement that I don't really have
>>> time to do.
>>>
>>> Thanks,
>>> David
>>> -----
>>
More information about the serviceability-dev
mailing list