sleep() function gives me problem in a program.
I have declared only one mutex, one condition variable and one global variable:
pthread_mutex_t mutex;
pthread_cond_t something1;
int protected = 1;
After initializing them, and creating the 2 threads inside the main with pthread_create, i write this:
void *Thread(void *arg)
{
while(1){
pthread_mutex_lock(&mutex);
while(protected == 0){
pthread_cond_wait(&something1, &mutex);
}
printf("aaa");
sleep(2);
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
void *Thread2(void *arg){
while(1){
pthread_mutex_lock(&mutex);
while(protected == 1){
pthread_cond_wait(&something1, &mutex);
}
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
This should just print "aaa" in loop forever, and this works if I remove the sleep(2).
If I leave it, the program starts, stays alive, but it prints nothing.
Any ideas why this happens?
printf is line buffered, meaning it won't flush the buffer to screen until it reaches a newline "\n", or when the buffer is full. When you don't sleep, the buffer fills up quickly and prints to screen when it gets full, but when you do sleep it will take a long time for the buffer to fill, and so you never see anything on screen. Try changing that to printf("aaa\n");, or add an fflush(stdout); after the printf. See more information here: Why does printf not flush after the call unless a newline is in the format string?
Related
I am new to pthreads. I am trying to print even and odd numbers from two threads. What is wrong with below code? Its intention is to create two threads - one will print odd numbers and other will print even numbers. The numbers have to be printed in order. It seems to get stuck (time limit exceeded in ideone).. I have spent a lot of time staring at it. Just can't figure out what is wrong..
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock;
int n = 0;
int max = 10;
pthread_cond_t even;
pthread_cond_t odd;
void* print_odd(void *x)
{
while(1)
{
pthread_mutex_lock(&lock);
while(n%2 != 0)
{
pthread_cond_wait(&even, &lock);
}
if(n >= max)
{
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}
printf("Thread A : %d", ++n);
pthread_cond_signal(&odd);
pthread_mutex_unlock(&lock);
}
}
void* print_even(void *x)
{
while(1)
{
pthread_mutex_lock(&lock);
while(n%2 == 0)
{
pthread_cond_wait(&odd, &lock);
}
if(n >= max)
{
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}
printf("Thread B : %d", ++n);
pthread_cond_signal(&even);
pthread_mutex_unlock(&lock);
}
}
main()
{
pthread_t t1, t2;
pthread_create(&t1, NULL, print_odd, NULL);
pthread_create(&t2, NULL, print_even, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
exit(0);
}
There are multiple issues with your program-
As suggested in the comments, the lock and the conditional variables need to be initialized.
pthread_mutex_t lock = PTHREAD_LOCK_INITIALIZER;
pthread_cond_t even = PTHREAD_COND_INITIALIZER;
pthread_cond_t odd = PTHREAD_COND_INITIALIZER;
You might get lucky accidentally here even without initialization since you've declared them as global and they will be zero-inited and pthread implementations might actually be zero-initing when you properly initialize them.
Your printf doesn't have \n and hence output is not flushed to screen. Just add the newline and you'll see your threads are indeed running.
When n reaches 10, ie when print_odd threads increments from 9, it simply exits without signaling the even thread. Hence your even thread is hung in the cond_wait and your main thread is hung in pthread_join. You can fix this by waking up the even thread by signalling it before exiting the odd thread.
EDIT I found one more issue
Even if the odd thread signals the even thread just before exiting, since n=10, the even thread does NOT exit the while(n%2 == 0) loop and goes back to sleep again. This time, there's no one to wake up the poor soul. It is for this reason that you need to test the termination condition n>=max inside the while loop
pthread_cond_wait is blocking the calling threads. In your case, you have asked the threads to wait on the true conditions of odd and even. Instead, they should wait on the incorrect conditions.
While i%2 == 0, the odd thread should call the wait function inside of the routine.
While i!=2, the even thread should call wait function.
I am trying to learn how to use conditional variables properly in C.
As an exercise for myself I am trying to make a small program with 2 threads that print "Ping" followed by "Pong" in an endless loop.
I have written a small program:
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* T1(){
printf("thread 1 started\n");
while(1)
{
pthread_mutex_lock(&lock);
sleep(0.5);
printf("ping\n");
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
pthread_cond_wait(&cond,&lock);
}
}
void* T2(){
printf("thread 2 started\n");
while(1)
{
pthread_cond_wait(&cond,&lock);
pthread_mutex_lock(&lock);
sleep(0.5);
printf("pong\n");
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
}
int main(void)
{
int i = 1;
pthread_t t1;
pthread_t t2;
printf("main\n");
pthread_create(&t1,NULL,&T1,NULL);
pthread_create(&t2,NULL,&T2,NULL);
while(1){
sleep(1);
i++;
}
return EXIT_SUCCESS;
}
And when running this program the output I get is:
main
thread 1 started
thread 2 started
ping
Any idea what is the reason the program does not execute as expected?
Thanks in advance.
sleep takes an integer, not a floating point. Not sure what sleep(0) does on your system, but it might be one of your problems.
You need to hold the mutex while calling pthread_cond_wait.
Naked condition variables (that is condition variables that don't indicate that there is a condition to read somewhere else) are almost always wrong. A condition variable indicates that something we are waiting for might be ready to be consumed, they are not for signalling (not because it's illegal, but because it's pretty hard to get them right for pure signalling). So in general a condition will look like this:
/* consumer here */
pthread_mutex_lock(&something_mutex);
while (something == 0) {
pthread_cond_wait(&something_cond, &something_mutex);
}
consume(something);
pthread_mutex_unlock(&something_mutex);
/* ... */
/* producer here. */
pthread_mutex_lock(&something_mutex);
something = 4711;
pthread_cond_signal(&something_cond, &something_mutex);
pthread_mutex_unlock(&something_mutex);
It's a bad idea to sleep while holding locks.
T1 and T2 are not valid functions to use as functions to pthread_create they are supposed to take arguments. Do it right.
You are racing yourself in each thread between cond_signal and cond_wait, so it's not implausible that each thread might just signal itself all the time. (correctly holding the mutex in the calls to pthread_cond_wait may help here, or it may not, that's why I said that getting naked condition variables right is hard, because it is).
First of all you should never use sleep() to synchronize threads (use nanosleep() if you need to reduce output speed). You may need (it's a common use) a shared variable ready to let each thread know that he can print the message. Before you make a pthread_cond_wait() you must acquire the lock because the pthread_cond_wait() function shall block on a condition variable. It shall be called with mutex locked by the calling thread or undefined behavior results.
Steps are:
Acquire the lock
Use wait in a while with a shared variable in guard[*]
Do stuffs
Change the value of shared variable for synchronize (if you've one) and signal/broadcast that you finished to work
Release the lock
Steps 4 and 5 can be reversed.
[*]You use pthread_cond_wait() to release the mutex and block the thread on the condition variable and when using condition variables there is always a Boolean predicate involving shared variables associated with each condition wait that is true if the thread should proceed because spurious wakeups may occur. watch more here
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0;
void* T1(){
printf("thread 1 started\n");
while(1)
{
pthread_mutex_lock(&lock);
while(ready == 1){
pthread_cond_wait(&cond,&lock);
}
printf("ping\n");
ready = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
}
void* T2(){
printf("thread 2 started\n");
while(1)
{
pthread_mutex_lock(&lock);
while(ready == 0){
pthread_cond_wait(&cond,&lock);
}
printf("pong\n");
ready = 0;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
}
int main(void)
{
int i = 1;
pthread_t t1;
pthread_t t2;
printf("main\n");
pthread_create(&t1,NULL,&T1,NULL);
pthread_create(&t2,NULL,&T2,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return EXIT_SUCCESS;
}
You should also use pthread_join() in main instead of a while(1)
I'm writing a program in c, which make use of threads, and i also want to catch Ctrl+C signal from the user. So, before i go multithreading, i make the signal catching.
My main thread (i mean besides the actual main thread that the program runs on), is a method to deal with user input, and i also join this thread to the main program thread.
The problem is, when testing and hitting Ctrl+C to exit program,
the thread responsible for receiving user input doesn't close until i hit "return" on my keyboard - like its stuck on infinite loop.
When exiting by typing 'q', all threads end up properly.
I use a global variable exit_flag to indicate the threads to finish their loops.
Also, in init_radio_stations method there's another single thread creation, that loops in the exact same way - on the exit_flag status, and this thread DOES close properly
Here's my main loop code:
void main_loop()
{
status_type_t rs = SUCCESS;
pthread_t thr_id;
/* Catch Ctrl+C signals */
if(SIG_ERR == signal(SIGINT, close_server)) {
error("signal() failed! errno = ");
}
printf("\n~ Welcome to radio_server! ~\n Setting up %d radio stations... ", srv_params.num_of_stations);
init_radio_stations();
printf("Done!\n\n* Hit 'q' to exit the application\n* Hit 'p' to print stations & connected clients info\n");
/* Create and join a thread to handle user input */
if(pthread_create(&thr_id, NULL, &rcv_usr_input, NULL)) {
error("main_loop pthread_create() failed! errno = ");
}
if(pthread_join(thr_id, NULL)) {
error("main_loop pthread_join() failed! errno = ");
}
}
close_server method:
void close_server(int arg)
{
switch(arg) {
case SIGINT: /* 2 */
printf("\n^C Detected!\n");
break;
case ERR: /* -1 */
printf("\nError occured!\n");
break;
case DEF_TO: /* 0 */
printf("\nOperation timed-out!\n");
break;
default: /* will handle USER_EXIT, and all other scenarios */
printf("\nUser abort!\n");
}
printf("Signaling all threads to free up all resources and exit...\n");
/* Update exit_flag, and wait 1 sec just in case, to give all threads time to close */
exit_flag = TRUE;
sleep(1);
}
And rcv_usr_input handle code:
void * rcv_usr_input(void * arg_p)
{
char in_buf[BUFF_SIZE] = {0};
while(FALSE == exit_flag) {
memset(in_buf, 0, BUFF_SIZE);
if(NULL == fgets(in_buf, BUFF_SIZE, stdin)) {
error("fgets() failed! errno = ");
}
/* No input from the user was received */
if('\0' == in_buf[0]) {
continue;
}
in_buf[0] = tolower(in_buf[0]);
if( ('q' == in_buf[0]) && ('\n' == in_buf[1]) ) {
close_server(USER_EXIT);
} else {
printf("Invalid input!\nType 'q' or 'Q' to exit only\n");
}
}
printf("User Input handler is done\n");
return NULL;
}
I'm guessing my problem is related to joining the thread that uses rcv_usr_input at the end of my main loop, but i can't figure out what exactly causing this behavior.
I'll be glad to get some help, Thanks
Mike and Kaylum have correctly identified the fundamental problem of blocking by fgets(). The larger issue remains, however: how to terminate a blocking thread when the process receives a SIGINT. There are several solutions.
Thead Detachment:
One solution is to detach the blocking thread because detached threads do not prevent the process from terminating when the last non-detached thread terminates. A thread is detached either by calling pthread_detach() on it, e.g.,
#include <pthread.h>
// Called by pthread_create()
static void* start(void* arg)
{
pthread_detach();
...
}
or by creating the thread with the PTHREAD_CREATE_DETACHED attribute, e.g.,
#include <pthread.h>
...
pthread_attr_t attr;
(void)pthread_attr_init(&attr);
(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
...
(void)pthread_t thread;
(void)pthread_create(&thread, &attr, ...);
Note that pthread_join() should not be called on a detached thread.
Signal Forwarding: Another solution is not to detach the blocking thread but to forward signals like SIGINT to the thread via pthread_kill() if the signal has not been received on the blocking thread, e.g.,
#include <pthread.h>
#include <signal.h>
...
static pthread_t thread;
...
static void handle_sigint(int sig)
{
if (!pthread_equal(thread, pthread_self()) // Necessary
(void)pthread_kill(thread, SIGINT);
}
...
sigaction_t sigaction;
sigaction.sa_mask = 0;
sigaction.sa_flags = 0;
sigaction.sa_handler = handle_sigint;
(void)sigaction(SIGHUP, &sigaction, ...);
...
(void)pthread_create(&thread, ...);
...
(void)pthread_join(thread, ...);
...
This will cause the blocking function to return with errno set to EINTR.
Note that use of signal() in a multi-threaded process is unspecified.
Thread Cancellation: Another solution is to cancel the blocking thread via pthread_cancel(), e.g.,
#include <pthread.h>
...
static void cleanup(...)
{
// Release allocated resources
...
}
...
static void* start(void* arg)
{
pthread_cleanup_push(cleanup, ...);
for (;;) {
...
// Call the blocking function
...
}
pthread_cleanup_pop(...);
...
}
....
static void handle_sigint(int sig)
{
(void)pthread_cancel(thread);
}
...
sigaction_t sigaction;
sigaction.sa_mask = 0;
sigaction.sa_flags = 0;
sigaction.sa_handler = handle_sigint;
(void)sigaction(SIGHUP, &sigaction, ...);
...
(void)pthread_create(&thread, ..., start, ...);
...
(void)pthread_join(thread, ...);
...
There is yet another solution for threads that block in a call to select() or poll(): create a file descriptor on which the blocking function also waits and close that descriptor upon receipt of an appropriate signal -- but that solution is, arguably, beyond the scope of this question.
According to http://www.cplusplus.com/reference/cstdio/fgets/, fgets blocks until the specified number of bytes have been read.
I suggest trying fread or some other input reception function that isn't blocking and then read only one byte at a time. Here's sample code to help you:
if (fread(in_buf, 1,1, stdin) > 0){
//character has been read
}
And I wouldn't worry about the extra sleep statement in your signal handler as it causes delays in forceful exiting at best.
The explanation is straight forward.
fgets(in_buf, BUFF_SIZE, stdin);
That call blocks the thread until it receives a line of input. That is, it does not return until a newline is input or BUFF_SIZE-1 characters are input.
So even though the signal handler sets exit_flag to FALSE, the rcv_usr_input thread will not see that until it unblocks from fgets. Which happens when you pressed "return".
I have two threads
xThread : Continuously Prints X on the console
inputThread: Gets input from the stdin
The continuous printing stops when the user enters 'C' or 'c'
#include<stdio.h>
#include<sys/select.h>
#include<pthread.h>
#define S sleep(0)
int read_c = 0;
pthread_mutex_t read_c_mutex = PTHREAD_MUTEX_INITIALIZER;
void* inputThread_fn(void* arg)
{
char inputChar;
while(1)
{
S;
printf("\nChecking input");
scanf("%c",&inputChar);
if(inputChar=='C' || inputChar == 'c')
{
pthread_mutex_trylock(&read_c_mutex); /*<--This must be _lock ?
because with the use of trylock even If i don't aquire a lock I go ahead and modify
the variable?*/
read_c = 1;
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
void* xThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
printf(" X");
else
pthread_exit(NULL);
pthread_mutex_unlock(&read_c_mutex);
}
}
void* yThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
printf(" Y");
else
pthread_exit(NULL);
pthread_mutex_unlock(&read_c_mutex);
}
}
int main()
{
pthread_t xThread,yThread,inputThread;
pthread_create(&xThread,NULL,xThread_fn,NULL);
pthread_create(&inputThread,NULL,inputThread_fn,NULL);
pthread_join(xThread,NULL);
pthread_join(inputThread,NULL);
return 0;
}
When I use sleep(1) the threads are spawned and [irrespective of which thread is started first] when the program reaches scanf in inputThread it halts for the user input and the code does not proceed until I enter an input.
When I execute the code with sleep(0), scanf does not halt for the input, it keeps printing 'X' until I enter 'C' or 'c'
Does sleep() interfere with scanf in someway?
Note: I am aware of select being used for non-blocking input. I have tried the same too and the code runs fine. I just want to know in the above case why inconsistent behaviour arises?
Update (Using trylock)
void* inputThread_fn(void* arg)
{
char inputChar;
while(1)
{
S;
scanf("%c",&inputChar);
if(inputChar=='C' || inputChar == 'c')
{
pthread_mutex_trylock(&read_c_mutex);
read_c = 1;
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
void* xThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
{
pthread_mutex_unlock(&read_c_mutex);
printf(" X");
}
else
{
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
fflush(stdout);
}
}
void* yThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
{
pthread_mutex_unlock(&read_c_mutex);
printf(" Z");
fflush(stdout);
}
else
{
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
The reason you don't see output is because you're not flushing the buffer.
The reason you don't need to flush the buffer with sleep(0) is because the writer thread writes so much data that the buffer fills up and is automatically flushed.
#define SLEEP_TIME 1
void* xThread_fn(void* arg)
{
while (1) {
sleep(SLEEP_TIME);
pthread_mutex_lock(&read_c_mutex);
if (read_c) {
pthread_mutex_unlock(&read_c_mutex);
return NULL;
}
pthread_mutex_unlock(&read_c_mutex);
printf(" X");
fflush(stdout); // <-- necessary
}
}
Don't use pthread_mutex_trylock()
Don't use pthread_mutex_trylock() here. It's wrong.
The difference between lock() and trylock() is that lock() will always succeed1 but trylock() will sometimes fail. That's why it's called "try".
Since trylock() sometimes fails, you have to handle the case where it failed. Your code doesn't handle the case: it simply plows forward, pretending it acquired the lock. So, suppose trylock() doesn't lock the mutex. What happens?
pthread_mutex_trylock(&read_c_mutex); // Might fail (i.e., not lock the mutex)
read_c = 1; // Modifying shared state (Wrong!)
pthread_mutex_unlock(&read_c_mutex); // Unlocking a mutex (Wrong!)
Then there's the question of how the code should handle trylock() failing. If you can't answer this question, then the default answer is "use lock()".
In the reader thread, you can't use trylock() because you have to lock the mutex:
int r = pthread_mutex_trylock(&read_c_mutex);
if (r != 0) {
// Uh... what are we supposed to do here? Try again?
} else {
read_c = 1;
pthread_mutex_unlock(&read_c_mutex);
}
In the writer thread, there's no point in using trylock():
int r = pthread_mutex_trylock(&read_c_mutex);
if (r != 0) {
// Okay, just try again next loop...
} else {
if (read_c) {
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
} else {
pthread_mutex_unlock(&read_c_mutex);
}
}
However, this is entirely pointless. The only reason trylock() will fail in the writer thread is if the reader thread owns the lock, which only happens if it is currently in the process of setting read_c = 1;. So you might as well wait for it to finish, since you know you're going to exit anyway (why write more output after you know that the user has signaled your program to stop?)
Just use lock(). You'll use lock() 99% of the time, and trylock() is for the other 1%.
1: The lock() function can fail, but this usually means you've misused the mutex.
Misconceptions about lock() and trylock()
You said this about trylock():
If i have another thread accessing the variable read_input then will it be appropriate to use it?
I think there is a very fundamental misunderstanding here about the nature of mutexes. If another thread weren't accessing the variable at the same time, then you wouldn't need a mutex at all.
Suppose you're doing important work at the office, and you need to use the photocopier. Only one person can use the photocopier at a time. You go to the photocopier and someone's already using it.
If you wait in line until it's your turn, then that's lock().
If you give up and go back to your desk, then that's trylock(). (Your program actually ignores the return code for trylock(), so you basically start mashing buttons on the photocopier even if someone else is using it.)
Now imagine that it takes one minute to use the photocopier, only two people ever use the photocopier, and they only use the photocopier once every twenty years.
If you use lock(), then you wait in line for at most one minute before using the photocopier.
If you use trylock(), then you give up and go back to your desk and wait twenty years before trying the photocopier again.
It doesn't make any sense to use trylock(), does it? Are your threads so impatient that they can't spend even one minute in line once every twenty years?
Now your boss comes down and said, "Where is that report I asked you to photocopy?" And you say, "Well, I went to the photocopier six years ago but someone was using it."
The numbers (one minute every twenty years) are based on Latency Numbers Every Programmer Should Know, where it notes that locking/unlocking a mutex is about 25ns. So if we pretend that it takes one minute to lock and then unlock a mutex, then sleep(1) causes the thread to wait for twenty years.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char work_area[WORK_SIZE];
void *thread_start(void *);
int main() {
pthread_t a_thread;
pthread_mutex_init(&work_mutex,NULL);
pthread_create(&a_thread,NULL,thread_start,NULL);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("Enter some text\n");
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
sleep(1);
while(1)
{
pthread_mutex_lock(&work_mutex);
printf("You enetered %d char",strlen(work_area));
pthread_mutex_unlock(&work_mutex);
}
}
When I execute the program, after releasing of the mutex lock in main thread, it again aquires the lock, everytime, before the second thread could aquire the lock. I was expecting that once the main thread would release the lock, the second thread which is already blocked would aquire the lock and start execution before the main.
To be more clear, I am getting such type of output :-
Enter some text
qwerty
Enter some text
asdaf
Enter some text
jkdf
Enter some text
It just seems that way to you. You lock before doing data entry in main which is going to be orders of magnitude greater than what it take to output the line. In all that time the other thread will do nothing but block. Your main is going to release the lock and microseconds later acquire it again.
If you do this long enough - maybe thousands of times - you will see it work. But it would be better to just copy the input line in main to a queue or some other piece of memory protect by the lock. Then the other thread would have a chance to get at it.
EDIT:
The general idea is this. My code additions are terrible but should work well enough for illustration.
int main()
{
pthread_t a_thread;
pthread_mutex_init(&work_mutex, NULL);
pthread_create(&a_thread, NULL, thread_start, NULL);
memset(work_area, '\0', sizeof(work_area));
char input[WORK_SIZE - 1];
while (1)
{
printf("Enter some text\n");
fgets(input, WORK_SIZE, stdin);
pthread_mutex_lock(&work_mutex);
strcpy(work_area, input);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
sleep(1);
while (1)
{
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '\0')
{
printf("You enetered %d char\n", strlen(work_area));
work_area[0] = '\0';
}
pthread_mutex_unlock(&work_mutex);
}
}
Putting asside the suggestion to use a semaphore, I think that the reason for the behaviour that you are observing is as follows.
Until the pthread_mutex_lock in thread_start is called, the loop in main won't be blocked.
Therefore, the only time that thread_start's loop will get a chance to call pthread_mutex_lock is when the time slice of the thread executing main expires
The chances of that time slice expiry occuring while the lock is released is miniscule. This is because the main thread will probably have a fresh time slice when it wakes from the blocked state while it waited for the ENTER key.
Note, this explanation assumes a single core. But even on a multicore system the thread_start thread is only going to be scheduled when another thread's time slice runs out. The chances of that happening while main's thread isn't holding the lock is small.
One possible test for my above hypothesis would be to call pthread_yield after releasing the lock. You'll probably want to do that in both threads. Even then I don't think it will GUARANTEE a thread switch every time.
You may want to create and initialise a semaphore and then wait in the 2nd thread for the main function to signal a event when a input is fed to it.
Check conditional wait and semaphores.
The second thread doesnt know what event is generated in the main thread. For your reference.
After reading the comments given by torak, I changed the code. Now it is working fine as expected.
[root#localhost threads]# diff -Nurp mutex.c mutex_modified.c
--- mutex.c 2010-07-09 19:50:51.000000000 +0530
+++ mutex_modified.c 2010-07-09 19:50:35.000000000 +0530
## -18,6 +18,7 ## pthread_mutex_lock(&work_mutex);
printf("Enter some text\n");
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
+sleep(1);
}
return 0;
}
## -30,5 +31,6 ## while(1)
pthread_mutex_lock(&work_mutex);
printf("You enetered %d char",strlen(work_area));
pthread_mutex_unlock(&work_mutex);
+sleep(1);
}
}
It is still very confusing though. Although it was a test program, what should one do to avoid such kind of situation while coding a real application?
Here's a slightly more awkward solution that's guaranteed to work: #include
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define WORK_SIZE 1024
pthread_mutex_t work_mutex;
char input_area[WORK_SIZE];
char work_area[WORK_SIZE];
void *thread_start(void *);
int main()
{
pthread_t a_thread;
pthread_mutex_init(&work_mutex,NULL);
work_area[0] = 0;
pthread_create(&a_thread,NULL,thread_start,NULL);
while(1) {
printf("Enter some text\n");
fgets(input_area, WORK_SIZE, stdin);
pthread_mutex_lock(&work_mutex);
while (work_area[0] != 0) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
memcpy(work_area, input_area, WORK_SIZE);
pthread_mutex_unlock(&work_mutex);
}
return 0;
}
void *thread_start(void *arg)
{
while(1)
{
pthread_mutex_lock(&work_mutex);
if (work_area[0] > 0) {
printf("you enetered %d char\n",strlen(work_area));
work_area[0] = 0;
pthread_mutex_unlock(&work_mutex);
} else {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
}
}
Note that because POSIX mutex locks are thread-specific, the consumer thread can't wait on a signal from the producer thread, so here it just wakes up every second to check for new data. Similarly, if the producer needs to put something in queue and the work area is full, it waits by seconds until the consumer gets around to emptying the buffer. You could get rid of some delays by using pthread_yield instead of sleep, but then your threads will 'busy-wait', consuming lots of CPU to check again and again for their condition to be met
Note that if you want it to print a line for 0-character entries you could add a separate bool indicating whether there's new data in the queue.
#torax
*"Note, this explanation assumes a single core. But even on a multicore system the thread_start thread is only going to be scheduled when another thread's time slice runs out"*
I think this is not true, the main thread and the thread_start can be scheduled parallely on both cores