User:Kr/A Thread's Life/Library Example
From Apache OpenOffice Wiki
< User:Kr | A Thread's Life
The Main Program
/* ** gcc -Wall -g main.c -o main.bin -lpthread -ldl */ #include "library.h" #include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <string.h> #include <unistd.h> #include <dlfcn.h> static unsigned int activities_ = 1; /* The "main" thread is the first "activity". */ static pthread_mutex_t activity_mutex_ = PTHREAD_MUTEX_INITIALIZER; static void activity_terminate_(void) { int use_exit = 0; pthread_mutex_lock(&activity_mutex_); -- activities_; use_exit = activities_ == 0; pthread_mutex_unlock(&activity_mutex_); /* Terminate the process, if this was the last "activity"! */ if (use_exit) exit(0); else pthread_exit(NULL); } static void * activity_func_(void (*func)(void)) { func(); activity_terminate_(); return NULL; } static void activity_create_(void (*func)(void)) { pthread_attr_t attr; /* Activities need to be detached. */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, 1); pthread_t bt; pthread_create(&bt, &attr, (void *(*)(void *))activity_func_, func); pthread_attr_destroy(&attr); pthread_mutex_lock(&activity_mutex_); ++ activities_; pthread_mutex_unlock(&activity_mutex_); } /* Some daemon functions. */ static daemon_create_t * pdaemon_create_; static daemons_getCount_t * pdaemons_getCount_; static void log_threads(void) { fprintf(stderr, "activities: %u daemons: %u\n", activities_, pdaemons_getCount_ ? pdaemons_getCount_() : 0); } static void log_daemon_(void) { while (1) { log_threads(); /* This is a cancellation point. */ sleep(1); } } static void some_daemon_(void) { int n = 10; while (n) { sleep(1); if (rand() < (RAND_MAX / (pdaemons_getCount_() + 1))) pdaemon_create_(some_daemon_); -- n; } } /* Some activity functions. */ static void some_activity_(void) { int n = 3; while (n) { sleep(1); if (rand() < (RAND_MAX / (activities_ + 1))) activity_create_(some_activity_); if (rand() < (RAND_MAX / (pdaemons_getCount_() + 1))) pdaemon_create_(some_daemon_); -- n; } } static void main_activity_(void) { srand(time (0)); pdaemon_create_(log_daemon_); pdaemon_create_(some_daemon_); activity_create_(some_activity_); sleep(3); pdaemon_create_(some_daemon_); sleep(5); } static void * dlhd_ = NULL; static void close_library(void) { daemons_cancelAndJoin_t * pdaemons_cancelAndJoin = dlsym(dlhd_, "daemons_cancelAndJoin"); if (pdaemons_cancelAndJoin) pdaemons_cancelAndJoin(); dlclose(dlhd_); pdaemon_create_ = NULL; pdaemons_getCount_ = NULL; } int main(void) { atexit(log_threads); atexit(close_library); dlhd_ = dlopen("library.so", RTLD_NOW); pdaemon_create_ = dlsym(dlhd_, "daemon_create"); pdaemons_getCount_ = dlsym(dlhd_, "daemons_getCount"); main_activity_(); activity_terminate_(); return 0; } </code> ====Library Header==== <code>[cpp] #ifndef library_h_included # define library_h_included typedef void daemon_create_t (void (*func)(void)); typedef int daemons_getCount_t (void); typedef void daemons_cancelAndJoin_t(void); void daemon_create (void (*func)(void)); int daemons_getCount (void); void daemons_cancelAndJoin(void); #endif </code> ====Library Implementation==== <code>[cpp] /* ** gcc -Wall -g library.c -shared -o library.so -lpthread */ #include "library.h" #include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <string.h> #include <unistd.h> static void my_pthread_cancel(pthread_t thread) { int res; if ((res = pthread_cancel(thread))) { fprintf(stderr, "%s(%p) => %s\n", __PRETTY_FUNCTION__, (void*)thread, strerror(res)); /* There seems to be an issue on Linux with pthread_cancel, ** returning an error in case "thread" has already terminated, ** though not joined. */ /* abort(); */ } } static void my_pthread_join(pthread_t th, void **thread_return) { int res; if ((res = pthread_join(th, thread_return))) { fprintf(stderr, "%s - %s\n", __PRETTY_FUNCTION__, strerror(res)); abort(); } } typedef struct Daemon Daemon; struct Daemon { Daemon * previous_; Daemon * next_; void (*func_)(void); pthread_t hd_; int finished_; }; static pthread_mutex_t daemons_mutex_ = PTHREAD_MUTEX_INITIALIZER; static Daemon * firstDaemon_ = NULL; static unsigned int daemons_ = 0; int daemons_getCount(void) { return daemons_; } /* Joins all or only "finished" daemons. */ static void daemons_join_(int finished) { pthread_mutex_lock(&daemons_mutex_); Daemon * daemon = firstDaemon_; while (daemon) { Daemon * next = daemon->next_; if (!finished || daemon->finished_) { -- daemons_; if (daemon->next_) daemon->next_->previous_ = daemon->previous_; if (daemon->previous_) daemon->previous_->next_ = daemon->next_; if (firstDaemon_ == daemon) firstDaemon_ = daemon->next_; pthread_mutex_unlock(&daemons_mutex_); my_pthread_join(daemon->hd_, NULL); pthread_mutex_lock(&daemons_mutex_); daemon->hd_ = 0xffffffff; free(daemon); daemon = firstDaemon_; } else daemon = next; } pthread_mutex_unlock(&daemons_mutex_); } /* Daemon finish function. */ static void daemon_finish_(Daemon * daemon) { int oldstate; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); daemons_join_(1); pthread_mutex_lock(&daemons_mutex_); daemon->finished_ = 1; pthread_mutex_unlock(&daemons_mutex_); pthread_setcancelstate(oldstate, NULL); } static void * daemon_func_(Daemon * daemon) { pthread_cleanup_push((void (*)(void *))daemon_finish_, daemon); daemon->func_(); pthread_cleanup_pop(1); return NULL; } /* Create a "Daemon" struct and register it. Start the "Daemon". */ void daemon_create(void (*func)(void)) { Daemon * daemon = malloc(sizeof(Daemon)); daemon->func_ = func; daemon->finished_ = 0; pthread_mutex_lock(&daemons_mutex_); daemon->previous_ = NULL; daemon->next_ = firstDaemon_; if (firstDaemon_) firstDaemon_->previous_ = daemon; firstDaemon_ = daemon; ++ daemons_; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, 0); pthread_create(&daemon->hd_, NULL, (void *(*)(void *))daemon_func_, daemon); pthread_attr_destroy(&attr); pthread_mutex_unlock(&daemons_mutex_); } /* Ask all registered daemons for cancellation and join them. */ /* Calling pthread_canel from within _fini leads to deadlocks.*/ /* static void __attribute__((destructor)) daemons_cancelAndJoin_(void) {*/ void daemons_cancelAndJoin(void) { fprintf(stderr, "Cancelling daemons ... "); pthread_mutex_lock(&daemons_mutex_); Daemon * daemon = firstDaemon_; while (daemon) { my_pthread_cancel(daemon->hd_); /* respectively */ /*pthread_kill();*/ fprintf(stderr, "# "); daemon = daemon->next_; } pthread_mutex_unlock(&daemons_mutex_); fprintf(stderr, "done\n"); fprintf(stderr, "Joining daemons ... "); daemons_join_(0); fprintf(stderr, "done\n"); }