[prev in list] [next in list] [prev in thread] [next in thread]
List: openjdk-serviceability-dev
Subject: Re: When does MONITOR_WAITED get thrown?
From: JC Beyler <jcbeyler () google ! com>
Date: 2017-09-22 3:09:55
Message-ID: CAF9BGBwnK+hRUFuMkYDZ61wUkjkqGw4Ht__nUFLdLXw4KYw9YQ () mail ! gmail ! com
[Download RAW message or body]
Hi David,
It does explain it, thanks for your detailed explanations :)
I'll come back if I have more questions but I think I'm good! Thanks again
and have a great weekend!
Thanks,
Jc
On Tue, Sep 19, 2017 at 3:01 PM, David Holmes <david.holmes@oracle.com>
wrote:
> Hi Jc,
>
> On 20/09/2017 2:26 AM, JC Beyler wrote:
>
>> Hi David,
>>
>> First off, thanks for looking at this case! I really apologize for my
>> example being difficult to follow. I hope you won't mind if I try again :).
>>
>> The question is less about ordering and more on the fact that sometimes
>> the MONITOR_WAITED does not get emitted it seems depending on what is
>> happening for the MONITOR_WAIT event. Let me give a full working code at
>> the bottom of this that show cases my question/issue.
>>
>> Basically, there are three cases I can think of for this test:
>>
>> 1) The MONITOR_WAIT callback throws an illegal exception (or another
>> exception I expect would have the same issue)
>> 2) The actual call from Java world to the wait is illegal due to a
>> negative value for example: thread.wait(-100) AND the MONITOR_WAIT callback
>> throws an illegal exception
>> 3) The actual call from Java world to the wait is illegal due to a
>> negative value for example: thread.wait(-100) AND the MONITOR_WAIT callback
>> does not throw an exception
>>
>>
>> What I've noticed is that the behavior is slightly different depending on
>> the case:
>>
>> 1) In the case 1 above, we get a MONITOR_WAITED event
>> 2) In the case 2 above, we get exactly the same error and I believe that
>> probably because we check the argument to wait after doing the callback to
>> MONITOR_WAIT; however we get MONITOR_WAITED callback at all
>> 3) In the case 3 above, however, we get an illegal argument due to the
>> -100 for the wait call, however we do get a MONITOR_WAITED callback.
>>
>
> First it is a bug that wait(-100) triggers any JVM TI events. The
> IllegalArgumentException should be thrown at the Java level - and indeed
> this has just been fixed. (Try using wait(-100,0) instead to see this.)
>
> That aside, for wait(-100) you will get a MONITOR_WAIT event, then the
> throwing of the IllegalArgumentException. You will never see MONITOR_WAITED
> with respect to an illegal call to wait() - we simply never hit the code
> that does the waiting and posts the event. (If you do see MONITOR_WAITED
> then it may well be for a different wait() call.)
>
> If the MONITOR_WAIT handler itself posts an exception, and the wait() call
> is illegal then the IllegalArgumentException will replace the exception
> posted by the handler.
>
> If the handler posts an exception and the call to wait is legal then it is
> as you describe below ...
>
> My question is really the following:
>>
>> - Should we not have the same behavior (Whatever that may be by the way)
>> in all three cases? In all three cases, we do not wait due to an exception
>> being thrown but 2 out of 3 do not have a MONITOR_WAITED event sent out.
>>
>> - When I look at the code, why this is happening is because the code
>> seems to be doing this:
>>
>> Do the JVM_MonitorWait in jvm.cpp
>> A) post the monitor wait callback
>> B) Do the actual wait via ObjectSynchronizer
>> i) Check the argument in ObjectSynchronizer::wait
>> (src/share/vm/runtime/synchronizer.cpp)
>> 1) All good? Go in ObjectMonitor::wait
>> (src/share/vm/runtime/objectMonitor.cpp)
>> - Check if the JNI threw, bail there after posting for waited
>>
>
> Other than if the thread is interrupted I don't see any check for a
> pending exception. So the wait() happens, the MONITOR_WAITED is posted, and
> eventually the pending exception from the MONITOR_WAIT is detected and
> thrown.
>
> 2) Not good? bail without throwing
>>
>
> If the timeout is illegal it posts the IllegalArgumentException and
> returns.
>
> The JVM TI spec says very little about exceptions and event callback
> handlers, other than being careful not to lose existing pending exceptions.
> In particular the fact there may be an exception pending, whether from the
> handler or other source, does not influence subsequent event handling
> directly - but obviously the logic in the VM that checks for pending
> exceptions will affect whether certain callback points can be reached if an
> exception is pending.
>
> Does this explain what you observe?
>
> Cheers,
> David
> ------
>
>
>>
>> Note: I've added the whole code below and refactored it so that the
>> output is more clear and you have all cases together in one code (I
>> augmented the sleep to 5 seconds to make it clear :)):
>>
>> Thanks for your help,
>> Jc
>>
>>
>> ------------------------------------------------------------
>> --------------------------
>>
>>
>> A) Here is the agent code:
>> #include <jvmti.h>
>> #include <stdio.h>
>> #include <string.h>
>>
>> static void JNICALL
>> monitor_wait(jvmtiEnv* jvmti, JNIEnv *env,
>> jthread thread, jobject object, jlong timeout) {
>> fprintf(stderr, "Waiting!\n");
>>
>> // Throw only every other time the first few times, then never again
>> (Works out well for the Java test :)):
>> // - We want it to throw the first wait but not the first join.
>> // - We want it to throw the second wait but not the second join.
>> // - We want it to no longer throw at all.
>> static int cnt = 0;
>> if (cnt++ % 2 == 0 && cnt < 4) {
>> jclass newExcCls = env->FindClass("java/lang/Ille
>> galArgumentException");
>> // Unable to find the new exception class, give up.
>> if (newExcCls == 0) {
>> return;
>> }
>> env->ThrowNew(newExcCls, "Exception from monitor_wait");
>> }
>> }
>>
>> static void JNICALL
>> monitor_waited(jvmtiEnv* jvmti, JNIEnv *env,
>> jthread thread, jobject object, jboolean timed_out) {
>> fprintf(stderr, "Waited!\n");
>> }
>>
>> extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options,
>> void *reserved) {
>> int ernum;
>>
>> jvmtiEnv *jvmti;
>> if (jvm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION) !=
>> JNI_OK) {
>> return JNI_ERR;
>> }
>>
>> jvmtiEventCallbacks callbacks;
>> memset(&callbacks, 0, sizeof(callbacks));
>> callbacks.MonitorWait = &monitor_wait;
>> callbacks.MonitorWaited = &monitor_waited;
>>
>> jvmtiCapabilities caps;
>> memset(&caps, 0, sizeof(caps));
>> caps.can_generate_monitor_events = 1;
>>
>> ernum = jvmti->AddCapabilities(&caps);
>> ernum = jvmti->SetEventCallbacks(&callbacks,
>> sizeof(jvmtiEventCallbacks));
>> ernum = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
>> JVMTI_EVENT_MONITOR_WAIT, NULL);
>> ernum = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
>> JVMTI_EVENT_MONITOR_WAITED, NULL);
>>
>> return ernum;
>> }
>>
>> B) Here is the Java code, it's a bit long but I added comments and made
>> it clear (I really hope :)):
>>
>> public class Main extends Thread {
>> public static void main(String[] args) {
>>
>> for (int i = 0; i < 3; i++) {
>> SecondThread st = new SecondThread("first");
>>
>> synchronized (st) {
>> st.start();
>>
>> System.err.println("Starting with simple wait");
>> try {
>> st.wait();
>> } catch(Exception e) {
>> System.out.println("Here we got a monitor_waited call, the
>> underlying monitor_wait callback threw: " + e);
>> }
>>
>> System.err.println("\n\nJoining so wait/waited will show");
>> try {
>> st.join();
>> } catch(Exception e) {
>> System.err.println("Join exception, not intended for this test:
>> " + e);
>> }
>> }
>>
>> st = new SecondThread("second");
>>
>> synchronized (st) {
>> st.start();
>>
>> System.err.println("\n\nStarting with negative wait");
>> try {
>> st.wait(-100);
>> } catch(Exception e) {
>> System.err.println("Here we did not get a callback to
>> monitor_waited though same exception as above: " + e);
>> }
>>
>> System.err.println("\n\nJoining so wait/waited will show");
>> try {
>> st.join();
>> } catch(Exception e) {
>> System.err.println("Join exception, not intended for this test:
>> " + e);
>> }
>> }
>>
>> st = new SecondThread("third");
>>
>> synchronized (st) {
>> st.start();
>>
>> System.err.println("\n\nStarting with simple wait, monitor_wait
>> won't throw anymore");
>> try {
>> st.wait(-100);
>> } catch(Exception e) {
>> System.err.println("Here we did not get a callback to
>> monitor_waited though different exception as above: " + e);
>> }
>>
>> System.err.println("\n\nJoining so wait/waited will show");
>> try {
>> st.join();
>> } catch(Exception e) {
>> System.err.println("Join exception, not intended for this test:
>> " + e);
>> }
>> }
>>
>> System.out.println("Done");
>> }
>> }
>>
>> class SecondThread extends Thread {
>> private String name;
>>
>> public SecondThread(String name) {
>> this.name <http://this.name> = name;
>>
>> }
>>
>> public void run() {
>> try {
>> Thread.sleep(5000);
>> } catch(Exception e) {
>> System.err.println(e);
>> }
>> System.err.println("Hello from " + name);
>> }
>> }
>>
>> C) The output I get is:
>> Starting with simple wait
>> Waiting!
>> Waited!
>> Here we got a monitor_waited call, the underlying monitor_wait callback
>> threw: java.lang.IllegalArgumentException: Exception from monitor_wait
>>
>>
>> Joining so wait/waited will show
>> Waiting!
>> Hello from first
>> Waited!
>>
>>
>> Starting with negative wait
>> Waiting!
>> Here we did not get a callback to monitor_waited though same exception as
>> above: java.lang.IllegalArgumentException: Exception from monitor_wait
>>
>>
>> Joining so wait/waited will show
>> Waiting!
>> Hello from second
>> Waited!
>>
>>
>> Starting with simple wait, monitor_wait won't throw anymore
>> Waiting!
>> Here we did not get a callback to monitor_waited though different
>> exception as above: java.lang.IllegalArgumentException: timeout value is
>> negative
>>
>>
>> Joining so wait/waited will show
>> Waiting!
>> Hello from third
>> Waited!
>> Done
>>
>>
>> On Mon, Sep 18, 2017 at 3:04 PM, David Holmes <david.holmes@oracle.com
>> <mailto:david.holmes@oracle.com>> wrote:
>>
>> Hi Jc,
>>
>> I found your example very difficult to follow. AFAICS you will
>> potentially encounter monitor related events that are unrelated to
>> the st.wait in your test code. And there are races in the test - as
>> soon as st.wait releases the monitor lock then SecondThread can run,
>> complete the sleep and print "Hello from A". I don't think it is
>> specified exactly when the MONIOR_WAIT event is sent with respect to
>> the releasing of the monitor lock.
>>
>> David
>>
>>
>> On 19/09/2017 3:45 AM, JC Beyler wrote:
>>
>> Hi all,
>>
>> When looking at the documentation of
>> https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.
>> html#MonitorWaited
>> <https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.
>> html#MonitorWaited>
>> , I see the following description:
>> "Sent when a thread finishes waiting on an object."
>>
>> However, there seems to be a slight issue when the MONITOR_WAIT
>> event handler throws an exception.
>>
>> Consider the following code:
>>
>> A) The monitor wait handler throws
>>
>> static void JNICALL
>> monitor_wait(jvmtiEnv* jvmti, JNIEnv *env,
>> jthread thread, jobject object, jlong timeout) {
>> fprintf(stderr, "Waiting!\n");
>>
>> jclass newExcCls =
>> env->FindClass("java/lang/IllegalArgumentException");
>> // Unable to find the new exception class, give up.
>> if (newExcCls == 0) {
>> return;
>> }
>> env->ThrowNew(newExcCls, "Exception from monitor_wait");
>> }
>>
>> B) The monitor waited handler does a printf:
>> static void JNICALL
>> monitor_waited(jvmtiEnv* jvmti, JNIEnv *env,
>> jthread thread, jobject object, jboolean
>> timed_out) {
>> fprintf(stderr, "Waited!\n");
>> }
>>
>> B) A second thread that will be asked to wait:
>> class SecondThread extends Thread {
>> public void run() {
>> try {
>> Thread.sleep(1);
>> } catch(Exception e) {
>> }
>> System.out.println("Hello from A");
>> }
>> }
>>
>> C) The main thread with the wait:
>> class Main extends Thread {
>> public static void main(String[] args) {
>> SecondThread st = new SecondThread();
>>
>> synchronized (st) {
>> st.start();
>>
>> try {
>> st.wait();
>> } catch(InterruptedException e) {
>> System.out.println("Exception caught!");
>> }
>> }
>>
>> System.out.println("Done");
>> }
>> }
>>
>> D) If I do this, what happens is that I get:
>>
>> Waiting!
>> Waited!
>> Exception in thread "main" java.lang.IllegalArgumentException:
>> Exception from monitor_wait
>> at java.lang.Object.wait(Native Method)
>> at java.lang.Object.wait(Object.java:502)
>> at Main.main(Main.java:9)
>> Hello from A
>>
>> - As we see, we get the print out from waiting, print out from
>> waited and then the exception in the waiting.
>>
>>
>> E) If instead, we do st.wait(-100) in the main method, we get:
>> Waiting!
>> Exception in thread "main" java.lang.IllegalArgumentException:
>> Exception from monitor_wait
>> at java.lang.Object.wait(Native Method)
>> at Main.main(Main.java:9)
>> Hello from A
>>
>> Notes: the stack is slightly different, the printout waited is
>> no longer there however
>>
>> F) If finally, we don't throw in the waiting handler but leave
>> the st.wait(-100) in place:
>> Waiting!
>> Exception in thread "main" java.lang.IllegalArgumentException:
>> timeout value is negative
>> at java.lang.Object.wait(Native Method)
>> at Main.main(Main.java:9)
>> Hello from A
>>
>>
>> The question thus becomes: is this normal that there is a slight
>> difference between D/E/F?
>>
>> Should we try to fix it to have a single behavior in the three
>> cases, and if so, which would be the behavior that should be the
>> default?
>>
>> Let me know if you would like to see a full code to easily
>> replicate, I gave the big parts but left out the Agent_OnLoad
>> method for example.
>>
>> Thanks!
>> Jc
>>
>>
>>
[Attachment #3 (text/html)]
<div dir="ltr">Hi David,<div><br></div><div>It does explain it, thanks for your \
detailed explanations :)</div><div><br></div><div>I'll come back if I have more \
questions but I think I'm good! Thanks again and have a great \
weekend!</div><div><br></div><div>Thanks,</div><div>Jc</div></div><div \
class="gmail_extra"><br><div class="gmail_quote">On Tue, Sep 19, 2017 at 3:01 PM, \
David Holmes <span dir="ltr"><<a href="mailto:david.holmes@oracle.com" \
target="_blank">david.holmes@oracle.com</a>></span> wrote:<br><blockquote \
class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex">Hi Jc,<span class=""><br> <br>
On 20/09/2017 2:26 AM, JC Beyler wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> Hi David,<br>
<br>
First off, thanks for looking at this case! I really apologize for my example being \
difficult to follow. I hope you won't mind if I try again :).<br> <br>
The question is less about ordering and more on the fact that sometimes the \
MONITOR_WAITED does not get emitted it seems depending on what is happening for the \
MONITOR_WAIT event. Let me give a full working code at the bottom of this that show \
cases my question/issue.<br> <br>
Basically, there are three cases I can think of for this test:<br>
<br>
1) The MONITOR_WAIT callback throws an illegal exception (or another exception I \
expect would have the same issue)<br> 2) The actual call from Java world to the wait \
is illegal due to a negative value for example: thread.wait(-100) AND the \
MONITOR_WAIT callback throws an illegal exception<br> 3) The actual call from Java \
world to the wait is illegal due to a negative value for example: thread.wait(-100) \
AND the MONITOR_WAIT callback does not throw an exception<br> <br>
<br>
What I've noticed is that the behavior is slightly different depending on the \
case:<br> <br>
1) In the case 1 above, we get a MONITOR_WAITED event<br>
2) In the case 2 above, we get exactly the same error and I believe that probably \
because we check the argument to wait after doing the callback to MONITOR_WAIT; \
however we get MONITOR_WAITED callback at all<br> 3) In the case 3 above, however, we \
get an illegal argument due to the -100 for the wait call, however we do get a \
MONITOR_WAITED callback.<br> </blockquote>
<br></span>
First it is a bug that wait(-100) triggers any JVM TI events. The \
IllegalArgumentException should be thrown at the Java level - and indeed this has \
just been fixed. (Try using wait(-100,0) instead to see this.)<br> <br>
That aside, for wait(-100) you will get a MONITOR_WAIT event, then the throwing of \
the IllegalArgumentException. You will never see MONITOR_WAITED with respect to an \
illegal call to wait() - we simply never hit the code that does the waiting and posts \
the event. (If you do see MONITOR_WAITED then it may well be for a different wait() \
call.)<br> <br>
If the MONITOR_WAIT handler itself posts an exception, and the wait() call is illegal \
then the IllegalArgumentException will replace the exception posted by the \
handler.<br> <br>
If the handler posts an exception and the call to wait is legal then it is as you \
describe below ...<span class=""><br> <br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> My question is really the following:<br>
<br>
- Should we not have the same behavior (Whatever that may be by the way) in all three \
cases? In all three cases, we do not wait due to an exception being thrown but 2 out \
of 3 do not have a MONITOR_WAITED event sent out.<br> <br>
- When I look at the code, why this is happening is because the code seems to be \
doing this:<br> <br>
Do the JVM_MonitorWait in jvm.cpp<br>
A) post the monitor wait callback<br>
B) Do the actual wait via ObjectSynchronizer<br>
i) Check the argument in ObjectSynchronizer::wait \
(src/share/vm/runtime/synchron<wbr>izer.cpp)<br>
1) All good? Go in ObjectMonitor::wait \
(src/share/vm/runtime/objectMo<wbr>nitor.cpp)<br>
- Check if the JNI threw, bail there after posting for waited<br>
</blockquote>
<br></span>
Other than if the thread is interrupted I don't see any check for a pending \
exception. So the wait() happens, the MONITOR_WAITED is posted, and eventually the \
pending exception from the MONITOR_WAIT is detected and thrown.<span class=""><br> \
<br> <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"> 2) Not good? bail without throwing<br>
</blockquote>
<br></span>
If the timeout is illegal it posts the IllegalArgumentException and returns.<br>
<br>
The JVM TI spec says very little about exceptions and event callback handlers, other \
than being careful not to lose existing pending exceptions. In particular the fact \
there may be an exception pending, whether from the handler or other source, does not \
influence subsequent event handling directly - but obviously the logic in the VM that \
checks for pending exceptions will affect whether certain callback points can be \
reached if an exception is pending.<br> <br>
Does this explain what you observe?<br>
<br>
Cheers,<br>
David<br>
------<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"><div><div class="h5"> <br>
<br>
Note: I've added the whole code below and refactored it so that the output is \
more clear and you have all cases together in one code (I augmented the sleep to 5 \
seconds to make it clear :)):<br> <br>
Thanks for your help,<br>
Jc<br>
<br>
<br>
------------------------------<wbr>------------------------------<wbr>--------------------------<br>
<br>
<br>
A) Here is the agent code:<br>
#include <jvmti.h><br>
#include <stdio.h><br>
#include <string.h><br>
<br>
static void JNICALL<br>
monitor_wait(jvmtiEnv* jvmti, JNIEnv *env,<br>
jthread thread, jobject object, jlong timeout) {<br>
fprintf(stderr, "Waiting!\n");<br>
<br>
// Throw only every other time the first few times, then never again (Works out \
well for the Java test :)):<br>
// - We want it to throw the first wait but not the first join.<br>
// - We want it to throw the second wait but not the second join.<br>
// - We want it to no longer throw at all.<br>
static int cnt = 0;<br>
if (cnt++ % 2 == 0 && cnt < 4) {<br>
jclass newExcCls = \
env->FindClass("java/lang/Ille<wbr>galArgumentException");<br> // \
Unable to find the new exception class, give up.<br> if (newExcCls == 0) {<br>
return;<br>
}<br>
env->ThrowNew(newExcCls, "Exception from monitor_wait");<br>
}<br>
}<br>
<br>
static void JNICALL<br>
monitor_waited(jvmtiEnv* jvmti, JNIEnv *env,<br>
jthread thread, jobject object, jboolean timed_out) {<br>
fprintf(stderr, "Waited!\n");<br>
}<br>
<br>
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char \
*options,<br>
void \
*reserved) {<br> int ernum;<br>
<br>
jvmtiEnv *jvmti;<br>
if (jvm->GetEnv(reinterpret_cast<<wbr>void **>(&jvmti), \
JVMTI_VERSION) != JNI_OK) {<br> return JNI_ERR;<br>
}<br>
<br>
jvmtiEventCallbacks callbacks;<br>
memset(&callbacks, 0, sizeof(callbacks));<br>
callbacks.MonitorWait = &monitor_wait;<br>
callbacks.MonitorWaited = &monitor_waited;<br>
<br>
jvmtiCapabilities caps;<br>
memset(&caps, 0, sizeof(caps));<br>
caps.can_generate_monitor_even<wbr>ts = 1;<br>
<br>
ernum = jvmti->AddCapabilities(&caps);<br>
ernum = jvmti->SetEventCallbacks(&call<wbr>backs, \
sizeof(jvmtiEventCallbacks));<br>
ernum = jvmti->SetEventNotificationMod<wbr>e(JVMTI_ENABLE, \
JVMTI_EVENT_MONITOR_WAIT, NULL);<br>
ernum = jvmti->SetEventNotificationMod<wbr>e(JVMTI_ENABLE, \
JVMTI_EVENT_MONITOR_WAITED, NULL);<br> <br>
return ernum;<br>
}<br>
<br>
B) Here is the Java code, it's a bit long but I added comments and made it clear \
(I really hope :)):<br> <br>
public class Main extends Thread {<br>
public static void main(String[] args) {<br>
<br>
for (int i = 0; i < 3; i++) {<br>
SecondThread st = new SecondThread("first");<br>
<br>
synchronized (st) {<br>
st.start();<br>
<br>
System.err.println("Starting with simple wait");<br>
try {<br>
st.wait();<br>
} catch(Exception e) {<br>
System.out.println("Here we got a monitor_waited call, the \
underlying monitor_wait callback threw: " + e);<br> }<br>
<br>
System.err.println("\n\nJoinin<wbr>g so wait/waited will \
show");<br> try {<br>
st.join();<br>
} catch(Exception e) {<br>
System.err.println("Join exception, not intended for this test: \
" + e);<br> }<br>
}<br>
<br>
st = new SecondThread("second");<br>
<br>
synchronized (st) {<br>
st.start();<br>
<br>
System.err.println("\n\nStarti<wbr>ng with negative wait");<br>
try {<br>
st.wait(-100);<br>
} catch(Exception e) {<br>
System.err.println("Here we did not get a callback to \
monitor_waited though same exception as above: " + e);<br> }<br>
<br>
System.err.println("\n\nJoinin<wbr>g so wait/waited will \
show");<br> try {<br>
st.join();<br>
} catch(Exception e) {<br>
System.err.println("Join exception, not intended for this test: \
" + e);<br> }<br>
}<br>
<br>
st = new SecondThread("third");<br>
<br>
synchronized (st) {<br>
st.start();<br>
<br>
System.err.println("\n\nStarti<wbr>ng with simple wait, monitor_wait \
won't throw anymore");<br> try {<br>
st.wait(-100);<br>
} catch(Exception e) {<br>
System.err.println("Here we did not get a callback to \
monitor_waited though different exception as above: " + e);<br> }<br>
<br>
System.err.println("\n\nJoinin<wbr>g so wait/waited will \
show");<br> try {<br>
st.join();<br>
} catch(Exception e) {<br>
System.err.println("Join exception, not intended for this test: \
" + e);<br> }<br>
}<br>
<br>
System.out.println("Done");<br>
}<br>
}<br>
<br>
class SecondThread extends Thread {<br>
private String name;<br>
<br>
public SecondThread(String name) {<br>
</div></div><a href="http://this.name" rel="noreferrer" target="_blank">this.name</a> \
<<a href="http://this.name" rel="noreferrer" \
target="_blank">http://this.name</a>> = name;<div><div class="h5"><br> }<br>
<br>
public void run() {<br>
try {<br>
Thread.sleep(5000);<br>
} catch(Exception e) {<br>
System.err.println(e);<br>
}<br>
System.err.println("Hello from " + name);<br>
}<br>
}<br>
<br>
C) The output I get is:<br>
Starting with simple wait<br>
Waiting!<br>
Waited!<br>
Here we got a monitor_waited call, the underlying monitor_wait callback threw: \
java.lang.IllegalArgumentExcep<wbr>tion: Exception from monitor_wait<br> <br>
<br>
Joining so wait/waited will show<br>
Waiting!<br>
Hello from first<br>
Waited!<br>
<br>
<br>
Starting with negative wait<br>
Waiting!<br>
Here we did not get a callback to monitor_waited though same exception as above: \
java.lang.IllegalArgumentExcep<wbr>tion: Exception from monitor_wait<br> <br>
<br>
Joining so wait/waited will show<br>
Waiting!<br>
Hello from second<br>
Waited!<br>
<br>
<br>
Starting with simple wait, monitor_wait won't throw anymore<br>
Waiting!<br>
Here we did not get a callback to monitor_waited though different exception as above: \
java.lang.IllegalArgumentExcep<wbr>tion: timeout value is negative<br> <br>
<br>
Joining so wait/waited will show<br>
Waiting!<br>
Hello from third<br>
Waited!<br>
Done<br>
<br>
<br></div></div><div><div class="h5">
On Mon, Sep 18, 2017 at 3:04 PM, David Holmes <<a \
href="mailto:david.holmes@oracle.com" target="_blank">david.holmes@oracle.com</a> \
<mailto:<a href="mailto:david.holmes@oracle.com" \
target="_blank">david.holmes@oracle.co<wbr>m</a>>> wrote:<br> <br>
Hi Jc,<br>
<br>
I found your example very difficult to follow. AFAICS you will<br>
potentially encounter monitor related events that are unrelated to<br>
the st.wait in your test code. And there are races in the test - as<br>
soon as st.wait releases the monitor lock then SecondThread can run,<br>
complete the sleep and print "Hello from A". I don't think it \
is<br>
specified exactly when the MONIOR_WAIT event is sent with respect to<br>
the releasing of the monitor lock.<br>
<br>
David<br>
<br>
<br>
On 19/09/2017 3:45 AM, JC Beyler wrote:<br>
<br>
Hi all,<br>
<br>
When looking at the documentation of<br>
<a href="https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#MonitorWaited" \
rel="noreferrer" target="_blank">https://docs.oracle.com/javase<wbr>/7/docs/platform/jvmti/jvmti.<wbr>html#MonitorWaited</a><br>
<<a href="https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#MonitorWaited" \
rel="noreferrer" target="_blank">https://docs.oracle.com/javas<wbr>e/7/docs/platform/jvmti/jvmti.<wbr>html#MonitorWaited</a>><br>
, I see the following description:<br>
"Sent when a thread finishes waiting on an object."<br>
<br>
However, there seems to be a slight issue when the MONITOR_WAIT<br>
event handler throws an exception.<br>
<br>
Consider the following code:<br>
<br>
A) The monitor wait handler throws<br>
<br>
static void JNICALL<br>
monitor_wait(jvmtiEnv* jvmti, JNIEnv *env,<br>
jthread thread, jobject object, jlong timeout) \
{<br> fprintf(stderr, "Waiting!\n");<br>
<br>
jclass newExcCls =<br>
env->FindClass("java/lang/Ille<wbr>galArgumentException");<br>
// Unable to find the new exception class, give up.<br>
if (newExcCls == 0) {<br>
return;<br>
}<br>
env->ThrowNew(newExcCls, "Exception from \
monitor_wait");<br> }<br>
<br>
B) The monitor waited handler does a printf:<br>
static void JNICALL<br>
monitor_waited(jvmtiEnv* jvmti, JNIEnv *env,<br>
jthread thread, jobject object, jboolean<br>
timed_out) {<br>
fprintf(stderr, "Waited!\n");<br>
}<br>
<br>
B) A second thread that will be asked to wait:<br>
class SecondThread extends Thread {<br>
public void run() {<br>
try {<br>
Thread.sleep(1);<br>
} catch(Exception e) {<br>
}<br>
System.out.println("Hello from A");<br>
}<br>
}<br>
<br>
C) The main thread with the wait:<br>
class Main extends Thread {<br>
public static void main(String[] args) {<br>
SecondThread st = new SecondThread();<br>
<br>
synchronized (st) {<br>
st.start();<br>
<br>
try {<br>
st.wait();<br>
} catch(InterruptedException e) {<br>
System.out.println("Exception caught!");<br>
}<br>
}<br>
<br>
System.out.println("Done");<br>
}<br>
}<br>
<br>
D) If I do this, what happens is that I get:<br>
<br>
Waiting!<br>
Waited!<br>
Exception in thread "main" \
java.lang.IllegalArgumentExcep<wbr>tion:<br> Exception from monitor_wait<br>
at java.lang.Object.wait(Native Method)<br>
at java.lang.Object.wait(Object.j<wbr>ava:502)<br>
at Main.main(Main.java:9)<br>
Hello from A<br>
<br>
- As we see, we get the print out from waiting, print out from<br>
waited and then the exception in the waiting.<br>
<br>
<br>
E) If instead, we do st.wait(-100) in the main method, we get:<br>
Waiting!<br>
Exception in thread "main" \
java.lang.IllegalArgumentExcep<wbr>tion:<br> Exception from monitor_wait<br>
at java.lang.Object.wait(Native Method)<br>
at Main.main(Main.java:9)<br>
Hello from A<br>
<br>
Notes: the stack is slightly different, the printout waited is<br>
no longer there however<br>
<br>
F) If finally, we don't throw in the waiting handler but leave<br>
the st.wait(-100) in place:<br>
Waiting!<br>
Exception in thread "main" \
java.lang.IllegalArgumentExcep<wbr>tion:<br> timeout value is negative<br>
at java.lang.Object.wait(Native Method)<br>
at Main.main(Main.java:9)<br>
Hello from A<br>
<br>
<br>
The question thus becomes: is this normal that there is a slight<br>
difference between D/E/F?<br>
<br>
Should we try to fix it to have a single behavior in the three<br>
cases, and if so, which would be the behavior that should be the<br>
default?<br>
<br>
Let me know if you would like to see a full code to easily<br>
replicate, I gave the big parts but left out the Agent_OnLoad<br>
method for example.<br>
<br>
Thanks!<br>
Jc<br>
<br>
<br>
</div></div></blockquote>
</blockquote></div><br></div>
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic