[concurrency-interest] Re: interrupt / notify races even in 6.0 beta?

Bart Jacobs bart.jacobs at cs.kuleuven.ac.be
Tue May 16 10:35:51 EDT 2006


Dawid's test case has the special circumstance that the notify() and the 
interrupt() occur in the same thread. The question is: are both of these 
calls considered to occur "while [the target thread is] waiting" or not?

Arguments pro:
- There is no synchronizes-with edge from the wait to the 
notify()/interrupt() call.
- Section 17.8.1 says:

Each thread must determine an order over the events that could cause it 
to be removed from a wait set. That order does not have to be consistent 
with other orderings, but the thread must behave as though those events 
occurred in that order.

So the order does not have to be consistent with program order.

Argument contra:
- A reader could be forgiven for thinking that after a 
notify()/interrupt() call, the target thread is not longer considered to 
be waiting, and therefore, operations that happen-after the 
notify()/interrupt() call do not occur "while [the target thread is] 
waiting" and "could [not] cause it to be removed from a wait set". As a 
result, neither 17.8.1 nor 17.8.4 would apply in the first place.

So it seems it would help if the JLS clarified the meaning of "while 
waiting" (both for 17.8.1 and 17.8.4), by saying that the thread "stops 
waiting" sometime after the notify()/interrupt() call, and therefore is 
still considered to "be waiting" until such time.

Bart

Doug Lea wrote:

> First, note that the JLS-3 specs allow some non-determinism here. See
>   http://java.sun.com/docs/books/jls/third_edition/html/memory.html
> Which says...
> [Sec 17.8.4]
>   The above specifications allow us to determine several properties 
> having to do
>   with the interaction of waits, notification and interruption. If a 
> thread is
>   both notified and interrupted while waiting, it may either:
>
>     * return normally from wait, while still having a pending 
> interrupt (in other works, a call to Thread.interrupted would return 
> true)
>     * return from wait by throwing an InterruptedException
>
>   The thread may not reset its interrupt status and return normally 
> from the
>   call to wait.
>
>   Similarly, notifications cannot be lost due to interrupts. Assume 
> that a set s
>   of threads is in the wait set of an object m, and another thread 
> performs a
>   notify on m. Then either
>
>     * at least one thread in s must return normally from wait, or
>     * all of the threads in s must exit wait by throwing 
> InterruptedException
>
>   Note that if a thread is both interrupted and woken via notify, and 
> that
>   thread returns from wait by throwing an InterruptedException, then 
> some other
>   thread in the wait set must be notified.
> [BTW: notice the typo "in other works"!]
>
> This boils down to saying that there are two forbidden outcomes:
>   1. A thread woken up via notify does not "lose" its interrupt status.
>   2. An InterruptedException does not "lose" the fact there is a
>      pending notification (that may wake up some other waiting thread).
>
> These are the "bugs" fixed in 1.5 JVMs in accord with JSR133 specs.
>
> I just threw together a little test program (attached) to verify that
> #1 does not happen. It doesn't on the JVMs/platforms I've checked.
> (This is not a pretty program because it has to
> use spins/yields to advance the threads so as not to contaminate
> with additional locks, waits, or sleeps.)
>
> However, it is the case that on all platforms I've tried, sometimes
> the "notifyBeforeInterrupt" method says it was interrupted before
> notified, and vice versa. This reflects the unavoidable race here,
> that will come out one way or the other depending on exactly what
> the target thread is doing when signaller invokes notify or interrupt.
>
> (There are also some tests for #2 around developed during JSR133,
> but I'm not sure where they are.)
>
>
> -Doug
>
>------------------------------------------------------------------------
>
>public class T516 extends Thread {
>    static final int NTESTS = 100;
>    static final int ITERS_PER_TEST = 50;
>    static final int NORMAL = 1;
>    static final int INTERRUPTED = 2;
>    volatile int waitStatus;
>    volatile int exitStatus;
>    volatile boolean started;
>    volatile boolean stop;
>    final Object lock = new Object();
>
>    public void run() {
>        started = true;
>        synchronized(lock) {
>            try {
>                lock.wait(); 
>                waitStatus = NORMAL;
>            } catch (InterruptedException e) {
>                waitStatus = INTERRUPTED;
>            }
>        }
>        while (!stop) 
>            Thread.yield();
>        exitStatus = interrupted()? INTERRUPTED : NORMAL;
>    }
>
>    public static void main(String[] args) throws Exception {
>        for (int i = 0; i < NTESTS; ++i) {
>            interruptBeforeNotify();
>            notifyBeforeInterrupt();
>        }
>    }
>
>    static void interruptBeforeNotify() throws Exception {
>        for (int i = 0; i < ITERS_PER_TEST; i++) {
>            T516 t = new T516();
>            t.start();
>            while (!t.started)
>                Thread.yield();
>            t.interrupt();
>            synchronized (t.lock) { t.lock.notify(); }
>            while(t.waitStatus == 0)
>                Thread.yield();
>            t.stop = true;
>            while(t.exitStatus == 0)
>                Thread.yield();
>            int r = t.waitStatus;
>            int e = t.exitStatus;
>            if (r == NORMAL && e == NORMAL) 
>                throw new Error("Notified but not interrupted");
>            else if (r == NORMAL) System.out.print("n");
>            else if (r == INTERRUPTED) System.out.print("i");
>            else throw new Error("Cannot happen");
>            t.join();
>        }
>
>        System.out.println();
>    }
>
>    
>    static void notifyBeforeInterrupt() throws Exception {
>        for (int i = 0; i < ITERS_PER_TEST; i++) {
>            T516 t = new T516();
>            t.start();
>            while (!t.started)
>                Thread.yield();
>            synchronized (t.lock) { t.lock.notify(); }
>            t.interrupt();
>            while(t.waitStatus == 0)
>                Thread.yield();
>            t.stop = true;
>            while(t.exitStatus == 0)
>                Thread.yield();
>            int r = t.waitStatus;
>            int e = t.exitStatus;
>            if (r == NORMAL && e == NORMAL) 
>                throw new Error("Notified but not interrupted");
>            else if (r == NORMAL) System.out.print("N");
>            else if (r == INTERRUPTED) System.out.print("I");
>            else throw new Error("Cannot happen");
>            t.join();
>        }
>        System.out.println();
>    }
>}
>
>
>
>  
>
>------------------------------------------------------------------------
>
>_______________________________________________
>Concurrency-interest mailing list
>Concurrency-interest at altair.cs.oswego.edu
>http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
>  
>


Disclaimer: http://www.kuleuven.be/cwis/email_disclaimer.htm



More information about the Concurrency-interest mailing list