I'm trying to design a multithreaded daemon for an industrial automation related project.
The daemon will be using a number of 3rd party libs like MQTT, mysql, etc..
My idea is to have worker threads (or callbacks registered with these 3rd party libs) relay events to the main thread using a global mutex and conditional variable based synchronisation mechanism.
The main thread then performs some global state management and some lite processing that I'm willing to restrict to the main thread (like mysql stuff). After that the main thread defers the processing to a pool of worker threads.
Here's my code. I haven't posted the logging and much of the error checking code here. I'm planning on using the start-stop-daemon facility for daemonizing.
I wanted to know if I'm headed in the right direction. Constructive criticism is welcome.
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#define RESPONSE_MAX_LENGTH 256
/**
* Internal data structures and enums
*/
typedef struct myThread_ {
pthread_t id;
} myThreadT;
typedef enum myEventType_ {
MY_EVENT_TYPE_NONE,
MY_EVENT_TYPE_STOP,
MY_EVENT_TYPE_RELOAD
} myEventTypeT;
typedef struct myEvent_ {
int is_triggered;
myEventTypeT type;
void *data; /* may include a callback */
} myEventT;
const myEventT myEventInitializer = {
.is_triggered = 0,
.type = MY_EVENT_TYPE_NONE,
.data = NULL
};
/**
* Global variables
*/
static myEventT myEvent;
static myThreadT worker;
/* Mutex and cond_var */
static pthread_mutex_t my_event_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t my_event_trigger_cv = PTHREAD_COND_INITIALIZER;
/*
* Triggers an internal myEvent
* Blocks calling thread till event acknowledgement
*/
int trigger_event(myEventTypeT type, void *data) {
pthread_mutex_lock(&my_event_mutex);
myEvent.type = type;
myEvent.data = data;
// Trigger event!
myEvent.is_triggered = 1;
pthread_cond_signal(&my_event_trigger_cv);
pthread_mutex_unlock(&my_event_mutex);
// Block till current trigger has been acknowledged
while(myEvent.is_triggered);
return 0;
}
void *worker_loop() {
while(1) {
sleep(5); /* Do something worthwhile */
trigger_event(MY_EVENT_TYPE_NONE, NULL);
}
pthread_exit(NULL);
}
/*
* Called from main thread, mainly for state management & db related processing,
* i.e. stuff we want to restrict to the main thread.
*/
int handle_event(myEventT *event, char *response) {
// handle the event, fill response if required
return 0;
}
int main(int argc, char* argv[]) {
// Initialize globals
myEvent = myEventInitializer;
// Thread management
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int thread_error;
thread_error = pthread_create(&worker.id, &attr, worker_loop, NULL);
if (thread_error != 0) {
return -1;
}
pthread_attr_destroy(&attr);
pthread_mutex_lock(&my_event_mutex);
// Main loop: wait for event
while(1) {
// Wait for signal from worker threads/ callbacks
int res_wait = pthread_cond_wait(&my_event_trigger_cv, &my_event_mutex);
if (res_wait == EINVAL) { // TODO: Consolidated error handling
return -1;
}
/**
* Event handling construct
*
*/
if (myEvent.is_triggered) {
pthread_mutex_unlock(&my_event_mutex);
char response_string[RESPONSE_MAX_LENGTH]; //TODO macro for size!
if(handle_event(&myEvent, response_string) >= 0) {
// Initiate deferred processing on another thread (maybe callback)
}
// Clear event triggered flag and reinit event data
myEvent = myEventInitializer;
pthread_mutex_lock(&my_event_mutex);
}
}
// Clean up pthread resources
pthread_mutex_destroy(&my_event_mutex);
pthread_cond_destroy(&my_event_trigger_cv);
pthread_exit(NULL);
}