Synchronization using Pthreads mutex and conditional variables in C - c

I am trying to create two threads resembling TaskA and TaskB. Both TaskA and TaskB do some kind of computation that it is not very interesting for this post. TaskA and TaskB have to be executed 10 times in order to cover the whole array. TaskA has an input AA and an output BB. BB is also the input of TaskB. CC is the output of TaskB. Because BB is written by taskA and read by taskB we need mutexes.
The behavior I would like to achieve is that when TaskA operates on i, TaskB operates on i-1 in parallel, where i is the number of arrays that are processed.
I want to avoid TaskB to wait for TaskA to finish for every i.
The problem here is that I have a deadlock. ThreadA and ThreadB represent TaskA and TaskB. To make it easier I removed all the computations and I left only synchronization instructions. The deadlock is caused because ThreadA signals the conditional variable CV[0] before threadB is in the state that waits for CV[0].
Do you know any way to remove the deadlock but without TaskA waiting for TaskB to finish and vice versa. Ideally when TaskA operates on array i TaskB should operate on array i-1.
/* Includes */
#include <unistd.h> /* Symbolic Constants */
#include <sys/types.h> /* Primitive System Data Types */
#include <errno.h> /* Errors */
#include <stdio.h> /* Input/Output */
#include <stdlib.h> /* General Utilities */
#include <pthread.h> /* POSIX Threads */
#include <string.h> /* String handling */
#include <semaphore.h> /* Semaphore */
#include <stdint.h>
#define ARRAY_SIZE 2048*2400
#define DEBUG
//#define CHECK_RESULTS
pthread_mutex_t mutex[10];
pthread_cond_t cv[10];
/* prototype for thread routine */
void threadA ( void *ptr );
void threadB ( void *ptr );
struct thread_arg
{
uint32_t *in;
uint32_t *out;
uint32_t ID;
};
int main()
{
pthread_t pthA;
pthread_t pthB;
//Memory allocation
uint32_t *AA = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
uint32_t *BB = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
uint32_t *CC = malloc(10*ARRAY_SIZE*sizeof(uint32_t));
unsigned int j,i;
// THread Arguments
struct thread_arg arguments[2];
arguments[0].in = AA;
arguments[0].out = BB;
arguments[0].ID = 1;
arguments[1].in = BB;
arguments[1].out = CC;
arguments[1].ID = 2;
//Init arguments data
for (j=0;j<10;j++)
{
for (i=0;i<ARRAY_SIZE;i++)
{
AA[j*ARRAY_SIZE+i] = i;
BB[j*ARRAY_SIZE+i] = 0;
CC[j*ARRAY_SIZE+i] = 99 ;
}
}
//Semaphore and conditional variables init
for (i=0;i<10;i++){
pthread_mutex_init(&mutex[i], NULL);
pthread_cond_init (&cv[i], NULL);
}
pthread_create (&pthA, NULL, (void *) &threadA, (void *) &arguments[0]);
pthread_create (&pthB, NULL, (void *) &threadB, (void *) &arguments[1]);
pthread_join(pthA, NULL);
pthread_join(pthB, NULL);
// Destroy Semaphores and CVs
for (i=0;i<10;i++)
{
pthread_mutex_destroy(&mutex[i]);
pthread_cond_destroy(&cv[i]);
}
// Checking results
exit(0);
} /* main() */
void threadA ( void *ptr )
{
int i;
struct thread_arg *arg = (struct thread_arg *) ptr;
for (i=0;i<10;i++)
{
pthread_mutex_lock(&mutex[i]);
printf("TA: LOCK_M%d \n",i);
pthread_cond_signal(&cv[i]);
printf("TA: SIG_CV%d\n",i);
pthread_mutex_unlock(&mutex[i]);
printf("TA: UNL_M%d\n",i);
}
pthread_exit(0); /* exit thread */
}
void threadB ( void *ptr )
{
int i;
struct thread_arg *arg = (struct thread_arg *) ptr;
for (i=0;i<10;i++)
{
pthread_mutex_lock(&mutex[i]);
printf("TB: WAIT_CV%d\n",i,i);
pthread_cond_wait(&cv[i], &mutex[i]);
printf("TB CV%d_PASSED\n",i);
pthread_mutex_unlock(&mutex[i]);
printf("TB UNL_M%d \n",i);
}
pthread_exit(NULL);
}

As WhozCraig commented, a condition variable needs to be paired with a condition over some shared state, known as a predicate. The mutex is used to protect the shared state.
In this example, your shared state could be an integer that contains the highest index of BB[] that ThreadA has produced. ThreadB then waits for this number to reach the index that it is up to reading. In this design, you only need one mutex and one condition variable. The globals would then be:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int BB_ready = -1; /* Protected by 'mutex' */
(Using the static PTHREAD_*_INITIALIZER initialisers means that you don't need to bother with pthread_*_init() and pthread_*_destroy()).
The loop in ThreadA would then be:
for (i=0;i<10;i++)
{
/* Process AA[i] into BB[i] here */
/* Now mark BB[i] as ready */
pthread_mutex_lock(&mutex);
printf("TA: LOCK_M%d \n",i);
BB_ready = i;
pthread_cond_signal(&cv);
printf("TA: SIG_CV%d\n",i);
pthread_mutex_unlock(&mutex);
printf("TA: UNL_M%d\n",i);
}
..and in ThreadB:
for (i=0;i<10;i++)
{
/* Wait for BB[i] to be ready */
pthread_mutex_lock(&mutex);
printf("TB: WAIT_CV%d\n",i);
while (BB_ready < i)
pthread_cond_wait(&cv, &mutex);
printf("TB CV%d_PASSED\n",i);
pthread_mutex_unlock(&mutex);
printf("TB UNL_M%d \n",i);
/* Now process BB[i] into CC[i] here */
}
Notice that pthread_cond_signal() is called whenever the shared state has changed, which allows the other thread to wake up and re-check the state, if it's waiting.
The waiting thread always loops around, checking the state and then waiting on the condition variable if the state isn't ready yet.

Related

How can I make the pthread tasks executes in the correct order according to their priorities?

I'm trying to create 3 thread task with different priorities. The task will only wait or signal other thread tasks, and save the execution in char array to see that they executed as supposed.
Here is the code
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
static void *pT1();
static void *pT2();
static void *pT3();
static void trigT3();
static void trigT2();
pthread_cond_t gCondVar1;
pthread_cond_t gCondVar2;
pthread_cond_t gCondVar3;
pthread_cond_t gFinish;
pthread_mutex_t gLock1;
pthread_mutex_t gLock2;
pthread_mutex_t gLock3;
pthread_mutex_t gLockF;
static char savedExe[4];
static int gPos = 0;
static bool gSignalT2 = false;
static bool gSignalT3 = false;
int main(int argc, char const *argv[])
{
struct sched_param param1;
struct sched_param param2;
struct sched_param param3;
pthread_attr_t attr1;
pthread_attr_t attr2;
pthread_attr_t attr3;
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_attr_init(&attr1);
pthread_attr_init(&attr2);
pthread_attr_init(&attr3);
pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
pthread_attr_setschedpolicy(&attr2, SCHED_FIFO);
pthread_attr_setschedpolicy(&attr3, SCHED_FIFO);
param1.sched_priority = 10;
param2.sched_priority = 20;
param3.sched_priority = 30;
pthread_attr_setschedparam(&attr1, &param1);
pthread_attr_setschedparam(&attr2, &param2);
pthread_attr_setschedparam(&attr3, &param3);
pthread_create(&tid1, &attr1, pT1, NULL);
pthread_create(&tid2, &attr2, pT2, NULL);
pthread_create(&tid3, &attr3, pT3, NULL);
for (int i = 0; i < sizeof(savedExe); ++i) {
printf("%c, ", savedExe[i]);
}
printf("\b\b\n");
pthread_cond_signal(&gCondVar1);
/*.
.
.
.*/
pthread_cond_wait(&gFinish, &gLockF);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
for (int i = 0; i < sizeof(savedExe); ++i) {
printf("%c, ", savedExe[i]);
}
printf("\b\b\n");
return 0;
}
static void trigT3(){
pthread_mutex_lock(&gLock3);
pthread_cond_signal(&gCondVar3);
gSignalT3 = true;
pthread_mutex_unlock(&gLock3);
}
static void trigT2(){
pthread_mutex_lock(&gLock2);
pthread_cond_signal(&gCondVar2);
gSignalT2 = true;
pthread_mutex_unlock(&gLock2);
}
static void *pT1(){
pthread_mutex_lock(&gLock1);
pthread_cond_wait(&gCondVar1, &gLock1);
trigT3();
gPos++;
savedExe[gPos] = '1';
pthread_mutex_unlock(&gLock1);
pthread_cond_signal(&gFinish);
return NULL;
}
static void *pT3(){
//printf("T3\n");
pthread_mutex_lock(&gLock3);
if(!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
gSignalT3 = false;
}
trigT2();
gPos++;
savedExe[gPos] = '3';
pthread_mutex_unlock(&gLock3);
return NULL;
}
static void *pT2(){
pthread_mutex_lock(&gLock2);
if(!gSignalT2){
pthread_cond_wait(&gCondVar2, &gLock2);
gSignalT3 = false;
}
gPos++;
savedExe[gPos] = '2';
pthread_mutex_unlock(&gLock2);
return NULL;
}
In the main, I create the 3 different tasks and assign them with different priorities. pT1 has the lowest priority = 10, pT2 = 20 and pT3 = 30.
pT1 starts the execution first and since its a lower priority and it triggers pT3, pT3 will start executing. pT3 will trigger pT2 but since pT2 is lower priority, pT3 will finish executing and then it will trigger pT2.
The output of this should be 3, 2, 1.
But what I get is 1, 2, 3, as if it does not take care to any priority at all and just executes the tasks in the given order.
Priority affects how much CPU time a thread gets when multiple threads want to use the CPU at once. It's not relevant here.
The problem is that you call trig too soon! You want T2 to run after T3, but you trigger T2 before T3 does its thing. Both end up trying to modify savedExe and gPos at the same time, which is bad. Both problems are avoided by properly ordering the statements.
static void *pT3() {
wait_for_trigger(&gLock3, &gCondVar3, &gSignalT3);
savedExe[gPos++] = '3';
trigger(&gLock2, &gCondVar2, &gSignalT2);
return NULL;
}
Demo on Compiler Explorer
Note that you don't need so many mutexes and conv vars. You could use a single mutex and condvar for all signals.
static void wait_for_trigger(bool *flag) {
pthread_mutex_lock(&mutex);
while (!*flag)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void trigger(bool *flag) {
pthread_mutex_lock(&mutex);
*flag = true;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
Demo on Compiler Explorer
You could even use a single variable for the different priority levels!
static void wait_for_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
while (prio_lvl > a_prio_lvl)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void switch_to_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
prio_lvl = a_prio_lvl;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
Demo on Compiler Explorer
You have numerous other problems covered below. These are all fixed in this demo.
Improper prototype
static void *pT1();
should be
static void *pT1(void);
Empty parens in a declaration doesn't mean no arguments. You need to use void to mean that.
Improper arguments
static void *pT1() { ... }
should be
static void *pT1(void *arg) { ... }
pthread_create calls the function with a void * argument.
You can use (void)arg to silence the unused argument warning.
Uninitialized mutexes
pthread_mutex_t gLock1;
should be
pthread_mutex_t gLock1 = PTHREAD_MUTEX_INITIALIZER;
Mutex need to be initialized before they are used!
Uninitialized cond vars
pthread_cond_t gCondVar1;
should be
pthread_cond_t gCondVar1 = PTHREAD_COND_INITIALIZER;
Cond vars need to be initialized before they are used!
Incorrect expectations from pthread_cond_wait
if (!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
}
should be
while (!gSignalT3){
pthread_cond_wait(&gCondVar3, &gLock3);
}
pthread_cond_wait can return at any time. To be clear, pthread_cond_wait can return even if pthread_cond_signal hasn't been used. So it must always be called in a loop that checks some external condition.
Passing unlocked mutex to pthread_cond_wait
pthread_cond_wait(&gFinish, &gLockF);
This suffers from four problems we've already covered:
Lack of initializing of gFinish
Lack of initialization of gLockF
Lack of a required loop
Lack of an external condition
But that's not it.
pthread_cond_wait requires a locked mutex, but you are passing an unlocked mutex. It will return immediately with error EINVAL.
Useless code
This is again about
pthread_cond_wait(&gFinish, &gLockF);
It's completely unneeded. pthread_join already waits for the thread to finish. So this serves no purpose.
Full final code:
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int prio_lvl = 3;
static int gPos = 0;
static char savedExe[3];
static void wait_for_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
while (prio_lvl > a_prio_lvl)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
static void switch_to_level(int a_prio_lvl) {
pthread_mutex_lock(&mutex);
prio_lvl = a_prio_lvl;
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
}
static void *pT1(void *arg) {
(void)arg;
wait_for_level(1);
savedExe[gPos++] = '1';
return NULL;
}
static void *pT2(void *arg) {
(void)arg;
wait_for_level(2);
savedExe[gPos++] = '2';
switch_to_level(1);
return NULL;
}
static void *pT3(void *arg) {
(void)arg;
wait_for_level(3);
savedExe[gPos++] = '3';
switch_to_level(2);
return NULL;
}
int main(void) {
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_create(&tid1, NULL, pT1, NULL);
pthread_create(&tid2, NULL, pT2, NULL);
pthread_create(&tid3, NULL, pT3, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
{
const char *format = "%c";
for (size_t i = 0; i < sizeof(savedExe); ++i) {
printf(format, savedExe[i]);
format = ", %c";
}
printf("\n");
}
return 0;
}
Ok, as i understood your requirements. You want to make priority based thread initiation and storing result in what so ever form.
You can achive this by making a simple priority queue of struct having the priority value and the ptherad variable to hold thread information.
only catch is that you need to make a custom priority queue in c over a structure of roughly of this kind
typedef struct prt_qu{
uint8_t prty;
pthread th;
} ptr_qu;
Upon execution you just need to enqueue all tasks in priority queue and make the main thread call these threads one by one. You can make main wait for current thread wait by just using pthread_join().

C uninitialized mutex works and initialized mutex fails?

My C program creates a producer thread, saving data as fast as possible. The main thread consumes and prints these.
After days of bug finding, I noticed that if the mutex was initialized, then the program stops within 30 seconds (deadlock?).
However if the mutex is left uninitialized it works perfectly.
Can anyone explain this?? To avoid undefined behavior, I would prefer to initialize them if possible.
Further Info: Specifically it's locking up if "pthread_mutex_t signalM" (the signaling mutex) is initialized
Initialized
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_mutex_t signalM;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_mutex_lock(&event->signalM);
pthread_cond_signal(&event->signalC);
pthread_mutex_unlock(&event->signalM);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
int eventCount;
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
// lock signaling mutex
pthread_mutex_lock(&event->signalM);
// loop until the ticket machine shows your number
while (ticket > eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->signalM);
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->signalM);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct event outEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
Uninitialized
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_mutex_t signalM;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_mutex_lock(&event->signalM);
pthread_cond_signal(&event->signalC);
pthread_mutex_unlock(&event->signalM);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
int eventCount;
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
// lock signaling mutex
pthread_mutex_lock(&event->signalM);
// loop until the ticket machine shows your number
while (ticket > eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->signalM);
// get the counter
pthread_mutex_lock(&event->critical);
eventCount = event->eventCount;
pthread_mutex_unlock(&event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->signalM);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents; /* = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
}; */
struct event outEvents; /* = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
}; */
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
Jonathan explained why the code that didn't initialize mutexes didn't deadlock (essentially because trying to use an uninitialized mutex would never block, it would just immediately return an error).
The problem causing the infinite wait in the version of the program that does properly initialize mutexes is that you aren't using your condition variables properly. The check of the predicate expression and the wait on the condition variable must be done atomically with respect to whatever other thread might be modifying the predicate. You code is checking a predicate that is a local variable that the other thread doesn't even have access to. Your code reads the actual predicate into a local variable within a critical section, but then the mutex for reading the predicate is released and a different mutex is acquired to read the 'false' predicate (which cannot be modified by the other thread anyway) atomically with the condition variable wait.
So you have a situation where the actual predicate, event->eventCount, can be modified and the signal for that modification be issued in between when the waiting thread reads the predicate and blocks on the condition variable.
I think the following will fix your deadlock, but I haven't had a chance to perform much testing. The change is essentially to remove the signalM mutex from struct event and replace any use of it with the critical mutex:
#include <stdlib.h> // exit_failure, exit_success
#include <stdio.h> // stdin, stdout, printf
#include <pthread.h> // threads
#include <string.h> // string
#include <unistd.h> // sleep
#include <stdbool.h> // bool
#include <fcntl.h> // open
struct event {
pthread_mutex_t critical;
pthread_cond_t signalC;
int eventCount;
};
struct allVars {
struct event inEvents;
struct event outEvents;
int bufferSize;
char buffer[10][128];
};
/**
* Advance the EventCount
*/
void advance(struct event *event) {
// increment the event counter
pthread_mutex_lock(&event->critical);
event->eventCount++;
pthread_mutex_unlock(&event->critical);
// signal await to continue
pthread_cond_signal(&event->signalC);
}
/**
* Wait for ticket and buffer availability
*/
void await(struct event *event, int ticket) {
// get the counter
pthread_mutex_lock(&event->critical);
// loop until the ticket machine shows your number
while (ticket > event->eventCount) {
// wait until a ticket is called
pthread_cond_wait(&event->signalC, &event->critical);
}
// unlock signaling mutex
pthread_mutex_unlock(&event->critical);
}
/**
* Add to buffer
*/
void putBuffer(struct allVars *allVars, char data[]) {
// get the current write position
pthread_mutex_lock(&allVars->inEvents.critical);
int in = allVars->inEvents.eventCount;
pthread_mutex_unlock(&allVars->inEvents.critical);
// wait until theres a space free in the buffer
await(&allVars->outEvents, in - allVars->bufferSize + 1); // set to 2 to keep 1 index distance
// add data to buffer
strcpy(allVars->buffer[in % allVars->bufferSize], data);
// increment the eventCounter and signal
advance(&allVars->inEvents);
}
/**
* Get from buffer
*/
char *getBuffer(struct allVars *allVars) {
// get the current read position
pthread_mutex_lock(&allVars->outEvents.critical);
int out = allVars->outEvents.eventCount;
pthread_mutex_unlock(&allVars->outEvents.critical);
// wait until theres something in the buffer
await(&allVars->inEvents, out + 1);
// allocate memory for returned string
char *str = malloc(128);
// get the buffer data
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
// increment the eventCounter and signal
advance(&allVars->outEvents);
return str;
}
/** child thread (producer) */
void *childThread(void *allVars) {
char str[10];
int count = 0;
while (true) {
sprintf(str, "%d", count++);
putBuffer(allVars, str);
}
pthread_exit(EXIT_SUCCESS);
}
int main(void) {
// init structs
struct event inEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct event outEvents = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_COND_INITIALIZER,
0
};
struct allVars allVars = {
inEvents, // events
outEvents,
10, // buffersize
{"", {""}} // buffer[][]
};
// create child thread (producer)
pthread_t thread;
if (pthread_create(&thread, NULL, childThread, &allVars)) {
fprintf(stderr, "failed to create child thread");
exit(EXIT_FAILURE);
}
// (consumer)
while (true) {
char *out = getBuffer(&allVars);
printf("buf: %s\n", out);
free(out);
}
return (EXIT_SUCCESS);
}
I modified the getBuffer() and putBuffer() routines as shown (in both the initialized and uninitialized versions of the code):
static
void putBuffer(struct allVars *allVars, char data[])
{
int lock_ok = pthread_mutex_lock(&allVars->inEvents.critical);
if (lock_ok != 0)
printf("%s(): lock error %d (%s)\n", __func__, lock_ok, strerror(lock_ok));
int in = allVars->inEvents.eventCount;
int unlock_ok = pthread_mutex_unlock(&allVars->inEvents.critical);
if (unlock_ok != 0)
printf("%s(): unlock error %d (%s)\n", __func__, unlock_ok, strerror(unlock_ok));
await(&allVars->outEvents, in - allVars->bufferSize + 1);
strcpy(allVars->buffer[in % allVars->bufferSize], data);
advance(&allVars->inEvents);
}
static
char *getBuffer(struct allVars *allVars)
{
int lock_ok = pthread_mutex_lock(&allVars->outEvents.critical);
if (lock_ok != 0)
printf("%s(): lock error %d (%s)\n", __func__, lock_ok, strerror(lock_ok));
int out = allVars->outEvents.eventCount;
int unlock_ok = pthread_mutex_unlock(&allVars->outEvents.critical);
if (unlock_ok != 0)
printf("%s(): unlock error %d (%s)\n", __func__, unlock_ok, strerror(unlock_ok));
await(&allVars->inEvents, out + 1);
char *str = malloc(128);
strcpy(str, allVars->buffer[out % allVars->bufferSize]);
advance(&allVars->outEvents);
return str;
}
Then running the uninitialized code yields a lot of messages like:
buf: 46566
putBuffer(): lock error 22 (Invalid argument)
getBuffer(): lock error 22 (Invalid argument)
putBuffer(): unlock error 22 (Invalid argument)
getBuffer(): unlock error 22 (Invalid argument)
Basically, it appears to me that your locking and unlocking is being ignored. There are other places in your code that you should check too.
Fundamentally, if you ignore the errors reported, you don't notice that the locking and unlocking is not working at all, and there's no reason for the code to stop running.
Always check the return values from system calls that can fail.
I don't have an immediate explanation for why the initialized code locks up. It does for me, running on Mac OS X 10.10.3 with GCC 5.1.0, after anywhere from about 100,000 to 800,000 iterations.

Implementing Producer/Consumer Communication

I am attempting to implement producer/consumer communication through a bounded buffer using semaphores and locks I've already implemented in C. I need to have the producer place "hello world" onto a 5-byte buffer, one character at a time. The producer needs to block if the buffer is full. I am struggling to figure out how to place the string on the buffer. Here is what I have so far:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZE 5
// the buffer works like a stack for
// the sake of simplicity, if needed
// we may implement a queue
typedef struct {
int buf[BUF_SIZE]; // the buffer
size_t len; // number of items in the buffer
pthread_mutex_t mutex; // needed to add/remove data from the buffer
pthread_cond_t can_produce; // signaled when items are removed
pthread_cond_t can_consume; // signaled when items are added
} buffer_t;
// produce random numbers
void* producer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef UNDERFLOW
// used to show that if the producer is somewhat "slow"
// the consumer will not fail (i.e. it'll just wait
// for new items to consume)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == BUF_SIZE) { // full
// wait until some elements are consumed
pthread_cond_wait(&buffer->can_produce, &buffer->mutex);
}
// in real life it may be some data fetched from
// sensors, the web, or just some I/O
int t = rand();
printf("Produced: %d\n", t);
// append data to the buffer
buffer->buf[buffer->len] = t;
++buffer->len;
// signal the fact that new items may be consumed
pthread_cond_signal(&buffer->can_consume);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
// consume random numbers
void* consumer(void *arg) {
buffer_t *buffer = (buffer_t*)arg;
while(1) {
#ifdef OVERFLOW
// show that the buffer won't overflow if the consumer
// is slow (i.e. the producer will wait)
sleep(rand() % 3);
#endif
pthread_mutex_lock(&buffer->mutex);
if(buffer->len == 0) { // empty
// wait for new items to be appended to the buffer
pthread_cond_wait(&buffer->can_consume, &buffer->mutex);
}
// grab data
--buffer->len;
printf("Consumed: %d\n", buffer->buf[buffer->len]);
// signal the fact that new items may be produced
pthread_cond_signal(&buffer->can_produce);
pthread_mutex_unlock(&buffer->mutex);
}
// never reached
return NULL;
}
int main(int argc, char *argv[]) {
buffer_t buffer = {
.len = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.can_produce = PTHREAD_COND_INITIALIZER,
.can_consume = PTHREAD_COND_INITIALIZER
};
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, (void*)&buffer);
pthread_create(&cons, NULL, consumer, (void*)&buffer);
pthread_join(prod, NULL); // will wait forever
pthread_join(cons, NULL);
return 0;
}
a stack is a Last in, First out. Not what you want.
I would strongly implementing a circular buffer, with a head and tail pointer.
Then use the mutex (no need for a semaphore) to lock the critical code.
(the critical code for the producer is where it is adding/trying to add a char)
(the critical code for the consumer is where it is removing/trying to remove a char)
the mutex is so the two critical variables 'head' and 'tail' are stable
while any one process is accessing the circular queue.

A variation on the seqlock algorithm

Can someone provide some examples/tips/indications of how to solve the following assignment: a resource may be used by 2 types of processes: black and white. When the resource is used by the white processes, it can not be used by the black processes and vice-versa. Implement the access to the resource avoiding starvation. In an older post I was advised to use a variation on the seqlock algorithm, but I can't figure how to adjust that algorithm for this assignment.
EDIT: this is the code I've written so far
#include <stdio.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
struct RW;
struct RW
{
volatile int num_reads_in_progress;
volatile int num_writes;
pthread_cond_t reader_cv;
pthread_cond_t writer_cv;
pthread_mutex_t lock;
};
char *buf;
void signal_next(struct RW *b);
extern char *xx_read(struct RW *);
extern void xx_write(struct RW *, char *);
// Precondition: b->lock must be locked before this function is called
void signal_next(struct RW *b)
{
if (b->num_writes > 0)
{
// if any writes are waiting wake one up
pthread_cond_signal(&b->writer_cv);
}
else
{
// if are no writes pending, wake up all the readers
pthread_cond_broadcast(&b->reader_cv);
}
}
void *ts_read(void *vb);
void *ts_read(void *vb)
{
struct RW *b = vb;
pthread_mutex_lock(&b->lock);
while (b->num_writes > 0)
{
// cond_wait unlocks the mutex, waits to be signaled, then re-acquires the mutex
pthread_cond_wait(&b->reader_cv, &b->lock);
}
// By there b->num_writes must be 0
b->num_reads_in_progress++;
pthread_mutex_unlock(&b->lock);
buf = xx_read(b);
pthread_mutex_lock(&b->lock);
b->num_reads_in_progress--;
signal_next(b);
pthread_mutex_unlock(&b->lock);
return 0;
}
void *ts_write(void *vb);
void *ts_write(void *vb)
{
struct RW *b = vb;
pthread_mutex_lock(&b->lock);
b->num_writes++;
if (b->num_writes > 1 || b->num_reads_in_progress > 0)
{
// cond_wait unlocks the mutex, waits to be signaled,
// then re-acquires the mutex
pthread_cond_wait(&b->writer_cv, &b->lock);
}
pthread_mutex_unlock(&b->lock);
xx_write(b, buf);
pthread_mutex_lock(&b->lock);
b->num_writes--;
signal_next(b);
pthread_mutex_unlock(&b->lock);
return 0;
}
int main(void)
{
pthread_t white[3];
pthread_t black[3];
struct RW *rw;
rw = malloc(sizeof(struct RW));
int i;
for (i = 0; i < 3; i++)
{
pthread_create(&white[i], NULL, &ts_read, &rw);
}
for (i = 0; i < 3; i++)
{
pthread_create(&black[i], NULL, ts_write, &rw);
}
for (i = 0; i < 3; i++)
{
pthread_join(white[i], NULL);
}
for (i = 0; i < 3; i++)
{
pthread_join(black[i], NULL);
}
return 0;
}
You need a Mutex that locks and unlocks. Basically you can think of a mutex as a boolean value that is either true or false(locked or unlocked if you prefer).
When black process accesses the resource, the mutex should be locked. And, on the other hand when white tries to access it, it should first check for the mutex's status. If the status of mutex is locked, then it will have to wait until the mutex is unlocked.
Pseudocode:
unsigned char mutex = 0;
//processBlack tries to access resource
if(mutex == 1)
while(mutex != 0);
mutex = 1;
//now the mutex is unlocked, do whatever you want
mutex = 0; //do not forget to unlock it.
//processWhite tries to access resource
if(mutex == 1)
while(mutex != 0);
mutex = 1;
//now the mutex is unlocked, do whatever you want
mutex = 0; //do not forget to unlock it.
If you want to use the seqlock mechanism take a look here, section 5.7.4.:
http://www.makelinux.net/ldd3/chp-5-sect-7
and here for writers example:
http://www.sao.ru/hq/sts/linux/doc/porting_to_26/22818.html

Wrong implementation of Peterson's algorithm?

I was trying to learn something about parallel programming, so I tried to implement Peterson's algorithm for an easy example where one shared counter is incremented by 2 threads. I know that Peterson's isn't optimal due to busy waiting but I tried it only for study reasons.
I supposed that the critical section of this code is in the thread function add where the shared counter is incremented. So i call the enter_section function before the counter incrementation and after it I called the leave_function. Is this part wrong? Did I asses the critical section wrong? Problem is that the counter sometimes gives an unexpectable value when these 2 threads are done. It has to be a synchronization problem between threads but I just don't see it... Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int counter; /* global shared counter */
int flag[2] = {0, 0}; /* Variables for Peterson's algorithm */
int turn = 0;
typedef struct threadArgs
{
pthread_t thr_ID;
int num_of_repeats;
int id;
} THREADARGS;
void enter_section (int thread) {
int other = 1 - thread;
flag[thread] = 1;
turn = thread;
while ((turn == thread) && (flag[other] == 1));
return;
}
void leave_section (int thread) {
flag[thread] = 0;
return;
}
void * add (void * arg) {
int i;
THREADARGS * a = (THREADARGS *) arg;
for (i = 0; i < a->num_of_repeats; i++) {
enter_section(a->id);
counter++;
leave_section(a->id);
}
return NULL;
}
int main () {
int i = 1;
pthread_attr_t thrAttr;
THREADARGS threadargs_array[2];
pthread_attr_init (&thrAttr);
pthread_attr_setdetachstate (&thrAttr, PTHREAD_CREATE_JOINABLE);
/* creating 1st thread */
threadargs_array[0].id = 0;
threadargs_array[0].num_of_repeats = 1000000;
pthread_create(&threadargs_array[0].thr_ID, &thrAttr, add, &threadargs_array[0]);
/* creating 2nd thread */
threadargs_array[1].id = 1;
threadargs_array[1].num_of_repeats = 2000000;
pthread_create(&threadargs_array[1].thr_ID, &thrAttr, add, &threadargs_array[1]);
/* free resources for thread attributes */
pthread_attr_destroy (&thrAttr);
/* waiting for 1st thread */
pthread_join (threadargs_array[0].thr_ID, NULL);
printf("First thread is done.\n");
/* waiting for 2nd thread */
pthread_join (threadargs_array[1].thr_ID, NULL);
printf("Second thread is done.\n");
printf("Counter value is: %d \n", counter);
return (EXIT_SUCCESS);
}
You have several problems here:
the access to your variables will we subject to optimization, so you'd have to declare them volatile, at least.
Algorithms like this that access data between threads without any of the lock data structures that are provided by POSIX can only work if your primitive operations are guaranteed to be atomic, which they usually aren't on modern processors. In particular the ++ operator is not atomic.
There would be several ways around this, in particular the new C standard C11 offers atomic primitives. But if this is really meant for you as a start to learn parallel programming, I'd strongly suggest that you first look into mutexes, condition variables etc, to learn how POSIX is intended to work.

Resources