I have implemented a conditional variable example. However, I see that once I sleep after the signal is received by the pthread_cond_wait. The code after sleep is not executed. It directly jumps to the else condition. I understand that the mutex is unlocked now. I have just two threads A and B. Both the threads have two different startup routine. If I put a sleep in thread A after getting a signal, then it should schedule thread B. Now, once thread B is done. The thread A should resume from the place after sleep. However, it is resuming not from sleep. It re-acquires the lock. See my example, and it's output. You will notice that the following lines never got printed - To quickly, troubleshoot my issue- just look at the following function - void *Thread_Function_A(void *thread_arg) and void *Thread_Function_B(void *thread_arg). Other functions are not that important.
The cond_wait is unblocked now
The thread A proceeds
Thread A unlocked
Here is the program now -
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
/** get pid **/
#include <sys/types.h>
#include <unistd.h>
/** kill signal **/
#include <signal.h>
using namespace std;
int shared_variable = 7;
pid_t pid_A;
pid_t pid_B;
class helium_thread
{
private:
pthread_t *thread_id;
pid_t process_pid;
public:
static pthread_mutex_t mutex_thread;
static pthread_cond_t cond_var;
void set_thread_id(pthread_t tid);
pthread_t *get_thread_id();
int create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg );
helium_thread();
~helium_thread();
};
helium_thread thread_1, thread_2;
/** The definition of the static member can't be inside a function, You need to put it outside **/
/** When I tried using inside a function, I got the error - error: invalid use of qualified-name ‘helium_thread::mutex_thread **/
pthread_mutex_t helium_thread::mutex_thread = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t helium_thread::cond_var = PTHREAD_COND_INITIALIZER;
void helium_thread::set_thread_id( pthread_t tid)
{
*(this->thread_id) = tid;
}
pthread_t * helium_thread::get_thread_id( )
{
return (this->thread_id);
}
int helium_thread::create_thread(pthread_t *thread_ptr, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg )
{
int ret;
ret = pthread_create(thread_ptr,attr,start_routine,(void *)arg) ;
cout<<"Thread created "<<std::hex<<thread_ptr<<endl;
return ret;
}
helium_thread::helium_thread()
{
thread_id = new pthread_t;
cout<<"Constructor called "<<std::hex<<thread_id<<endl;
}
helium_thread::~helium_thread()
{
cout<<"Destructor called"<<std::hex<<thread_id<<endl;
delete thread_id;
}
/** While defining the methods of the class, Keywords static and virtual should not be repeated in the definition. **/
/** They should only be used in the class declaration. **/
void handler(int sig)
{
//do nothing
cout<<"Handler called"<<endl;
}
void *Thread_Function_A(void *thread_arg)
{
int rc = 0;
pid_A = getpid();
cout<<"The pid value of Thread A is"<< pid_A << endl;
while(1)
{
pthread_mutex_lock(&(helium_thread::mutex_thread));
cout<<"Thread A lock acquire first"<<endl;
if ( shared_variable != 5)
{
/** Now you put a sleep to introduce a race condition **/
/** You will find that there is no race condition here **/
cout<<"Going to conditional wait"<<endl;
//cout<<"Sleep thread A"<<endl;
//sleep(5);
pthread_cond_wait(&helium_thread::cond_var, &helium_thread::mutex_thread);
cout<<"Sleep after cond_wait"<<endl;
sleep(5);
cout<<"The cond_wait is unblocked now "<<endl;
cout<<"The thread A proceeds"<<endl;
cout<<"The shared_variable value = "<< std::dec<< shared_variable << endl;
pthread_mutex_unlock(&(helium_thread::mutex_thread));
cout<<"Thread A unlocked"<<endl;
}
else
{
cout<<"Else condition thread A..shared variable value is "<<shared_variable<<endl;
cout<<"The condition of thread A is met now"<<endl;
pthread_mutex_unlock(&(helium_thread::mutex_thread));
cout<<"Thread A unlocked in else condition"<<endl;
pthread_exit(NULL);
}
}
}
void *Thread_Function_B(void *thread_arg)
{
pthread_mutex_lock(&(helium_thread::mutex_thread));
pid_B = getpid();
cout<<"The pid value of Thread B is"<< pid_B << endl;
shared_variable = 5;
/** Now you put a sleep to introduce a race condition **/
/** You will find that there is no race condition here **/
//sleep(5);
cout<<"Signal the thread A now "<<endl;
pthread_cond_signal (&helium_thread::cond_var);
cout<<"Changed the shared_variable value now"<<endl;
pthread_mutex_unlock(&(helium_thread::mutex_thread));
cout<<"Return thread function b now"<<endl;
}
int main(int argc, char *argv[])
{
pid_t thread_pid_val = getpid();
thread_1.create_thread((thread_1.get_thread_id()),NULL,Thread_Function_A,&thread_pid_val);
thread_2.create_thread((thread_2.get_thread_id()),NULL,Thread_Function_B,&thread_pid_val);
pthread_join( *(thread_1.get_thread_id()), NULL);
pthread_join( *(thread_2.get_thread_id()), NULL);
return 0;
}
The output is the following.
$ ./thread_basic.out
Constructor called 0x2012010
Constructor called 0x2012030
Thread created 0x2012010
The pid value of Thread A is5bfd
Thread created 0x2012030
Thread A lock acquire first
Going to conditional wait
The pid value of Thread B is5bfd
Signal the thread A now
Changed the shared_variable value now
Return thread function b now
Sleep after cond_wait
The cond_wait is unblocked now
The thread A proceeds
The shared_variable value = 5
Thread A unlocked
Thread A lock acquire first
Else condition thread A..shared variable value is 5
The condition of thread A is met now
Thread A unlocked in else condition
Destructor called0x2012030
Destructor called0x2012010
condition_variable::wait
is designed to take a lambda so that it is impossible to forget to loop and thereby ensure correctness, among other helpful design-induced safety mechanisms. – Marksleep()
is never taken. You might want to output something before waiting on the condition variable. – Dietmar Kühl