6

First, this is a near duplicate of: How to differentiate when wait(long timeout) exit for notify or timeout?

But it is a new follow-on question.

Having this wait declaration:

public final native void wait(long timeout) throws InterruptedException;

It could exit by InterruptedException, or by timeout, or because Notify/NotifyAll method was called in another thread, Exception is easy to catch but...

My code absolutely needs to know if the exit was from timeout or notify. (In the future, this code needs to be redesigned, but that cannot be done now. So I need to know the reason for the exit from wait.)

Specifically, can someone give an example of using a ThreadLocal Boolean that is set to true only on notify() and where all this is inside an existing loop as shown below? (This was more or less the accepted answer in the other thread, but no specific code example was given. I'm not all that familiar with Java, so I need a specific code example -- ideally in the context of the existing code below.)

public synchronized int getLastSequenceNumber() {
    while (empty) {
        try {
            wait(waitTimeValue);
        } catch (InterruptedException e) {}
    }
    empty = true;
    return reportedSequenceNumber;
}
public synchronized void reconcileLastSequenceNumber(int sequenceNumber) {
    empty = false;
    this.reportedSequenceNumber = sequenceNumber;
    notifyAll();
}

the Boolean "empty" serves a purpose outside of the specific question I'm asking here. I believe I will need to add another Boolean to fulfill the suggested answer from the original question. How would I integrate that proposed solution into the existing code snippet above? Thanks.

Community
  • 1
  • 1
MountainX
  • 6,217
  • 8
  • 52
  • 83
  • You're asking for a queue alike behavior. Check java.util.concurrent.BlockingQueue and any of its implementation. – bestsss May 24 '11 at 23:49

4 Answers4

8

You might be better off using a Condition (and its await method) rather than built-in monitors, because await returns a boolean value indicating whether the wait timed out.

And even then, you must beware of spurious wakeup (which is indistinguishable from a call to signal.)

finnw
  • 47,861
  • 24
  • 143
  • 221
  • Looks interesting. I'm reading the Javadoc on Condition now. When I finish reading, if it seem appropriate, I will try it. Looks like it will take me some time to figure it out... – MountainX May 24 '11 at 17:32
3

You should be using a loop as you currently are anyway, regardless of knowing whether the wait timed out - partly due to the possibility of spurious wakeups. However, I'm not at all sure that you really need to know whether the call exited due to notification or not.

Consider the situation where the notification occurs a nanosecond before the timeout vs the situation where the notification occurs a nanosecond after the timeout. What's the useful difference between the two? Fundamentally there's a race condition if the two occur at "about the same time".

As far as I can tell, wait() really doesn't let you tell whether the call timed out or not, but it shouldn't affect your code. You should be looping and testing something else that is a side-effect of the notification anyway.

It's not clear to me where a ThreadLocal would come into play to be honest - that's exactly the opposite of what you want if you need to be able to tell from the waiting thread whether the notifying the thread has reached a certain point. I don't think you need an extra variable at all - your empty is fine.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Jon, I have a lot of respect for you, but I specifically said that "My code absolutely needs to know if the exit was from timeout or notify." This is not a matter of timing. It is a matter of logic and it holds true regardless of how small a fraction of time might separate the causes. – MountainX May 24 '11 at 17:30
  • @MoutainX: Yes, you *claim* you need to know it. But I'm not convinced. What happens if the timeout occurs *while* another thread is in `reconcileLastSequenceNumber` but before the `notify()` call? Is that *genuinely* different to the timeout occurring immediately *after* the `notify()` call? What's the difference? If you can explain why you believe the difference is crucial, that may suggest another way round - but I *suspect* that while justifying your original belief, you'll find that you already have the information you need elsewhere (e.g. due to `empty`)? – Jon Skeet May 24 '11 at 17:33
1

This is an expanded version based on Jenkov's signal class. An exception is raised if it does not end with a Notify. Thought it might help as I ran into the same problem.

public class MonitorObject{
 }

 public class Signal{

     MonitorObject myMonitorObject = new MonitorObject();
     boolean wasSignalled = false;

     public void doWait(int timeOut) throws InterruptedException,TimeoutException{
         synchronized(myMonitorObject){
             long startTime = System.currentTimeMillis();
             long endTime = startTime + timeOut;
             Log.d(TAG, String.format("MonitorStart time %d",startTime));

             while(!wasSignalled){
                 long waitTime = endTime - System.currentTimeMillis();
                 if(waitTime > 0) 
                     myMonitorObject.wait(waitTime);        
                 else{
                     Log.e(TAG, String.format("Monitor Exit timeout error"));
                     throw new TimeoutException();
                 }       
             }

             Log.d(TAG, String.format("MonitorLoop Exit currentTime=%d EndTime=%d",System.currentTimeMillis(),startTime + timeOut));
             //Spurious signal so clear signal and continue running.
             wasSignalled = false;
         }
     }

     public void doNotify(){
         synchronized(myMonitorObject){
             wasSignalled = true;
             myMonitorObject.notify();
         }
     }
 } 
TLama
  • 75,147
  • 17
  • 214
  • 392
redevill
  • 341
  • 1
  • 3
  • 9
1

There's no direct way to report this with the builtin monitor API, but you could replace the wait() and other functions with a new implementation that tracks this explicitly (untested):

private int wait_ct = 0, signal_ct = 0;

public void checkedNotifyAll() {
  synchronized {
    signal_ct = wait_ct;
    notifyAll();
  }
}

public void checkedNotify() {
  synchronized {
    signal_ct++;
    if (signal_ct > wait_ct)
      signal_ct = wait_ct;
    notify();
}

// Returns true if awoken via notify
public boolean waitChecked(long timeout, int nanos) throws InterruptedException {
  synchronized(this) {
    try {
      wait_ct++;
      super.wait(timeout, nanos);
      if (signal_ct > 0) {
        signal_ct--;
        return true;
      }
      return false;
    } finally {
      wait_ct--;
      if (signal_ct > wait_ct) signal_ct = wait_ct;
      notify(); // in case we picked up the notify but also were interrupted
    }
}

// Note: Do not combine this with normal wait()s and notify()s; if they pick up the signal themselves
// the signal_ct will remain signalled even though the checkedWait()s haven't been
// awoken, potentially resulting in incorrect results in the event of a spurious wakeup

This isn't necessarily a good way to do this, of course; if you timeout just before notify() is called, the signal condition may be lost, after all. You really should be waiting in a loop, checking some persistent condition.

bdonlan
  • 224,562
  • 31
  • 268
  • 324