[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&#39;ll come back if I have more \
questions but I think I&#39;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">&lt;<a href="mailto:david.holmes@oracle.com" \
target="_blank">david.holmes@oracle.com</a>&gt;</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&#39;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&#39;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&#39;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&#39;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 &lt;jvmti.h&gt;<br>
#include &lt;stdio.h&gt;<br>
#include &lt;string.h&gt;<br>
<br>
static void JNICALL<br>
monitor_wait(jvmtiEnv* jvmti, JNIEnv *env,<br>
                      jthread thread, jobject object, jlong timeout) {<br>
     fprintf(stderr, &quot;Waiting!\n&quot;);<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 &amp;&amp; cnt &lt; 4) {<br>
        jclass newExcCls = \
env-&gt;FindClass(&quot;java/lang/Ille<wbr>galArgumentException&quot;);<br>  // \
Unable to find the new exception class, give up.<br>  if (newExcCls == 0) {<br>
           return;<br>
        }<br>
        env-&gt;ThrowNew(newExcCls, &quot;Exception from monitor_wait&quot;);<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, &quot;Waited!\n&quot;);<br>
}<br>
<br>
extern &quot;C&quot; JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char \
                *options,<br>
                                                                         void \
*reserved) {<br>  int ernum;<br>
<br>
     jvmtiEnv *jvmti;<br>
     if (jvm-&gt;GetEnv(reinterpret_cast&lt;<wbr>void **&gt;(&amp;jvmti), \
JVMTI_VERSION) != JNI_OK) {<br>  return JNI_ERR;<br>
     }<br>
<br>
     jvmtiEventCallbacks callbacks;<br>
     memset(&amp;callbacks, 0, sizeof(callbacks));<br>
     callbacks.MonitorWait = &amp;monitor_wait;<br>
     callbacks.MonitorWaited = &amp;monitor_waited;<br>
<br>
     jvmtiCapabilities caps;<br>
     memset(&amp;caps, 0, sizeof(caps));<br>
     caps.can_generate_monitor_even<wbr>ts = 1;<br>
<br>
     ernum = jvmti-&gt;AddCapabilities(&amp;caps);<br>
     ernum = jvmti-&gt;SetEventCallbacks(&amp;call<wbr>backs, \
                sizeof(jvmtiEventCallbacks));<br>
     ernum = jvmti-&gt;SetEventNotificationMod<wbr>e(JVMTI_ENABLE, \
                JVMTI_EVENT_MONITOR_WAIT, NULL);<br>
     ernum = jvmti-&gt;SetEventNotificationMod<wbr>e(JVMTI_ENABLE, \
JVMTI_EVENT_MONITOR_WAITED, NULL);<br> <br>
     return ernum;<br>
}<br>
<br>
B) Here is the Java code, it&#39;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 &lt; 3; i++) {<br>
           SecondThread st = new SecondThread(&quot;first&quot;);<br>
<br>
        synchronized (st) {<br>
           st.start();<br>
<br>
           System.err.println(&quot;Starting with simple wait&quot;);<br>
           try {<br>
              st.wait();<br>
           } catch(Exception e) {<br>
              System.out.println(&quot;Here we got a monitor_waited call, the \
underlying monitor_wait callback threw: &quot; + e);<br>  }<br>
<br>
           System.err.println(&quot;\n\nJoinin<wbr>g so wait/waited will \
show&quot;);<br>  try {<br>
              st.join();<br>
           } catch(Exception e) {<br>
              System.err.println(&quot;Join exception, not intended for this test: \
&quot; + e);<br>  }<br>
        }<br>
<br>
        st = new SecondThread(&quot;second&quot;);<br>
<br>
        synchronized (st) {<br>
           st.start();<br>
<br>
           System.err.println(&quot;\n\nStarti<wbr>ng with negative wait&quot;);<br>
           try {<br>
              st.wait(-100);<br>
           } catch(Exception e) {<br>
              System.err.println(&quot;Here we did not get a callback to \
monitor_waited though same exception as above: &quot; + e);<br>  }<br>
<br>
           System.err.println(&quot;\n\nJoinin<wbr>g so wait/waited will \
show&quot;);<br>  try {<br>
              st.join();<br>
           } catch(Exception e) {<br>
              System.err.println(&quot;Join exception, not intended for this test: \
&quot; + e);<br>  }<br>
        }<br>
<br>
        st = new SecondThread(&quot;third&quot;);<br>
<br>
        synchronized (st) {<br>
           st.start();<br>
<br>
           System.err.println(&quot;\n\nStarti<wbr>ng with simple wait, monitor_wait \
won&#39;t throw anymore&quot;);<br>  try {<br>
              st.wait(-100);<br>
           } catch(Exception e) {<br>
              System.err.println(&quot;Here we did not get a callback to \
monitor_waited though different exception as above: &quot; + e);<br>  }<br>
<br>
           System.err.println(&quot;\n\nJoinin<wbr>g so wait/waited will \
show&quot;);<br>  try {<br>
              st.join();<br>
           } catch(Exception e) {<br>
              System.err.println(&quot;Join exception, not intended for this test: \
&quot; + e);<br>  }<br>
        }<br>
<br>
        System.out.println(&quot;Done&quot;);<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> \
&lt;<a href="http://this.name" rel="noreferrer" \
target="_blank">http://this.name</a>&gt; = 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(&quot;Hello from &quot; + 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&#39;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 &lt;<a \
href="mailto:david.holmes@oracle.com" target="_blank">david.holmes@oracle.com</a> \
&lt;mailto:<a href="mailto:david.holmes@oracle.com" \
target="_blank">david.holmes@oracle.co<wbr>m</a>&gt;&gt; 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 &quot;Hello from A&quot;. I don&#39;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>
  &lt;<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>&gt;<br>
  , I see the following description:<br>
            &quot;Sent when a thread finishes waiting on an object.&quot;<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, &quot;Waiting!\n&quot;);<br>
<br>
                   jclass newExcCls =<br>
            env-&gt;FindClass(&quot;java/lang/Ille<wbr>galArgumentException&quot;);<br>
  // Unable to find the new exception class, give up.<br>
                   if (newExcCls == 0) {<br>
                      return;<br>
                   }<br>
                   env-&gt;ThrowNew(newExcCls, &quot;Exception from \
monitor_wait&quot;);<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, &quot;Waited!\n&quot;);<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(&quot;Hello from A&quot;);<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(&quot;Exception caught!&quot;);<br>
                         }<br>
                      }<br>
<br>
                      System.out.println(&quot;Done&quot;);<br>
                   }<br>
            }<br>
<br>
            D) If I do this, what happens is that I get:<br>
<br>
            Waiting!<br>
            Waited!<br>
            Exception in thread &quot;main&quot; \
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 &quot;main&quot; \
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&#39;t throw in the waiting handler but leave<br>
            the st.wait(-100) in place:<br>
            Waiting!<br>
            Exception in thread &quot;main&quot; \
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