I am testing a producer-consumer scenario where the producer blocks when it tries to write on a queue which is full. I want to test that the producer thread wakes up properly and works as per expectation after the consumer reads from the full queue*. The queue write API calls pthread_cond_wait() on detecting queue full and the read API signals the conditional variable after reading from the queue.
How do I ensure the occurrence of Sequence 3 over any other sequence of operations in my test environment?
* Yes I want to test this limited scenario separately; there are other tests which do test the functionality of the whole queue, this test is in addition to those.
More details -
There is a single mutex governing the queue. There are 2 conditional variables - one to signal write (all writes), one to signal read (all reads). The queue_write API blocks on the read condvar if the queue is full. The queue_read API blocks on the write condvar if the queue is empty. All signalling happens under the aegis of the mutex.
There is a lot more nuance in the queue but for the purpose of setting context for this question this is an adequate summary of the queue's functioning.
The pthread_cond_signal() routine is used to signal (or wake up) another thread which is waiting on the condition variable. It should be called after mutex is locked, and must unlock mutex in order for pthread_cond_wait() routine to complete.
It is not safe to use the pthread_cond_signal() function in a signal handler that is invoked asynchronously.
Yes, pthread_cond_wait , when successful, causes the thread to wait until notified.
The pthread_cond_wait() function atomically unlocks mutex and performs the wait for the condition. In this case, atomically means with respect to the mutex and the condition variable and another threads access to those objects through the pthread condition variable interfaces.
Since your queue uses pthread_cond_signal
, it must be holding a lock as well. So your test case should simply hold the lock, create the producer, and then wait for the signal. After the producer generates the signal, release the lock, and create a consumer.
void test () {
pthread_mutex_lock(q_lock);
// Blocks on the same queue lock the producer and
// consumer would use.
create_producer();
// The producer will block on the queue lock when
// it tries to write to the queue.
do {
pthread_cond_wait(q_write_cond, q_lock);
// Mimic a blocked queue_read, and wait for the
// producer to signal. This will release the lock
// and allow the producer to progress.
} while (!q_is_full());
// The queue is now full, lock is held since
// pthread_cond_wait returned.
pthread_mutex_unlock(q_lock);
// Release the queue lock, allow the consumer to
// operate unhindered.
create_consumer();
// The consumer will proceed to drain the queue.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With