On 28/06/2025 4:03 am, Pavel Rappo wrote:
David,

Having re-read your replies multiple times, I think I now understand
it. It finally clicked after I read your "once notified it is no
longer waiting" for the Nth time. Indeed, even if a thread has not yet
restored its synchronization claims, it's no longer waiting. I
suppose, the same applies to interruption: once interrupted, the
thread is no longer waiting.

Correct. A "wait" places a thread in the monitor's wait-set. A notification, or interrupt, removes it from the wait-set, so that it can then reclaim the monitor (when it is available) and then return either normally or by throwing IE.

So here's my refined mental model. JLS says [^1] that an
implementation must not lose notifications or interruptions, even if
both occur at the same time. Obviously, this is on a best-efforts
basis because everything is racy.

Not sure what you are getting at here. The implementation synchronizes both actions such that it can always respond appropriately - actions become serialized internally.

It's easier not to lose interruptions than notifications. For an
interruption, either InterruptedException is thrown or the interrupted
status is set. For a notification (specifically, a notification
through Object.notify), the implementation must make sure that some
thread returns normally from Object.wait.

I think you need to look into the implementation, you're overthinking this somewhat. There's no inherent reason one is easier than the other.

If a selected thread cannot return normally because it has been
interrupted, the implementation selects another thread from the wait
set and checks if that thread can return normally, and so forth. This
continues until a suitable thread is found or there are no threads
left in the wait set. In which case notification will be missed.
Hence, best-efforts.

If there are no threads to notify then nothing is missed so that really isn't "best effort".

Once a thread has been removed from the wait-set due to interrupt, then notification never sees it. However, if a thread is removed from the wait-set due to notification and then looks at its interrupt state (which may have been set after the notification was processed) and decides to throw IE, then we would have to pass on the notification. But the simplest approach is to not check the interrupt state in that case. Hotspot gives preference to notification over interrupt, and implicitly to interrupt over timeout.

So, Object.wait *does not choose* to return normally from an
interruption to deliver a notification. It just happens so naturally.
The only thing that accounts for this edge case of simultaneous
notification-interruption is the iterative search for a thread to
notify.

Is my mental model accurate?

If the mental model fits the spec then it is accurate. The Hotspot implementation works in a different way.

David
-----

[^1]: https://docs.oracle.com/javase/specs/jls/se24/html/jls-17.html#jls-17.2.4

On Fri, Jun 27, 2025 at 1:11 PM Pavel Rappo <pavel.ra...@gmail.com> wrote:

So, Object.wait() guarantees to throw InterruptedException if the
interrupt status is set upon entrance, yes? Could this be added to
javadoc?

On Fri, Jun 27, 2025 at 12:59 PM David Holmes <david.hol...@oracle.com> wrote:

On 27/06/2025 5:58 pm, Pavel Rappo wrote:
David,

As you correctly identified, the edge case that I mentioned is this:
concurrent interrupt and notify [^1]. It has nothing to do with
spurious wake-ups or virtual threads. In fact, I've seen that with
virtual threads Object.wait behaves exactly the same way as with
platform threads in that regard. So I cannot report any issue here.

I'm only asking about the accuracy of the javadoc contract. Say, a
programmer uses this template from Object.wait's javadoc.

      synchronized (obj) {
          while ( <condition does not hold and timeout not exceeded> ) {
              long timeoutMillis = ... ; // recompute timeout values
              int nanos = ... ;
              obj.wait(timeoutMillis, nanos);
          }
          ... // Perform action appropriate to condition or timeout
      }

Obviously, the programmer handles InterruptedException outside this
template. Perhaps the programmer relies on that exception being thrown
on an interrupt. So they think of this code as of interruptible.

Now, if it is generally possible -- and not just in this edge case --
for Object.wait to return from an interrupt normally, the above code
is effectively uninterruptible. And the programmer does not know about
it. To fix that code, the programmer needs to additionally and
explicitly check the interrupt status.

The wait() will throw IE on the next loop if the condition is not met.
Otherwise the code proceeds with the knowledge the thread was notified,
irrespective of whether the thread was also interrupted "around the same
time".

David
-----

In contrast, j.u.c.Condition.await, makes a special provision, which
guarantees that if the method returns normally from an interrupt, it
will throw an exception on the next iteration of the
j.u.c.{Lock,Condition} analogue of the above template:

If the current thread:

has its interrupted status set on entry to this method;
or ...
then InterruptedException is thrown and the current thread's interrupted status 
is cleared.

[^1]: https://docs.oracle.com/javase/specs/jls/se24/html/jls-17.html#jls-17.2.4

On Fri, Jun 27, 2025 at 3:47 AM David Holmes <david.hol...@oracle.com> wrote:

Hi Pavel,

On 27/06/2025 8:23 am, Pavel Rappo wrote:
Here's an interesting behaviour. A thread that has been blocked in
Object.wait is interrupted. But instead of throwing an
InterruptedException, Object.wait returns normally with the thread's
interrupt status set.

Do you mean it returns normally without being notified? That's allowed
by spec as it is just a spurious wakeup.

That said, it shouldn't really happen in the current implementation
AFAIK, though with recent changes around virtual thread support it is
possible some new edge case has crept in. Or a bug.

If the interrupt is racing with a notification then what you see is also
allowed as you can't tell which occurred first:

"If the current thread is interrupted by any thread before or while it
is waiting, ...

once notified it is no longer waiting.

This behaviour is not mentioned in javadoc, yet I witnessed it.
Granted, I could only trigger it in an edge case described in JLS. I

What edge case is described in current JLS?

wonder if the behaviour is reserved for that edge case, or it can be
observed elsewhere and there is a genuine gap in javadoc. FWIW,
javadoc for j.u.c.Condition documents this behaviour.

Here's a practical implication of this. If some code that uses
Object.wait wants to be interruptible, it must not rely on Object.wait
to throw InterruptedException. It should check interrupt status or
call other methods that are guaranteed to throw InterruptedException
on thread interruption.

In typical usage though if you were notified as well then responding to
that is generally fine and is what would happen if the notification
definitely occurred before the interrupt. If it was a spurious wakeup
then you should be re-waiting in a loop and the next wait will throw the
IE. So I think no real code would be adversely impacted by these edge cases.

Cheers,
David
-----

-Pavel



Reply via email to