I appreciate your patience in correcting my misunderstandings; thanks, David.
On Sat, Jun 28, 2025 at 5:53 AM David Holmes <david.hol...@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.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 > >>>>> > >>> >