Reputation: 30548
I have a wait/notify mechanism in my code which basically wraps Lock
and Condition
like this:
class ConditionWithTimeout { // timeout part omitted
private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();
public void doWait() throws InterruptedException {
lock.lock();
cond.await();
lock.unlock();
}
public void doNotify() {
lock.lock();
cond.signalAll();
lock.unlock();
}
}
My problem is that if I do something like this:
someFunction();
myCond.doWait();
it is possible that if someFunction
calls doNotify
at some point on myCondition
I will wait until myCondition
times out because doWait
in someFunction
might be executed before this code jumps to the next line to execute myCond.doWait
.
I worked around it by adding a preWaitFn
:
class ConditionWithTimeout {
private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();
private Executor hookExecutor = Executors.newSingleThreadExecutor();
public void doWait() throws InterruptedException {
doWait(() -> {
});
}
public void doWait(Runnable preWaitFn) throws InterruptedException {
lock.lock();
hookExecutor.execute(preWaitFn);
cond.await();
lock.unlock();
}
public void doNotify() {
lock.lock();
cond.signalAll();
lock.unlock();
}
}
and it works now but this seems like a code smell to me because it is still possible that doNotify
is called before cond.await
is called.
So my question is that what is the best practice in such situations? I need to block until someFunction
finishes its work but I wish to avoid such corner cases as this.
Upvotes: 0
Views: 321
Reputation: 11454
You could try using a CountDownLatch. Depending on your use case, this may not be the best answer - it works if you can create a new instance of your flag object for each call, but not if you need to reuse one. The linked Javadoc page contains examples of how to use it. This is basically a off-the-shelf replacement for shmosel's answer.
Upvotes: 1
Reputation: 50726
The usual pattern is to check some logical condition and use the signal as a means of notifying another thread that the condition has been updated. In your case, a simple flag should suffice:
private boolean flag = false;
public void doWait() throws InterruptedException {
lock.lock();
try {
// Loop is necessary to avoid spurious wakeup
while (!flag) {
cond.await();
}
} finally {
// Unlock in finally in case exception is thrown
lock.unlock();
}
}
public void doNotify() {
lock.lock();
try {
flag = true;
cond.signalAll();
} finally {
lock.unlock();
}
}
Upvotes: 2