Understanding pthreads locks and condition variables - c

I had an exercise about threads, locks, and condition variables in C. I needed to write a program that get data, turn it into a linked list, starting 3 threads each calculating result for each node in the list and main thread printing the results after evreyone finished.
This is the main function:
int thread_finished_count;
// Lock and Conditional variable
pthread_mutex_t list_lock;
pthread_mutex_t thread_lock;
pthread_cond_t thread_cv;
int main(int argc, char const *argv[])
{
node *list;
int pairs_count, status;
thread_finished_count = 0;
/* get the data and start the threads */
node *head = create_numbers(argc, argv, &pairs_count);
list = head; // backup head for results
pthread_t *threads = start_threads(&list);
/* wait for threads and destroy lock */
status = pthread_cond_wait(&thread_cv, &list_lock);
chcek_status(status);
status = pthread_mutex_destroy(&list_lock);
chcek_status(status);
status = pthread_mutex_destroy(&thread_lock);
chcek_status(status);
/* print result in original list */
print_results(head);
/* cleanup */
wait_for_threads(threads, NUM_THREADS);
free_list(head);
free(threads);
return EXIT_SUCCESS;
}
Please note that the create_numbers function is working properly, and the list is working as intended.
Here is the start_thread and thread_function code:
pthread_t *start_threads(node **list)
{
int status;
pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t) * NUM_THREADS);
check_malloc(threads);
for (int i = 0; i < NUM_THREADS; i++)
{
status = pthread_create(&threads[i], NULL, thread_function, list);
chcek_status(status);
}
return threads;
}
void *thread_function(node **list)
{
int status, self_id = pthread_self();
printf("im in %u\n", self_id);
node *currentNode;
while (1)
{
if (!(*list))
break;
status = pthread_mutex_lock(&list_lock);
chcek_status(status);
printf("list location %p thread %u\n", *list, self_id);
if (!(*list))
{
status = pthread_mutex_unlock(&list_lock);
chcek_status(status);
break;
}
currentNode = (*list);
(*list) = (*list)->next;
status = pthread_mutex_unlock(&list_lock);
chcek_status(status);
currentNode->gcd = gcd(currentNode->num1, currentNode->num2);
status = usleep(10);
chcek_status(status);
}
status = pthread_mutex_lock(&thread_lock);
chcek_status(status);
thread_finished_count++;
status = pthread_mutex_unlock(&thread_lock);
chcek_status(status);
if (thread_finished_count != 3)
return NULL;
status = pthread_cond_signal(&thread_cv);
chcek_status(status);
return NULL;
}
void chcek_status(int status)
{
if (status != 0)
{
fputs("pthread_function() error\n", stderr);
exit(EXIT_FAILURE);
}
}
Note that self_id is used for debugging purposes.
My problem
My main problem is about splitting the work. Each thread so get an element from the global linked list, calculate gcd, then go on and take the next element. I get this effect only if I'm adding usleep(10) after I unlock the mutex in the while loop. If I don't add the usleep, the FIRST thread will go in and do all the work while other thread just wait and come in after all the work has been done.
Please note!: I thought about the option that maybe the first thread is created, and untill the second thread is created the first one already finish all the jobs. This is why I added the "I'm in #threadID" check with usleep(10) when evrey thread is created. They all come in but only first one is doing all the jobs.
Here is the output example if I do usleep after the mutex unlock (notice diffrent thread ID)
with usleep
./v2 nums.txt
im in 1333593856
list location 0x7fffc4fb56a0 thread 1333593856
im in 1316685568
im in 1325139712
list location 0x7fffc4fb56c0 thread 1333593856
list location 0x7fffc4fb56e0 thread 1316685568
list location 0x7fffc4fb5700 thread 1325139712
list location 0x7fffc4fb5720 thread 1333593856
list location 0x7fffc4fb5740 thread 1316685568
list location 0x7fffc4fb5760 thread 1325139712
list location 0x7fffc4fb5780 thread 1333593856
list location 0x7fffc4fb57a0 thread 1316685568
list location 0x7fffc4fb57c0 thread 1325139712
list location 0x7fffc4fb57e0 thread 1333593856
list location 0x7fffc4fb5800 thread 1316685568
list location (nil) thread 1325139712
list location (nil) thread 1333593856
...
normal result output
...
And thats the output if I comment out the usleep after mutex lock (Notice the same thread ID)
without usleep
./v2 nums.txt
im in 2631730944
list location 0x7fffe5b946a0 thread 2631730944
list location 0x7fffe5b946c0 thread 2631730944
list location 0x7fffe5b946e0 thread 2631730944
list location 0x7fffe5b94700 thread 2631730944
list location 0x7fffe5b94720 thread 2631730944
list location 0x7fffe5b94740 thread 2631730944
list location 0x7fffe5b94760 thread 2631730944
list location 0x7fffe5b94780 thread 2631730944
list location 0x7fffe5b947a0 thread 2631730944
list location 0x7fffe5b947c0 thread 2631730944
list location 0x7fffe5b947e0 thread 2631730944
list location 0x7fffe5b94800 thread 2631730944
im in 2623276800
im in 2614822656
...
normal result output
...
My second question is about order of threads working. My exercise ask me to not use join to synchronize the threads (only use at the end to "free resources") but instand use that condition variable.
My goal is that each thread will take element, do the calculation and in the meanwhile another thread will go in and take another element, and new thread will take each element (or at least close to that)
Thanks for reading and I appriciate your help.

First, you are doing the gcd() work while holding the lock... so (a) only one thread will do any work at any one time, though (b) that does not entirely explain why only one thread appears to do (nearly) all the work -- as KamilCuk says, it may be that there is so little work to do, that it's (nearly) all done before the second thread wakes up properly. [More exotic, there may be some latency between thread 'a' unlocking the mutex and another thread starting to run, such that thread 'a' can acquire the mutex before another thread gets there.]
POSIX says that when a mutex is unlocked, if there are waiters then "the scheduling policy shall determine which thread shall acquire the mutex". The default "scheduling policy" is (to the best of my knowledge) implementation defined.
You could try a couple of things: (1) use a pthread_barrier_t to hold all the threads at the start of thread_function() until they are all running; (2) use sched_yield(void) after pthread_mutex_unlock() to prompt the system into running the newly runnable thread.
Second, you should not under any circumstance treat a 'condition variable' as a signal. For main() to know that all threads have finished you need a count -- which could be a pthread_barrier_t; or it could be simple integer, protected by a mutex, with a 'condition variable' to hold the main thread on while it waits; or it could be a count (in main()) and a semaphore (posted once by each thread as it exits).
Third, you show pthread_cond_wait(&cv, &lock); in main(). At that point main() must own lock... and it matters when that happened. But: as it stands, the first thread to find the list empty will kick cv, and main() will proceed, even though other threads are still running. Though once main() does re-acquire lock, any threads which are then still running will either be exiting or will be stuck on the lock. (It's a mess.)
In general, the template for using a 'condition variable' is:
pthread_mutex_lock(&...lock) ;
while (!(... thing we need ...))
pthread_cond_wait(&...cond_var, &...lock) ;
... do stuff now we have what we need ....
pthread_mutex_unlock(&...lock) ;
NB: a 'condition variable' does not have a value... despite the name, it is not a flag to signal that some condition is true. A 'condition variable' is, essentially, a queue of threads waiting to be re-started. When a 'condition variable' is signaled, at least one waiting thread will be re-started -- but if there are no threads waiting, nothing happens, in particular the (so called) 'condition variable' retains no memory of the signal.
In the new code, following the above template, main() should:
/* wait for threads .... */
status = pthread_mutex_lock(&thread_lock);
chcek_status(status);
while (thread_finished_count != 3)
{
pthread_cond_wait(&thread_cv, &thread_lock) ;
chcek_status(status);
} ;
status = pthread_mutex_unlock(&thread_lock) ;
chcek_status(status);
So what is going on here ?
main() is waiting for thread_finished_count == 3
thread_finished_count is a shared variable "protected" by the thread_lock mutex.
...so it is incremented in thread_function() under the mutex.
...and main() must also read it under the mutex.
if main() finds thread_finished_count != 3 it must wait.
to do that it does: pthread_cond_wait(&thread_cv, &thread_lock), which:
unlocks thread_lock
places the thread on the thread_cv queue of waiting threads.
and it does those atomically.
when thread_function() does the pthread_cond_signal(&thread_cv) it wakes up the waiting thread.
when the main() thread wakes up, it will first re-acquire the thread_lock...
...so it can then proceed to re-read thread_finished_count, to see if it is now 3.
FWIW: I recommend not destroying the mutexes etc until after all the threads have been joined.

I have looked deeper into how glibc (v2.30 on Linux & x86_64, at least) implements pthread_mutex_lock() and _unlock().
It turns out that _lock() works something like this:
if (atomic_cmp_xchg(mutex->lock, 0, 1))
return <OK> ; // mutex->lock was 0, is now 1
while (1)
{
if (atomic_xchg(mutex->lock, 2) == 0)
return <OK> ; // mutex->lock was 0, is now 2
...do FUTEX_WAIT(2)... // suspend thread iff mutex->lock == 2...
} ;
And _unlock() works something like this:
if (atomic_xchg(mutex->lock, 0) == 2) // set mutex->lock == 0
...do FUTEX_WAKE(1)... // if may have waiter(s) start 1
Now:
mutex->lock: 0 => unlocked, 1 => locked-but-no-waiters, 2 => locked-with-waiter(s)
'locked-but-no-waiters' optimizes for the case where there is no lock contention and there is no need to do FUTEX_WAKE in _unlock().
the _lock()/_unlock() functions are in the library -- they are not in the kernel.
...in particular, the ownership of the mutex is a matter for the library, not the kernel.
FUTEX_WAIT(2) is a call to the kernel, which will place the thread on a pending queue associated with the mutex, unless mutex->lock != 2.
The kernel checks for mutex->lock == 2 and adds the thread to the queue atomically. This deals with the case of _unlock() being called after the atomic_xchg(mutex->lock, 2).
FUTEX_WAKE(1) is also a call to the kernel, and the futex man page tells us:
FUTEX_WAKE (since Linux 2.6.0)
This operation wakes at most 'val' of the waiters that are waiting ... No guarantee is provided about which waiters are awoken (e.g., a waiter with a higher scheduling priority is not guaranteed to be awoken in preference to a waiter with a lower priority).
where 'val' in this case is 1.
Although the documentation says "no guarantee about which waiters are awoken", the queue appears to be at least FIFO.
Note especially that:
_unlock() does not pass the mutex to the thread started by the FUTEX_WAKE.
once woken up, the thread will again try to obtain the lock...
...but may be beaten to it by any other running thread -- including the thread which just did the _unlock().
I believe this is why you have not seen the work being shared across the threads. There is so little work for each one to do, that a thread can unlock the mutex, do the work and be back to lock the mutex again before a thread woken up by the unlock can get going and succeed in locking the mutex.

Related

How can I add mutexes and condition variables to guarantee this behavior?

Multiple threads will be “busy waiting” for the next_action variable to be set. Ideally one thread would call perform_action whenever the main sets it to a nonzero.
// choose a time-consuming activity based on action ...
void perform_action(int action);
int next_action = 0;
void* threadfunc(void*)
{
while (1)
{
while (next_action == 0);
int my_action = next_action;
next_action = 0;
perform_action(my_action);
}
}
int main()
{
// assume we've spawned threads executing threadfunc ...
while (1)
{
// determine what action should be dispatched to a thread next
next_action = read_action_from_user();
// exit the program
if (next_action == -1) { break; }
}
}
For this issue I would use a semaphore.
Using a while loop will waste processor time by constantly checking for changes in variable.
Here we can use synchronization methods that can alert when is available.
A semaphore has two options: acquire and release.
Main thread initially acquires the semaphore, and the thread is enqueued to acquire it. Thread will wait until semaphore becomes available.
When main thread sets it releases the semaphore to signal that a nonzero value has been set to it.
The thread will now wake up, acquire the semaphore, perform requested operation and release the semaphore.
When main thread needs to change the semaphore, it must acquire the semaphore again, set the and release the semaphore.
To acquire the semaphore, main thread must necessarily wait until the thread has finished.
I would not use a mutex because you also need a signaling mechanism to wake up your thread, and not only a protection for a shared variable.
See also Conditional Variable vs Semaphore

Trying to understand pthread_cond_lock and pthread_cond_signal

So I'm trying to understand exactly how pthread_mutex_lock works.
My current understanding is that it unlocks the mutex and puts whatever thread is going though it to sleep. Sleep meaning that the thread is inactive and consuming no resources.
It then waits for a signal to go from asleep to blocked, meaning that the thread can no longer change any variables.
thread 1:
pthread_mutex_lock(&mutex);
while (!condition){
printf("Thread wating.\n");
pthread_cond_wait(&cond, &mutex);
printf("Thread awakened.\n");
fflush(stdout);
}
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&condVar);
pthread_mutex_unlock(&mutex);
So basically in the sample above, the loop runs and runs and each iteration pthread_cond_wait checks if the condition of the loop is true. If it is then the cond_signal is sent and the thread is blocked so it can't manipulate any more data.
I'm really having trouble wrapping my head around this, I'd appreciate some input and feedback about how this works and whether or not I am beginning to understand this based on what I have above.
I've gone over this post but am still having trouble
First, a summary:
pthread_mutex_lock(&mutex):
If mutex is free, then this thread grabs it immediately.
If mutex is grabbed, then this thread waits until the mutex becomes free, and then grabs it.
pthread_mutex_trylock(&mutex):
If mutex is free, then this thread grabs it.
If mutex is grabbed, then the call returns immediately with EBUSY.
pthread_mutex_unlock(&mutex):
Releases mutex.
pthread_cond_signal(&cond):
Wake up one thread waiting on the condition variable cond.
pthread_cond_broadcast(&cond):
Wake up all threads waiting on the condition variable cond.
pthread_cond_wait(&cond, &mutex):
This must be called with mutex grabbed.
The calling thread will temporarily release mutex and wait on cond.
When cond is broadcast on, or signaled on and this thread happens to be the one woken up, then the calling thread will first re-grab the mutex, and then return from the call.
It is important to note that at all times, the calling thread either has mutex grabbed, or is waiting on cond. There is no interval in between.
Let's look at a practical, running example code. We'll create it along the lines of OP's code.
First, we'll use a structure to hold the parameters for each worker function. Since we'll want the mutex and the condition variable to be shared between threads, we'll use pointers.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Worker function work. */
struct work {
pthread_t thread_id;
pthread_mutex_t *lock; /* Pointer to the mutex to use */
pthread_cond_t *wait; /* Pointer to the condition variable to use */
volatile int *done; /* Pointer to the flag to check */
FILE *out; /* Stream to output to */
long id; /* Identity of this thread */
unsigned long count; /* Number of times this thread iterated. */
};
The thread worker function receives a pointer to the above structure. Each thread iterates the loop once, then waits on the condition variable. When woken up, if the done flag is still zero, the thread iterates the loop. Otherwise, the thread exits.
/* Example worker function. */
void *worker(void *workptr)
{
struct work *const work = workptr;
pthread_mutex_lock(work->lock);
/* Loop as long as *done == 0: */
while (!*(work->done)) {
/* *(work->lock) is ours at this point. */
/* This is a new iteration. */
work->count++;
/* Do the work. */
fprintf(work->out, "Thread %ld iteration %lu\n", work->id, work->count);
fflush(work->out);
/* Wait for wakeup. */
pthread_cond_wait(work->wait, work->lock);
}
/* *(work->lock) is still ours, but we've been told that all work is done already. */
/* Release the mutex and be done. */
pthread_mutex_unlock(work->lock);
return NULL;
}
To run the above, we'll need a main() as well:
#ifndef THREADS
#define THREADS 4
#endif
int main(void)
{
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait = PTHREAD_COND_INITIALIZER;
volatile int done = 0;
struct work w[THREADS];
char *line = NULL, *p;
size_t size = 0;
ssize_t len = 0;
unsigned long total;
pthread_attr_t attrs;
int i, err;
/* The worker functions require very little stack, but the default stack
size is huge. Limit that, to reduce the (virtual) memory use. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2 * PTHREAD_STACK_MIN);
/* Grab the mutex so the threads will have to wait to grab it. */
pthread_mutex_lock(&lock);
/* Create THREADS worker threads. */
for (i = 0; i < THREADS; i++) {
/* All threads use the same mutex, condition variable, and done flag. */
w[i].lock = &lock;
w[i].wait = &wait;
w[i].done = &done;
/* All threads output to standard output. */
w[i].out = stdout;
/* The rest of the fields are thread-specific. */
w[i].id = i + 1;
w[i].count = 0;
err = pthread_create(&(w[i].thread_id), &attrs, worker, (void *)&(w[i]));
if (err) {
fprintf(stderr, "Cannot create thread %d of %d: %s.\n", i+1, THREADS, strerror(errno));
exit(EXIT_FAILURE); /* Exits the entire process, killing any other threads as well. */
}
}
fprintf(stderr, "The first character on each line controls the type of event:\n");
fprintf(stderr, " e, q exit\n");
fprintf(stderr, " s signal\n");
fprintf(stderr, " b broadcast\n");
fflush(stderr);
/* Let each thread grab the mutex now. */
pthread_mutex_unlock(&lock);
while (1) {
len = getline(&line, &size, stdin);
if (len < 1)
break;
/* Find the first character on the line, ignoring leading whitespace. */
p = line;
while ((p < line + len) && (*p == '\0' || *p == '\t' || *p == '\n' ||
*p == '\v' || *p == '\f' || *p == '\r' || *p == ' '))
p++;
/* Do the operation mentioned */
if (*p == 'e' || *p == 'E' || *p == 'q' || *p == 'Q')
break;
else
if (*p == 's' || *p == 'S')
pthread_cond_signal(&wait);
else
if (*p == 'b' || *p == 'B')
pthread_cond_broadcast(&wait);
}
/* It is time for the worker threads to be done. */
pthread_mutex_lock(&lock);
done = 1;
pthread_mutex_unlock(&lock);
/* To ensure all threads see the state of that flag,
we wake up all threads by broadcasting on the condition variable. */
pthread_cond_broadcast(&wait);
/* Reap all threds. */
for (i = 0; i < THREADS; i++)
pthread_join(w[i].thread_id, NULL);
/* Output the thread statistics. */
total = 0;
for (i = 0; i < THREADS; i++) {
total += w[i].count;
fprintf(stderr, "Thread %ld: %lu events.\n", w[i].id, w[i].count);
}
fprintf(stderr, "Total: %lu events.\n", total);
return EXIT_SUCCESS;
}
If you save the above as example.c, you can compile it to example using e.g. gcc -Wall -O2 example.c -lpthread -o example.
To get the correct intuitive grasp of the operations, run the example in a terminal, with the source code in a window next to it, and see how the execution progresses as you provide input.
You can even run commands like printf '%s\n' s s s b q | ./example to run a sequence of events in a quick succession, or printf 's\ns\ns\nb\nq\n' | ./example with even less time in between events.
After some experimentation, you'll hopefully find out that not all input events cause their respective action. This is because the exit event (q above) is not synchronous: it does not wait for all pending work to be done, but tells the threads to exit right then and there. That is why the number of events may vary even for the exact same input.
(Also, if you signal on the condition variable, and immediately broadcast on it, the threads tend to only get woken up once.)
You can mitigate that by delaying the exit, using e.g. (printf '%s\n' s s b s s s ; sleep 1 ; printf 'q\n' ) | ./example.
However, there are better ways. A condition variable is not suitable for countable events; it is really flag-like. A semaphore would work better, but then you should be careful to not overflow the semaphore; it can only be from 0 to SEM_VALUE_MAX, inclusive. (So, you could use a semaphore to represent the number of pending job, but probably not for the number of iterations done by each/all thread workers.) A queue for the work to do, in thread pool fashion, is the most common approach.
pthread_cond_wait() simply means that the current thread shall release the mutex and then waits on a condition. The trick here is that both happens atomically, so it cannot happen, that the thread has released the mutex and is not yet waiting on the condition or is already waiting on the condition and has not yet released the mutex. Either both has happened or none has happened.
pthread_cond_signal() simply wakes up any thread that is currently waiting on the signaled condition. The first thing the woken up thread will do is obtaining the mutex again, if it cannot obtain it (e.g. as the signaling thread is currently owning the mutex), it will block until it can. If multiple threads are waiting on the condition, pthread_cond_signal() just wakes up one of them, which one is not defined. If you want to wake up all the waiting threads, you must use pthread_cond_broadcast() instead; but of course they won't run at the same time as now each of them first requires to obtain the mutex and that will only be possible one after another.
pthread_cond_t has no state. If you signal a condition no thread is waiting for, then nothing will happen. It's not like this will set a flag internally and if later on some thread calls pthread_cond_wait(), it will be woken up immediately as there is a pending signal. pthread_cond_signal() only wakes up threads that are already waiting, that means these threads must have called pthread_cond_wait() prior to you calling pthread_cond_signal().
Here's some simple sample code. First a reader thread:
// === Thread 1 ===
// We want to process an item from a list.
// To make sure the list is not altered by one
// thread while another thread is accessing it,
// it is protected by a mutex.
pthread_mutex_lock(&listLock);
// Now nobody but us is allowed to access the list.
// But what if the list is empty?
while (list->count == 0) {
// As long as we hold the mutex, no other thread
// thread can add anything to the list. So we
// must release it. But we want to know as soon
// as another thread has changed it.
pthread_cond_wait(&listCondition, &listLock);
// When we get here, somebody has signaled the
// condition and we have the mutex again and
// thus are allowed to access the list. The list
// may however still be empty, as another thread
// may have already consumed the new item in case
// there are multiple readers and all are woken
// up, thus the while-loop. If the list is still
// empty, we just go back to sleep and wait again.
}
// If we get here, the list is not empty.
processListItem(list);
// Finally we release the mutex again.
pthread_mutex_unlock(&listLock);
And then a writer thread:
// === Thread 2 ===
// We want to add a new item to the list.
// To make sure that nobody is accessing the
// list while we do, we need to obtain the mutex.
pthread_mutex_lock(&listLock);
// Now nobody but us is allowed to access the list.
// Check if the list is empty.
bool listWasEmpty = (list->count == 0);
// We add our item.
addListItem(list, newItem);
// If the list was empty, one or even multiple
// threads may be waiting for us adding an item.
// So we should wake them up here.
if (listWasEmpty) {
// If any thread is waiting for that condition,
// wake it up as now there is an item to process.
pthread_cond_signal(&listCondition);
}
// Finally we must release the mutex again.
pthread_mutex_unlock(&listLock);
The code is written so that there can be any number of reader/writer threads. Signaling only if the list was empty (listWasEmpty) is just a performance optimization, the code would also work correctly if you always signal the condition after adding an item.

Correctly waiting for a thread to terminate in C

This code plays a sound clip by creating a thread to do it. When bleep() runs, it sets the global variable bleep_playing to TRUE. In its main loop, if it notices that bleep_playing has been set to FALSE, it terminates that loop, cleans up (closing files, freeing buffers), and exits. I don't know the correct way to wait for a detached thread to finish. pthread_join() doesn't do the job. The while loop here continually checks bleep_id to see if it's valid. When it isn't, execution continues. Is this the correct and portable way to tell a thread to clean up and terminate before the next thread is allowed to be created?
if (bleep_playing) {
bleep_playing = FALSE;
while (pthread_kill(bleep_id, 0) == 0) {
/* nothing */
}
}
err = pthread_create(&bleep_id, &attr, (void *) &bleep, &effect);
I
Hmm... pthread_join should do the job. As far as I remember the thread has to call pthread_exit...?
/* bleep thread */
void *bleep(void *)
{
/* do bleeping */
pthread_exit(NULL);
}
/* main thread */
if (pthread_create(&thread, ..., bleep, ...) == 0)
{
/*
** Try sleeping for some ms !!!
** I've had some issues on multi core CPUs requiring a sleep
** in order for the created thread to really "exist"...
*/
pthread_join(&thread, NULL);
}
Anyway if it isn't doing its thing you shouldn't poll a global variable since it will eat up your CPU. Instead create a mutex (pthread_mutex_*-functions) which is initially locked and freed by the "bleep thread". In your main thread you can wait for that mutex which makes your thread sleep until the "bleep thread" frees the mutex.
(or quick & and dirty: sleep for a small amount of time while waiting for bleep_playing becoming FALSE)

how to unlock and destroy a mutex atomically

I'm reading stevens's book: apue. 2e. I have encountered a problem, about Mutex.
Let's see a peice of code first: (the following code comes from figure 11.10 of apue 2e)
#include <stdlib.h>
#include <pthread.h>
struct foo {
int f_count;
pthread_mutex_t f_lock;
/* ... more stuff here ... */
};
struct foo *
foo_alloc(void) /* allocate the object */
{
struct foo *fp;
if((fp = malloc(sizeof(struct foo))) != NULL){
fp->f_count = 1;
if(pthread_mutex_init(&fp->f_lock, NULL) != 0){
free(fp);
return NULL;
}
/* ... continue initialization ... */
}
return (fp);
}
void
foo_hold(struct foo *fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
void
foo_rele(struct foo *fp) /* release a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
if(--fp->f_count == 0){ /* last reference */
pthread_mutex_unlock(&fp->f_lock); /* step 1 */
pthread_mutex_destroy(&fp->f_lock); /* step 2 */
free(fp);
}else{
pthread_mutex_unlock(&fp->f_lock);
}
}
Assuming we have two threads: thread1 and thread2. thread1 is running right now.
it calls function foo_rele and has finished the execution of step 1(see above),
and prepare to execute step2.
however, context switch occurs at this moment. and thread2 starts to execute.
thread2 locks the lock fp->f_lock then does something. but before thread2 unlocks the lock, context switch occurs again.
thread2 stops executing and thread1 starts to execute. thread1 destroy the lock and it's known to us all that error generates.
so, my problem is: Is the situation mentioned above possible to happen?
how can we avoid it and is there any interface(API) that can unlock and destroy a mutex atomically?
I agree with usr (+1), the design is wrong.
Generally speaking, before one thread can destroy anything it must establish that all other threads have finished using it. That requires some other form of inter-thread synchronisation. What you need is a way in which thread 1 can tell thread 2 that the resource needs to be freed, and for thread 2 to acknowledge that so that thread 1 can call foo_rele() knowing for sure that thread 2 will not try to use fp ever again. Or, for thread 2 telling thread 1 that it no longer needs fp and thread 1 calling foo_rele() as a consequence.
This can be accomplished several ways. One is for thread 1 to set a flag under the protection of the lock, and then wait for thread 2 to quit. Meanwhile, thread 2 eventually gets round to seeing the flag, and then quits. This releases thread 1 which then calls foo_rele(). Another way is message passing over pipes (my preferred means of inter-thread synchronisation); something along the lines of thread 1 -> thread 2, "Please stop using fp": thread 2 -> thread 1, "Ok" (though of course a better defined message set based on enums is advisable, but you get my meaning).
What you can't ever have is thread 1 calling foo_rele() without thread 2 either knowing that it must never touch fp ever again or thread 2 having already quit.
This is not well-designed because thread2 accesses an object which might have been destroyed already. Add a reference (increment f_count) before starting thread2. That way thread2 already starts out with the guarantee that the object is stable while it is running.

My Posix Thread wakes up without a signal

I'm trying to implement a blocking queue using POSIX threads. Important code segments are shown below. I tried to run this program. The thread trying to remove an element from the queue goes to sleep when there are no elements in the queue and wakes up again without any signal from the thread that adds an element into the queue (This I conclude because I did not start any thread that adds an element into the queue). The thread woke up again goes to sleep and this process repeats. What am I doing wrong? Please some one tell me what I am missing here?
struct rqueue
{
int qsize;
int capacity;
pthread_mutex_t lock;
pthread_cond_t not_empty;
pthread_cond_t not_full;
};
remove_element_method:
pthread_mutex_lock(&rq->lock);
while(rq->qsize == 0){
perror("Q size is zero going to sleep");
pthread_cond_wait(&rq->not_empty);
perror("woke up");
}
// some code
pthread_cond_signal(&rq->not_full);
pthread_mutex_unlock(&rq->lock);
add_element_method:
pthread_mutex_lock(&rq->lock);
if(rq->capacity != -1 ){
while(rq->qsize == rq->capacity){
pthread_cond_wait(&rq->not_full);
}
}
//some code
pthread_cond_signal(&rq->not_empty);
pthread_mutex_unlock(&rq->lock);
pthread_cond_wait() takes two arguments -- the second is the mutex you're holding. You're only passing it one argument.
Also, did you initialize the mutex and condition variables using pthread_mutex_init() and pthread_cond_init()?

Resources