Yet another n00b in pthreads ...
Hi,
I am new in pthread library and perhaps my question is naive. Anyway, here is the problem I want to solve. I want to create N >= 2 threads and one of them (from now on "generator") will always fill in with "chunks of data" a shared queue, while the rest (N-1) threads will get the data from the queue (assuming there is something) - call these (N-1) "workers". For this purpose I decided to use a mutex for references in queue, as well as a condition variable. Hence, my idea is that, if the workers can't find data in queue, they should wait for a signal. Obviously the "generator" sends that signal once new data have been added into the queue. So, conceptually, here is how I implemented those functions initially (numbered lines for clarity):
Code:
01: generator(){
02: while (!created_required_amount_of_data) {
03: data <-- create_data ()
04: lock (mutex)
05: enqueue (data)
06: if (there_are_threads_waiting)
07: send_condition_signal () // <---- NOT broadcast
08: unlock (mutex)
09: }
10: }
I guess the above is fine. Let's switch to the problematic "worker":
Code:
01: worker (){
02: while (!processed_required_amount_of_data) {
03: lock (mutex)
04: if (queue_is_empty()) {
05: waiting_threads++
06: wait_for_signal ()
07: waiting_threads--
08: }
09: data <-- dequeue_data ()
10: unlock (mutex)
11: process (data)
12: }
13: }
Unfortunately, the above scenario does NOT work always. It seems to be working *always* if I change line 4 in the "worker" version from an 'if' statement to a 'while' statement.
Now, here comes my first question:
1) Is it possible to use signal and wake just a *single one* thread from those waiting for a signal? In the documentation of pthread_cond_signal it is stated that *at least one* thread will wake up. But I only want *exactly* one ... :(
And the above change from 'if' to 'while' saves me almost always on runtime, since one of the threads that wake up check if the queue is empty, and because another one was faster on waking up, they end up sleeping again! But of course, my impression is that this works only because it is a very unfortunate scenario for the scheduler *not* to work! :p (I hope I make sense). E.g. two threads wake up; first asks if queue is empty; gets the correct answer (non-empty); and now the scheduler stops that thread and gives priority to the other one who woke up as well. The 2nd thread performs the same check, it is falsely interpreted as a non-empty queue (because the first one didn't make it to dequeue something - lol), and both threads will attempt very soon to dequeue. But if nothing arrives in between, one of the threads will get a NULL pointer, and a beautiful "Segmentation fault" (or "Bus error" on a mac) will occur. :)
Second question:
2) Now, how on earth am I going to overcome this problem? :-) I want a deterministic solution to the problem and not something magic like above!
I thought this was the idea of a mutex and a condition variable ...
Source code and makefile can be obtained here so that you can re-produce the problem to your machines. The critical 'if' / 'while' statements are on lines 205-206 on the random_nums.cpp.
I would be really thankful if you could shed some light on the above. Really, any comment is appreciated because I a complete newbie to this.
Thank you in advance