Unlocking the condition variable mutex twice? - c

I'm looking at the following snippets:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cvar;
char buf[25];
void*thread_f(void *);
int main(){
pthread_t thr;
pthread_cond_init(&cvar,NULL);
pthread_mutex_lock(&mtx);
pthread_create(&thr,NULL,thread_f,NULL);
pthread_cond_wait(&cvar,&mtx);
pthread_mutex_unlock(&mtx);
...join and destroy thread...
}
and thread_f:
void* thread_f(void* argp){
pthread_mutex_lock(&mtx);
strcpy(buf,"test");
pthread_cond_signal(&cvar);
pthread_mutex_unlock(&mtx); //why?
pthread_exit(NULL);
}
My understanding of the above code is, that main grabs the lock, creates the thread and runs thread_f which blocks. Then, main signals the wait on the condition variable cvar and unlocks the mutex. Then, thread_f unblocks, strcpys and signals cvar to wake up. Then, both main and thread_f unlock the mutex.
Is this the actual behaviour, or am I missing something somewhere? If it's the actual behaviour, isn't unlocking the already unlocked mutex UB, therefore should one of he mutex unlocks in the end be removed?
Thanks.

What this code is doing is valid.
When the thread calls pthread_cond_signal it doesn't immediately cause the main thread to grab the mutex, but wakes up the main thread so it can attempt to lock it. Then once the thread releases the mutex, the main thread is able to lock it and return from pthread_cond_wait.

The pthread_cond_wait() in the main thread unlocks the mutex and waits for the condition to be signalled — and relocks the mutex before returning.
The child thread is able to lock the mutex, manipulate the buffer, signal the condition and then unlock the mutex, letting the main thread reacquire the lock.
Consequently, the code is well-behaved — give or take the need to worry about 'spurious wakeups' in more general situations with multiple child threads. I don't think you can get a spurious wakeup here.

Related

Using conditional variables to unblock one thread but wouldn't mutex lock cause a deadlock?

Basic question but for the sake of briefness, I have two threads where bar unblocks foo upon a certain condition, but even though the program runs fine to my surprise, shouldn't it cause deadlock if foo is run first which acquires the lock which means bar shouldn't be able to proceed further given the condition variable would never be true in foo?
pthread_mutex_t lock;
pthread_cond_t cv;
bool dataReady = false;
void foo(void *arg)
{
printf ("Foo...\n");
pthread_mutex_lock(&lock);
while (!dataReady)
{
pthread_cond_wait(&cv, &lock);
}
printf ("Foo unlocked...\n");
dataReady = true;
pthread_mutex_unlock(&lock);
}
void bar(void *arg)
{
printf ("Bar...\n");
pthread_mutex_lock(&lock);
sleep(3);
printf ("Data ready...\n");
dataReady = true;
pthread_cond_broadcast(&cv);
pthread_mutex_unlock(&lock);
}
int main(void)
{
int main()
{
pthread_t t1,t2;
pthread_create(&t1,NULL,foo,NULL);
pthread_create(&t2,NULL,bar,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
Also in this context, using semaphore wouldn't make sense yes?
pthread_cond_wait(&cv, &lock); atomically releases the mutex when called and the re-acquires it when woken up.
From man 3 pthread_cond_wait:
These functions atomically release mutex and cause the calling thread
to block on the condition variable cond; atomically here means
"atomically with respect to access by another thread to the mutex and
then the condition variable". That is, if another thread is able to
acquire the mutex after the about-to-block thread has released it,
then a subsequent call to pthread_cond_broadcast() or
pthread_cond_signal() in that thread shall behave as if it were issued
after the about-to-block thread has blocked.
Upon successful return, the mutex shall have been locked and shall be
owned by the calling thread.
IMHO C++ documentation contains clearer explanation (I know the languages differ, but the principle of operation remains the same):
https://en.cppreference.com/w/cpp/thread/condition_variable
acquire a std::unique_lockstd::mutex, on the same mutex as used to protect the shared variable
either
check the condition, in case it was already updated and notified
execute wait, wait_for, or wait_until. The wait operations atomically release the mutex and suspend the execution of the thread.
When the condition variable is notified, a timeout expires, or a spurious wakeup occurs, the thread is awakened, and the mutex is
atomically reacquired. The thread should then check the condition and
resume waiting if the wake up was spurious.

Mutex is lock but other threads are entering in critical section

We have studied that if we dealing with multi-threaded problems then we use one of thread synchronizing method called mutex which allows to lock critical section so that other threads do not interfere and goes into block state until mutex unlocks the critical section.
But I am doing this thing in my program but the output of this program is not matches with the concept of mutex. If I am wrong so correct me.
Code
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#define MAX 10
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int toConsume=0;
int i;
void* consumer(void *ptr) {
pthread_mutex_lock(&the_mutex);
while(i<MAX)
{
/* protect buffer */
while (toConsume <= 0) /* If there is nothing in the buffer then wait */
{
printf("Waiting Thread id:%lu \n",pthread_self());
pthread_cond_wait(&condc, &the_mutex);
}
pthread_mutex_unlock(&the_mutex); /* release the buffer */
sleep(2);
pthread_mutex_lock(&the_mutex); /* protect buffer */
toConsume--;
i++;
}
pthread_mutex_unlock(&the_mutex); /* release the buffer */
pthread_exit(0);
}
int main(int argc, char **argv) {
pthread_t pro, con[3];
pthread_mutex_init(&the_mutex, NULL);
pthread_cond_init(&condc, NULL); /* Initialize consumer condition variable */
pthread_cond_init(&condp, NULL); /* Initialize producer condition variable */
// Create the threads
for(int i=0 ;i<3;i++)
pthread_create(&con[i], NULL, consumer, NULL);
for(int i=0 ;i<3;i++)
pthread_join(con[i], NULL);
return 0;
}
OUTPUT
$ ./ex
Waiting Thread id:140580582618880
Waiting Thread id:140580574226176
Waiting Thread id:140580565833472
All thread enters into critical section even the mutex remain its lock.
The function pthread_cond_wait will unlock the held mutex while the thread is waiting. This allows another thread to enter the critical section.
The purpose of using pthread_cond_wait is that the thread needs to wait for some condition to become true before really performing the work inside the critical section. To test the condition in the first place requires locking the mutex. But, if the condition is false, it must wait for some event to make the condition true. If it waits with the lock held, no other thread will be able to update the state for the condition to become true, since updating the condition will also require locking the same mutex.
Thus, when waiting on a condition variable, the mutex is unlocked to allow another thread to grab the lock to perform something that would make the condition true.
As an example, consider a job queue. A thread will lock a mutex to get a job from the queue. However, if the queue is empty, it must wait for a job to appear in the queue. This is the condition that is must wait on, and a condition variable can be used for that purpose. When it calls pthread_cond_wait on the condition variable, the associated mutex is unlocked.
Another thread wishes to place a job into the queue. That thread can lock the mutex, add the job to the queue, signal the condition variable, then unlock the mutex.
When the condition variable is signaled, the waiting thread is woken up and pthread_cond_wait returns with the lock on the mutex held again. It detects that the queue is non-empty, and can enter the critical section of dequeueing a job from the queue.

pthread_mutex_lock why not block the thread as usual

Recently I read some code about thread mutex, releated code is here:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_t thread;
void fn(void *arg)
{
3 pthread_mutex_lock(&mutex);
printf( "signal before\n" );
4 pthread_cond_signal(&cond);
printf( "signal after\n" );
pthread_mutex_unlock(&mutex);
}
int main()
{
int err1;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond,NULL);
1 pthread_mutex_lock(&mutex);
2 err1 = pthread_create(&thread, NULL, fn, NULL);
usleep(1);
pthread_cond_wait(&cond,&mutex);
printf( "main thread get signal\n");
pthread_mutex_unlock(&mutex);
pthread_join(thread, NULL);
pthread_mutex_destroy( &mutex );
pthread_cond_destroy( &cond );
}
In main thread, first I call pthread_mutex_lock function locks the mutex in num 1, after I create a child thread in num 2, and in the child thread start function void fn( void *arg) I call pthread_mutex_lock in num 3 again, In theory it should block until mutex (main thread) is freed, but why can it continue to execute code in num 4 in child thread ?
execute result:
gcc version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Thanks very much.
The purpose of a condition variable is to permit threads to wait for things to happen in other threads. The problem with doing this with a mutex is that the following code doesn't work:
Acquire the lock that protects the shared state.
See if we need to wait, if so wait.
Oops. Now we're waiting while we hold the lock. So no other thread can change the shared state. So we'll wait forever. Let's try it again, this time releasing the lock:
Acquire the lock that protects the shared state.
If we need to wait, release the lock and wait.
Oops, we still have a problem. What if the thing we're waiting for happens after we release the lock but before we begin waiting? Again, we'll wait forever.
So we need an atomic "unlock and wait" function to make step 2 work. That's what pthread_cond_wait is. So the mutex is released while the thread is waiting.
The canonical use of pthread_cond_wait is:
pthread_mutex_lock(&mutex);
while (we_need_to_wait)
pthread_cond_wait(&cond, &mutex);
// possibly do some stuff while we still hold the mutex
pthread_mutex_unlock(&mutex);
Notice this allows us to decide to wait while we still hold the mutex, wait without holding the mutex, but have no window for a race condition where the mutex is released but we're not yet waiting.
By the way, it's extremely difficult to use a condition variable sanely any other way. So you really shouldn't attempt to use pthread_cond_wait in any other pattern until you have a rock solid understanding of how condition variables work. Make absolutely sure you always know precisely what you're waiting for and the thing you're waiting for is protected by the mutex that you pass to pthread_cond_wait.

Condition variables and mutex_unlock

Code:
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
sleep(1);
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&count_threshold_cv,&mutex);
sleep(1);
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
Now we have one mutex_unlock function to be executed in each thread. And one locked mutex. Why doesn't this lead to an undefined behaviour. Since both threads try to unlock the same mutex which leads to one trying to unlock an already unlocked mutex.
Edit: pthread_cond_wait releases the mutex for the second thread to use. Now consider the second thread executes pthread_cond_signal which leads to the first thread to reacquire the mutex. Now we have two threads having the same mutex lock,because both didn't get to execute the mutex_unlock because of the 'sleep' function. Is my understanding wrong?
pthread_mutex_lock() blocks if the mutex to be locked is already locked. It returns if the mutex got unlocked.
pthread_cond_wait() unlocks the mutex when starting to wait and locks is before returning. Returning might get delayed if the mutex in question is still locked. Returning then will be delay until the mutex got unlocked.
Putting the above together and applying it to the code you show one sees that each thread function nicely starts with a locking followed by an unlocking (and so on), so everything is fine.
Referring to the example code: pthread_cond_wait() returns when inc_func() had called pthread_mutex_unlock().
To successfully handle the scenario described by the example code you need to consider two special cases
the case of signal coming first and
the case of so called "spurious wake-ups", that is pthread_cond_wait() returning without having been signalled.
To handle both such case each condition should have a watch variable.
pthread_mutex_t mutex = ...
pthread_cond_t count_threshold_cv = ...
int signalled = 0;
void *inc_func(void *arg)
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&count_threshold_cv);
signalled = 1;
pthread_mutex_unlock(&mutex);
}
void *watch(void *arg)
{
pthread_mutex_lock(&mutex);
while (0 == signalled)
{
pthread_cond_wait(&count_threshold_cv,&mutex);
}
pthread_mutex_unlock(&mutex);
}
int main(void)
{
pthread_t id[2];
pthread_create(&id[0],NULL,watch,NULL);
pthread_create(&id[1],NULL,inc_func,NULL);
int i;
for(i=0;i<2;i++)
pthread_join(id[i],NULL);
}
If indeed the order is
watch runs first and locks (while inc_func waits)
watch which has the mutex waits using pthread_cond_wait which unlocks the mutext and blocks as per the documentation
These functions atomically release mutex and cause the calling thread
to block on the condition variable cond;
This allows the following
inc_func acquires the mutex then signals (at this point the mutex is yet to be released)
inc_func releases the mutex
Because the mutex was released and the mutex unlocked the execution of watch resumes having locked the mutex as per the documentation:
Upon successful return, the mutex has been locked and is owned by the calling thread.
What follows is a legal release of the mutex.
The scenario that you did not consider is what if the code of inc_func executes first without switching to watch
inc_func locks the mutex
inc_func signals but there's no one to be signaled, which is OK.
inc_func unlocks the mutex
watch locks the mutex
then waits for the conditional variable but there will be no one to signal it so it'll wait forever.

pthreads: cancel blocking thread

I have a situation like
-Thread A-
Lock Mutex
If ActionA() == ERROR
Stop = True
Unlock Mutex
-Mainthread-
Lock Mutex
If ActionB() == ERROR
Stop = True
If Stop == True
Cancel ThreadA
Join ThreadA
Exit program
Unlock Mutex
where a Mutex is used for synchronization. The worker thread 'Thread A' and the main thread can both get into an error state and set the local variable 'Stop' then, which should lead to the cancellation of Thread A and exit of the main program. The Mutex is used to prevent a race condition when accessing Stop (and other shared objects).
Unfortunately calling pthread_cancel() does not seem to work when the target thread is waiting for a Mutex:
#include <pthread.h>
pthread_mutex_t mutex;
void *thread(void *args){
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_mutex_lock(&mutex);
return NULL;
}
int main(int argc, const char * argv[])
{
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
pthread_t t;
pthread_create(&t, NULL, thread, NULL);
pthread_cancel(t);
pthread_join(t, NULL);
//Execution does not get here
}
Do you have any idea what I might try else?
Thank you in advance
pthread_mutex_lock() is just plain not a cancellation point where pthread_cancel() can cancel the thread; if you really need to break the thread, you need to find a way to release the mutex it's waiting for so it can reach a cancellation point (or, well, not use mutexes in the regions where it needs to be cancelled).
From the pthread_cancel() manual;
pthread_mutex_lock() is not a cancellation point, although it may block indefinitely; making pthread_mutex_lock() a cancellation point would make writing correct cancellation handlers difficult, if not impossible.

Resources