I am trying to implement a "proper" pthread multithreading program with pthread mutex. I want it to be as C-standard-conforming and POSIX-conforming as possible. The below is my implementation, any thoughts or bugs?:
main.c
:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "common.h"
static char shared_string[512];
pthread_mutex_t my_mutex;
void* func_that_takes_params(void* tpl) {
struct ThreadPayload* args = (struct ThreadPayload*)tpl;
while (!should_stop) {
// Let's add a bit of perturbation, but not too much
usleep(1000 * 1000 - args->thread_id);
if (pthread_mutex_lock(&my_mutex) != 0) {
perror("pthread_mutex_lock()");
continue;
}
sprintf(shared_string, "[%d] Message from caller: %s\n",
args->thread_id, args->message);
write(STDOUT_FILENO, shared_string, strlen(shared_string));
if (pthread_mutex_unlock(&my_mutex) != 0) {
perror("pthread_mutex_unlock()");
return NULL;
}
}
size_t* ret = malloc(sizeof(size_t));
if (ret != NULL) {
*ret = strlen(args->message);
} else {
perror("malloc()");
}
return (void*)ret;
}
int main(void) {
install_signal_handler();
should_stop = false;
if (pthread_mutex_init(&my_mutex, NULL) != 0) {
perror("pthread_mutex_init()");
return 1;
}
size_t thread_count = sizeof(thread_payloads)/sizeof(thread_payloads[0]);
pthread_t ths[thread_count];
size_t started_threads;
for (started_threads = 0; started_threads < thread_count; ++started_threads) {
if (pthread_create(&ths[started_threads], NULL, func_that_takes_params,
&thread_payloads[started_threads]) != 0) {
perror("pthread_create()");
--started_threads;
break;
}
}
printf("%d threads started\n", started_threads);
for (int i = 0; i < started_threads; ++i) {
size_t* ret = NULL;
if (pthread_join(ths[i], (void**)&ret) != 0) {
perror("pthread_join()");
continue;
}
if (ret != NULL) {
printf("ret: %u\n", *ret);
} else {
printf("ret is NULL\n");
}
free(ret);
}
if (pthread_mutex_destroy(&my_mutex) != 0) {
perror("pthread_mutex_destroy()");
}
return 0;
}
common.h
#include <signal.h>
#include <stdbool.h>
volatile sig_atomic_t should_stop;
struct ThreadPayload {
size_t thread_id;
char* message;
};
static struct ThreadPayload thread_payloads[] = {
{
.thread_id = 0,
.message = "This is a test message"
}, {
.thread_id = 1,
.message = "This is also a test message"
}, {
.thread_id = 2,
.message = "This is yet another a test message"
}, {
.thread_id = 3,
.message = "Hello world!!"
}, {
.thread_id = 4,
.message = "foobar"
}, {
.thread_id = 5,
.message = "0xDEADBEEF"
}, {
.thread_id = 6,
.message = "The quick brown fox jumps over the lazy dog"
}, {
.thread_id = 7,
.message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
}
};
static void signal_handler(int signum) {
char msg[] = "Signal [ ] caught\n";
msg[8] = '0' + signum / 10;
msg[9] = '0' + signum % 10;
size_t len = sizeof(msg) - 1;
size_t written = 0;
while (written < len) {
ssize_t ret = write(STDOUT_FILENO, msg + written, len - written);
if (ret == -1) {
perror("write()");
break;
}
written += ret;
}
should_stop = true;
}
// This function calls abort() if fails
void install_signal_handler() {
struct sigaction act;
if (sigemptyset(&act.sa_mask) == -1) {
perror("sigemptyset()");
abort();
}
act.sa_handler = signal_handler;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, 0) + sigaction(SIGABRT, &act, 0) +
sigaction(SIGQUIT, &act, 0) + sigaction(SIGTERM, &act, 0) < 0) {
perror("sigaction()");
abort();
}
}
Makefile
CC = gcc
LIBS = -lpthread -lglib-2.0
OPTS = -O2 -Wall -pedantic -Wextra -Wc++-compat
main: main.out
main.out: main.c common.h
$(CC) main.c -o main.out $(LIBS)
.PHONY: clean
clean:
rm *.out