The methods notify()
and notifyAll()
are used to waken waiting thread(s). These methods must be called from code that holds the same object lock as the waiting thread(s). The method notify()
is deceptive in most cases unless all of the following conditions hold: [[Goetz 06]]
- Only one condition predicate is used with the locked object. Also, each thread must execute the same code after waking up from a wait.
- Only one thread must wake up on the notify signal. This is contingent on the condition predicate, in that, only one predicate must fulfill the condition and allow the thread to proceed.
These requirements are typically true when only one thread is waiting. Otherwise, if either condition does not hold, incorrect program behavior may result.
Noncompliant Code Example
This noncompliant code example violates the liveness property. A lock is obtained using a raw Object
lock
and three threads are started. Two condition predicates are used. One checks whether the buffer has zero elements and the other checks if the buffer is full with ten elements (buffer is not shown for brevity, only the count of the number of elements in the buffer at any time is shown). Initially the buffer is neither full nor empty. Conditions are created so that the buffer becomes empty and thread 1 goes into wait state, followed by thread 2, when the buffer becomes full.
public class MissedSignal implements Runnable { private static final Object lock = new Object(); private static int buffer_count = 5; private static int number; // Selects function based on thread number public void setThreadNumber(int num) { number = num; } public void run (){ synchronized(lock) { try { if(number == 1) { while(buffer_count == 0) { lock.wait(); } } else if(number == 2) { while(buffer_count == 10) { lock.wait(); } } else if(number == 3) { lock.notify(); } } catch (InterruptedException ie) { // Handle the exception } } } public static void makeThread1True() { buffer_count = 0; } public static void makeThread2True() { buffer_count = 10; } public static void main(String[] args) throws IOException, InterruptedException { MissedSignal ms = new MissedSignal(); makeThread1True(); ms.setThreadNumber(1); new Thread(ms).start(); Thread.sleep(1000); makeThread2True(); ms.setThreadNumber(2); new Thread(ms).start(); Thread.sleep(1000); ms.setThreadNumber(3); new Thread(ms).start(); } }
Note that when thread 2 goes into the wait state, the condition predicate of thread 1 becomes false
. When notify()
is invoked by thread 3, it can be delivered to either thread 1 or thread 2 depending on the particular Java Virtual Machine (JVM). If thread 1 is chosen to be notified, its condition turns out to be false
, which terminates it. This is the required functionality, that is, any thread whose condition predicate is false
must be terminated. However, if the notification is delivered to thread 2, it has no effect because its condition predicate is still true
, and consequently, it goes into the wait state once again. Thread 1 continues to wait despite its condition predicate being false
and is not terminated in this case.
Compliant Solution
This compliant solution uses the notifyAll()
method which sends notifications to all threads that wait on the same lock object. As a result, liveness is not affected unlike the noncompliant code example. The condition predicate controls which threads can resume their operations. Ensure that the lock is released promptly after the call to notifyAll()
.
else if(number == 3) { list.notifyAll(); }
Exceptions
EX1: If there are several similar threads waiting for a notification, and it is permissible to invoke any of them, notify()
may be used. The criteria for liveness is relaxed in this case.
Risk Assessment
Invoking the notify()
method instead of notifyAll()
can be a threat to the liveness property of the system.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
CON19- J |
low |
unlikely |
medium |
P2 |
L3 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[JLS 05]] Chapter 17, Threads and Locks
[[Goetz 06]] Section 14.2.4, Notification
[[Bloch 01]] Item 50: Never invoke wait outside a loop
CON18-J. Always invoke wait() and await() methods inside a loop 11. Concurrency (CON) CON20-J. Do not synchronize methods that make network calls