thread A created first, so thread A runs first
The fact that thread A was created first does not necessarily mean that thread A will be the first thread to lock the mutex. It is possible for thread B to be faster.
If you want to ensure that thread A does something before thread B, you must make thread B wait for thread A to be finished doing that thing (for example using pthread_cond_wait).
B runs and prints next sequence then sends signal and Thread A wakes up
Thread A will not be able to awaken until the condition varible is signaled AND the mutex is unlocked.
Thread B locks the mutex again a very short time after unlocking it. Mutexes are not guaranteed to be fair, and are often implemented in such a way that they favor performance over fairness. Therefore, you cannot rely on Thread A having an opportunity to acquire the mutex.
If you want to ensure that thread A gets an opportunity to wake up, you must make thread B wait for thread A (for example using pthread_cond_wait).
In order to ensure that the threads take turns in printing one character, I suggest that you add a variable named turn to your program which indicates which thread's turn it is to print the next character. This variable should be protected by the mutex. You should also add a second condition variable, so that each thread has its own condition variable to wait on.
Another issue in your code is that your cleanup code in the main thread is not being executed, because you have a pthread_exit function call beforehand. I am not sure if this is intentional.
Your cleanup code should have calls to pthread_join even if those threads have been canceled with pthread_cancel. Otherwise, you will have a resource leak. Another reason to use pthread_join is that you should not attempt to destroy the mutex or the condition variable(s) while one of the threads is still running.
Even if you wait for the threads to complete using pthread_join, it is possible that one of the threads will terminate while leaving the mutex in a locked state. See this answer to another question for further information. This will cause the function call to pthread_mutex_destroy to invoke undefined behavior. This is because according to POSIX, calling pthread_mutex_destroy on a locked mutex invokes undefined behavior.
In order to prevent this undefined behavior from being possibly invoked, it would probably be better to use a different mechanism for stopping the threads than pthread_cancel, because it is difficult to use pthread_cancel in such a way that the mutex gets unlocked before the thread terminates. Therefore, instead of using pthread_cancel, I suggest that the main thread sets a variable to tell the other threads to stop. I do not recommend protecting this variable with the mutex, because the main thread may not be able to acquire the mutex in a timely manner, since the other threads are constantly contending for the mutex. Instead, I recommend an atomic variable.
After doing the above, the code will look like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>
int turn = 0;
atomic_bool terminate = false;
int string_index = 0;
char string_to_print[10] = "0123456789";
pthread_mutex_t lock;
pthread_cond_t condA, condB;
void *funcA(void *arg)
{
pthread_mutex_lock(&lock); // lock mutex
while (1)
{
if (terminate)
{
pthread_mutex_unlock(&lock);
pthread_cond_signal(&condB);
pthread_exit(NULL);
}
if (turn == 0)
{
printf("A%c", string_to_print[string_index]);
string_index++;
turn = 1;
pthread_cond_signal(&condB); //send cond var signal
}
pthread_cond_wait(&condA, &lock); //sleep and wait for signal
}
}
void *funcB(void *arg)
{
pthread_mutex_lock(&lock); //lock mutex
while (1)
{
if (terminate)
{
pthread_mutex_unlock(&lock);
pthread_cond_signal(&condA);
pthread_exit(NULL);
}
if (turn == 1)
{
printf("B%c", string_to_print[string_index]);
string_index++;
if(string_index > 9)
{
string_index = 0;
printf("\n");
}
turn = 0;
pthread_cond_signal(&condA); //send cond var signal
}
pthread_cond_wait(&condB, &lock); //sleep and wait for signal
}
}
int main(void)
{
//initialize mutex lock
if (pthread_mutex_init(&lock, NULL) != 0)
{
printf("\n mutex init has failed\n");
return 1;
}
//initialize conditional variables
if (pthread_cond_init(&condA, NULL) != 0)
{
printf("cond var init failed");
return 1;
}
if (pthread_cond_init(&condB, NULL) != 0)
{
printf("cond var init failed");
return 1;
}
pthread_t A,B;
//create thread A
if (pthread_create(&A, NULL, &funcA, NULL) != 0)
{
printf("Error creating thread");
exit(-1);
}
//create thread B
if (pthread_create(&B, NULL, &funcB, NULL) != 0)
{
printf("Error creating thread");
exit(-1);
}
sleep(20);
// set this atomic variable instead of using pthread_cancel
terminate = true;
//pthread_cancel(A);
//pthread_cancel(B);
pthread_join(B, NULL);
pthread_join(A, NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&condB);
pthread_cond_destroy(&condA);
}
This code has the following output:
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
A0B1A2B3A4B5A6B7A8B9
[...]
As you can see, the threads are now reliably taking turns in printing characters.
It may be worth noting that if you are sure that you have at least two hardware threads, you can also use a single atomic variable for thread synchronization, instead of mutexes and condition variables. That way, the threads will busy-wait instead of falling asleep:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdatomic.h>
atomic_int turn = 0;
int string_index = 0;
char string_to_print[10] = "0123456789";
void *funcA(void *arg)
{
while (1)
{
switch ( turn )
{
case 0:
printf("A%c", string_to_print[string_index]);
string_index++;
turn = 1;
break;
case 1:
continue;
default:
pthread_exit( NULL );
}
}
}
void *funcB(void *arg)
{
while (1)
{
switch ( turn )
{
case 0:
continue;
case 1:
printf("B%c", string_to_print[string_index]);
string_index++;
if(string_index > 9)
{
string_index = 0;
printf("\n");
}
turn = 0;
break;
default:
pthread_exit( NULL );
}
}
}
int main(void)
{
pthread_t A,B;
//create thread A
if (pthread_create(&A, NULL, &funcA, NULL) != 0)
{
printf("Error creating thread");
exit(-1);
}
//create thread B
if (pthread_create(&B, NULL, &funcB, NULL) != 0)
{
printf("Error creating thread");
exit(-1);
}
sleep(20);
turn = 2;
pthread_join(B, NULL);
pthread_join(A, NULL);
}
This program has the same output as the other one.
funcBto wait forfuncAto do its thing. WhenfuncBcallspthread_cond_signal, it immediately loops and callspthread_mutex_lock. It takes time for the system to send the signal to the other thread and for that thread to wake up. Before that happens,funcBhas taken the lock again, and thenfuncAcannot get the lock and goes back to sleep, waiting. You need both threads to signal the other and to wait for a signal from the other.