Object.wait returns normally if interrupted

Pavel Rappo pavel.rappo at gmail.com
Sat Jun 28 07:09:54 UTC 2025


I appreciate your patience in correcting my misunderstandings; thanks, David.


On Sat, Jun 28, 2025 at 5:53 AM David Holmes <david.holmes at oracle.com> wrote:
>
> 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.rappo at 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.holmes at 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.holmes at 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
> >>>>>
> >>>
>


More information about the core-libs-dev mailing list