RE: JavaMemoryModel: Waits, Interrupts and Notify-s

From: Sylvia Else (sylviae@optushome.com.au)
Date: Mon May 26 2003 - 03:06:11 EDT


>My view is that whatever the VM does to ensure no lost notification,
>shouldn't produce any observable change in behaviour that would not
>have been observable (as per the spec) if the notification had been
>received by an alternate thread in the first place. Notwithstanding
>any scheduling order differences that no program should be relying
>upon anyway.

Ok. We agree on that.

However, we have seen that wait/notify/interrupt is an area where the
implementations have not always conformed to the specification, even though
it appears that they could have. In most areas of IT I think one would
simply consider the implementations to be broken, and fix them. In this
particular corner, the view seems to be that we change the specification on
the grounds that it's easier, and existing programs have lived with
implementations that didn't conform to the old specification.

I still have misgivings about the validity of that argument, but at least
lets make sure the practice ends here. With this in mind, I've included the
following code that I think illustrates a non-obvious interaction between
notification propagation and thread scheduling. Expert VM implementors can
obviously ignore it as trivial.

public class Test39
{
     static volatile Test39 m;
     static volatile int waitCount = 0;
     static volatile boolean thread1Woken = false;
     static volatile boolean thread2Woken = false;

     public static void main(String args[])
     {
         m = new Test39();

         // Start a couple of threads.
         Thread t1 = new TestThread(1);
         t1.start();

         new TestThread(2).start();

         // Wait until both threads are in the wait() method.
         while(waitCount < 2)
             Thread.yield();

         synchronized(m)
         {
             // Notify the monitor. This must wake up either thread 1 or
             // thread 2.
             m.notify();

             // Start thread 3.
             new TestThread(3).start();
         }

         // Thread 3 may have now reached its wait call.

         // Interrupt thread 1. If thread 1 was woken up above, but will
throw Interrupted
         // Exception as a result of this call, then thread 2 must be woken
up - not
         // thread 3!
         t1.interrupt();

         // So this loop is guaranteed to terminate.
         while(!thread1Woken && !thread2Woken)
             Thread.yield();

         System.err.println("main method terminating.");
     }

     static class TestThread extends Thread
     {
         private final int id;

         TestThread(int id)
         {
             this.id = id;
         }

         public void run()
         {
             synchronized(m)
             {
                 waitCount++;
                 try
                 {
                     System.err.println("Thread " + id + " running.");

                     m.wait();

                     System.err.println("Thread " + id + " woke up;
interrupted = " + Thread.interrupted());

                     if(id == 1)
                         thread1Woken = true;
                     else if(id == 2)
                         thread2Woken = true;
                 }
                 catch (InterruptedException ex)
                 {
                     System.err.println("Thread " + id + " interrupted.");
                 }
             }
         }
     }
}

The issue here is that an implementation that propagates a notification at
the point where Thread.interrupt() is called, or at any later time, risks
waking up thread 3 rather than thread 2. If that happens, then thread 1 is
interrupted, thread 3 is woken up, but thread 2 is left waiting. This
combination of events is not permitted by the specification even where
wait() is allowed to wake up spontaneously.

One solution is to ensure that threads are woken up in the order in which
they wait, but I can't say whether there's another correct program that
won't fail.

It's this sort of scenario that makes me uncomfortable about fudges that
provide supposedly logically equivalent behaviours. It's too easy to get it
wrong, and the fact that the existing implementations don't work correctly
gives me no confidence that the VM writers will really provide correct
implementations. Then we'll come back in a few years, and tweak the
specifications again.

Sylvia.

-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:45 EDT