[concurrency-interest] Propagation of signals to non-interrupted thread

David Holmes davidcholmes at aapt.net.au
Sun Nov 13 16:45:30 EST 2011


Dr Heinz M. Kabutz writes:
> Yes, the spurious wakeup is definitely part of the game.  One of my

The park/unpark scenario is somewhat different to the wait/await situation,
though.

Also a "spurious wakeup" does not refer to the case of some other thread
invoking unpark or signal/notify, but of the implementation doing so. Of
course the same coding practices protect against both.

> customers had a system that basically imploded in Java 6 because they
> happened frequently under heavy load.

That is not just interesting (as Martin put it) but somewhat disturbing, as
even when permitted they should be extremely rare. I'd like to hear more on
this if you can send some details to me.

> I agree with David's assessment that it is much easier to convince
> people to use a while() loop to test their condition predicate if they
> know that "spurious wakeups" might occur.

The basic requirement for looping on the predicate is easily explained by
"the condition may have have changed again by the time you can respond to
it" - ala the two threads taking example. The "spurious wakeup" argument
comes into play when someone asks about specialized cases like single
producer with single consumer "can't I just use if in that case?".

Cheers,
David
-----

> For many years I tried to
> explain during courses that this is wrong:
>
> public synchronized void put(Object o) {
>   queue.add(o);
>   notify();
> }
>
> public synchronized Object take() throws InterruptedException {
>   if (queue.isEmpty()) {
>     wait();
>   }
>   return queue.remove(0);
> }
>
> My argument was:  Thread 1 calls take, queue is empty and is parked with
> wait() and thus lets go of the lock.  Thread 2 comes along, gets the
> lock, adds an element and calls notify(), which thus wakes up thread 1.
> However, thread 1 cannot proceed until he has the lock again.  Thread 2
> now returns the lock, but at that instant, Thread 3 comes along and
> calls take().  Finding the queue non-empty, it removes the first element
> and returns.  Thread 1 now finally gets the lock and calls
> queue.remove(0), which results in a NoSuchElementException.
>
> A long argument, which is much easier to argue with spurious wakeups
> :-)  "Thread 1 is waiting, but can wake up due to a spurious signal.
> These things really do happen in production.  Any questions?"
>
> Some of my customers are still using Java 1.4.2.  Do we need to
> propagate the signal if we use low-level synchronized?  I would feel
> most comfortable even doing this in Java 5+, even if it is not
> necessary, since the take() method needs to cope with spurious signals
> anyway.
>
> public synchronized Object take() throws InterruptedException {
>   while (queue.isEmpty()) {
>     try {
>       wait();
>     } catch(InterruptedException ex) {
>       notify();
>       throw ex;
>     }
>   }
>   return queue.remove(0);
> }
>
>
> Regards
>
> Heinz
> --
> Dr Heinz M. Kabutz (PhD CompSci)
> Author of "The Java(tm) Specialists' Newsletter"
> Sun Java Champion
> IEEE Certified Software Development Professional
> http://www.javaspecialists.eu
> Tel: +30 69 72 850 460
> Skype: kabutz
>
>
>
> On 11/11/11 7:00 PM, David M. Lloyd wrote:
> > Absolutely.  Spurious wakeups are an indelible part of the
> > park()/unpark() contract, and in fact can make coding certain things
> > possible or at least much easier.  Never, ever, ever, ever assume that
> > they can't happen, no matter what you hear from anyone.
> >
> > On 11/11/2011 10:50 AM, Nathan Reynolds wrote:
> >> Does Solaris/SPARC or some other platform still cause spurious wakeups?
> >> Or is this email chain saying that has been fixed?
> >>
> >> Spurious wakeups should always be considered possible even if the
> >> hardware and JVM don't allow it. LockSupport.unpark() can be called by
> >> any thread in any piece of code. It has a globally accessible flag. One
> >> piece of code could call unpark() and cause what seems to be a
> >> "spurious" wakeup in another piece of code.
> >>
> >> For example, let's say some buggy code allows for two threads
> (A & B) to
> >> call LockSupport.unpark() on the same thread (C). Thread A calls
> >> unpark(). Thread C wakes up exits that piece of code and then goes into
> >> another piece of code. Thread C then calls LockSupport.park(). Thread B
> >> gets some CPU time and calls LockSupport.unpark() on Thread C. Thread C
> >> now wakes up and thinks it has been signaled correctly. This problem is
> >> really tough to debug because the two pieces of code are completely
> >> unrelated except via LockSupport.
> >>
> >> Coding for spurious wakeups (i.e. rechecking the condition) is always a
> >> good thing. Sure the piece of code you are looking at could be perfect.
> >> But, some other piece of code which you have never seen could be doing
> >> things in a way that breaks your code.
> >>
> >> Nathan Reynolds
> >> <http://psr.us.oracle.com/wiki/index.php/User:Nathan_Reynolds> |
> >> Consulting Member of Technical Staff | 602.333.9091
> >> Oracle PSR Engineering <http://psr.us.oracle.com/> | Server Technology
> >>
> >> On 11/11/2011 8:36 AM, Tim Peierls wrote:
> >>> This just bit me. (I never really followed the discussions that
> >>> resulted in that documentation strategy.) I suggested some code for
> >>> someone that I claimed was not ready to use because it didn't handle
> >>> spurious wakeups. I now think in fact it /was/ ready to use. Better
> >>> safe than sorry, of course, but it would have been nicer to read that
> >>> await was not prone to spurious wakeups along with a reminder that in
> >>> many cases await should be called in a loop to check the condition.
> >>>
> >>> --tim
> >>>
> >>> On Thu, Nov 10, 2011 at 5:56 PM, David Holmes
> >>> <davidcholmes at aapt.net.au <mailto:davidcholmes at aapt.net.au>> wrote:
> >>>
> >>>     Martin,
> >>>     As discussed in the past the reason for not
> >>>     documenting/guaranteeing "no spurious wakeups" is that it would
> >>>     encourage people to use await() in an if-statement rather than a
> >>>     loop, which would often be incorrect even without spurious
> >>>     wakeups. It is a safer multi-threaded world if programmers believe
> >>>     that spurious wakeups are lying in wait (pun intended) and so are
> >>>     always accounted for - this will instil safe programming practice
> >>>     across all the main threading systems.
> >>>     Cheers,
> >>>     David
> >>>
> >>>         -----Original Message-----
> >>>         *From:* concurrency-interest-bounces at cs.oswego.edu
> >>>         <mailto:concurrency-interest-bounces at cs.oswego.edu>
> >>>         [mailto:concurrency-interest-bounces at cs.oswego.edu
> >>>         <mailto:concurrency-interest-bounces at cs.oswego.edu>]*On Behalf
> >>>         Of *Martin Buchholz
> >>>         *Sent:* Friday, 11 November 2011 12:07 AM
> >>>         *To:* Dr Heinz M. Kabutz
> >>>         *Cc:* concurrency-interest at cs.oswego.edu
> >>>         <mailto:concurrency-interest at cs.oswego.edu>
> >>>         *Subject:* Re: [concurrency-interest] Propagation of signals
> >>>         tonon-interrupted thread
> >>>
> >>>         These classes are designed to work with JDK6 as well as JDK7.
> >>>
> >>>         AbstractQueuedSynchronizer has been written so that interrupts
> >>>         can never cause signals to be lost. If this were not the case,
> >>>         there would be many more places than just in
> >>>         ArrayBlockingQueue where special handling of interrupts would
> >>>         be required (and getting concurrent classes right is already
> >>>         hard enough). Implementation of ABQ has merely been simplified
> >>>         to rely on AQS's de-facto guarantee.
> >>>
> >>>         This ought to be better documented.
> >>>
> >>>         I would like to see the classes in j.u.c.locks document more
> >>>         guarantees than they currently do.
> >>>         For example, I'd like to see this guarantee:
> >>>
> >>>         diff -u -r1.96 ReentrantLock.java
> >>>         --- main/java/util/concurrent/locks/ReentrantLock.java9 Jun
> >>>         2011 07:48:44 -00001.96
> >>>         +++ main/java/util/concurrent/locks/ReentrantLock.java10 Nov
> >>>         2011 14:00:08 -0000
> >>>         @@ -467,6 +467,9 @@
> >>>         * but for <em>fair</em> locks favors those threads that have
> >>> been
> >>>         * waiting the longest.
> >>>         *
> >>>         + * <li>None of the condition {@linkplain Condition#await()
> >>>         waiting}
> >>>         + * methods ever return due to a "<em>spurious
> >>>         wakeup</em>".
> >>>         + *
> >>>         * </ul>
> >>>         *
> >>>         * @return the Condition object
> >>>
> >>>         Martin
> >>>
> >>>         On Thu, Nov 10, 2011 at 05:15, Dr Heinz M. Kabutz
> >>>         <heinz at javaspecialists.eu <mailto:heinz at javaspecialists.eu>>
> >>>         wrote:
> >>>
> >>>             In Java 6, the ArrayBlockingQueue used this construct for
> >>>             the take() method:
> >>>
> >>>             public E take() throws InterruptedException {
> >>>             final ReentrantLock lock = this.lock;
> >>>             lock.lockInterruptibly();
> >>>             try {
> >>>             try {
> >>>             while (count == 0)
> >>>             notEmpty.await();
> >>>             } catch (InterruptedException ie) {
> >>>             notEmpty.signal(); // propagate to non-interrupted thread
> >>>             throw ie;
> >>>             }
> >>>             E x = extract();
> >>>             return x;
> >>>             } finally {
> >>>             lock.unlock();
> >>>             }
> >>>             }
> >>>
> >>>             In other words, it would /always/ send a signal on
> >>>             interrupt, even if it had not received one.
> >>>
> >>>             In Java 7, this was taken away, so we now have:
> >>>
> >>>             public E take() throws InterruptedException {
> >>>             final ReentrantLock lock = this.lock;
> >>>             lock.lockInterruptibly();
> >>>             try {
> >>>             while (count == 0)
> >>>             notEmpty.await();
> >>>             return extract();
> >>>             } finally {
> >>>             lock.unlock();
> >>>             }
> >>>             }
> >>>
> >>>             However, I could not find substantial differences between
> >>>             the await() methods of Java 6 and 7. Does this mean that
> >>>             propagating of the signal was not necessary in Java 6
> >>>             either? According to Doug Lea's book section 3.2.4.2, it
> >>>             is necessary with wait/notify to propagate the signal if
> >>>             you get interrupted. However, it looks like
> >>>             Condition.await() is coded to cater for this eventuality
> >>>             anyway.
> >>>
> >>>             Regards
> >>>
> >>>             Heinz
> >>>             --
> >>>             Dr Heinz M. Kabutz (PhD CompSci)
> >>>             Author of "The Java(tm) Specialists' Newsletter"
> >>>             Sun Java Champion
> >>>             IEEE Certified Software Development Professional
> >>>             http://www.javaspecialists.eu
> >>>             Tel: +30 69 72 850 460 <tel:%2B30%2069%2072%20850%20460>
> >>>             Skype: kabutz
> >>>             _______________________________________________
> >>>             Concurrency-interest mailing list
> >>>             Concurrency-interest at cs.oswego.edu
> >>>             <mailto:Concurrency-interest at cs.oswego.edu>
> >>>             http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>>
> >>>
> >>>
> >>>     _______________________________________________
> >>>     Concurrency-interest mailing list
> >>>     Concurrency-interest at cs.oswego.edu
> >>>     <mailto:Concurrency-interest at cs.oswego.edu>
> >>>     http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>>
> >>>
> >>>
> >>>
> >>> _______________________________________________
> >>> Concurrency-interest mailing list
> >>> Concurrency-interest at cs.oswego.edu
> >>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>
> >>
> >> _______________________________________________
> >> Concurrency-interest mailing list
> >> Concurrency-interest at cs.oswego.edu
> >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >
> >
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>



More information about the Concurrency-interest mailing list