Difference between revisions of "User:Kr/A Thread's Life/Example Code"
m (Worked in some code review comments, improved structure.) |
m (Fixed a race - should always do proper error handling to actually see the problems ;-)) |
||
Line 1: | Line 1: | ||
<code>[cpp] | <code>[cpp] | ||
// | // | ||
− | // gcc -g | + | // gcc -g example.c -o main.bin -lpthread |
// | // | ||
Line 8: | Line 8: | ||
#include <pthread.h> | #include <pthread.h> | ||
#include <string.h> | #include <string.h> | ||
+ | |||
+ | |||
+ | void my_pthread_cancel(pthread_t thread) { | ||
+ | int res; | ||
+ | if (res = pthread_cancel(thread)) { | ||
+ | fprintf(stderr, "%s - %s\n", __PRETTY_FUNCTION__, strerror(res)); | ||
+ | abort(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | 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(); | ||
+ | } | ||
+ | } | ||
Line 18: | Line 35: | ||
void (*func_)(void); | void (*func_)(void); | ||
pthread_t hd_; | pthread_t hd_; | ||
+ | int inJoin_; | ||
}; | }; | ||
+ | static pthread_mutex_t daemons_mutex_ = PTHREAD_MUTEX_INITIALIZER; | ||
static Daemon * firstDaemon_ = NULL; | static Daemon * firstDaemon_ = NULL; | ||
static unsigned int daemons_ = 0; | static unsigned int daemons_ = 0; | ||
− | |||
/* Unregister the given "Daemon" struct | /* Unregister the given "Daemon" struct | ||
Line 38: | Line 56: | ||
if (firstDaemon_ == daemon) | if (firstDaemon_ == daemon) | ||
firstDaemon_ = daemon->next_; | firstDaemon_ = daemon->next_; | ||
+ | |||
+ | |||
+ | /* This daemon is not in the list of daemons anymore, | ||
+ | and therefor can't be joined, except if acively cancelled. */ | ||
+ | if (!daemon->inJoin_) | ||
+ | pthread_detach(pthread_self()); | ||
pthread_mutex_unlock(&daemons_mutex_); | pthread_mutex_unlock(&daemons_mutex_); | ||
Line 43: | Line 67: | ||
free(daemon); | free(daemon); | ||
− | |||
− | |||
− | |||
} | } | ||
Line 62: | Line 83: | ||
static void daemon_create_(void (*func)(void)) { | static void daemon_create_(void (*func)(void)) { | ||
Daemon * daemon = malloc(sizeof(Daemon)); | Daemon * daemon = malloc(sizeof(Daemon)); | ||
− | daemon->func_ = func; | + | daemon->func_ = func; |
+ | daemon->inJoin_ = 0; | ||
pthread_mutex_lock(&daemons_mutex_); | pthread_mutex_lock(&daemons_mutex_); | ||
Line 89: | Line 111: | ||
pthread_t hd; | pthread_t hd; | ||
memcpy(&hd, &firstDaemon_->hd_, sizeof(pthread_t)); | memcpy(&hd, &firstDaemon_->hd_, sizeof(pthread_t)); | ||
+ | firstDaemon_->inJoin_ = 1; | ||
pthread_mutex_unlock(&daemons_mutex_); | pthread_mutex_unlock(&daemons_mutex_); | ||
// We may want to cancel a thread actively during termination: | // We may want to cancel a thread actively during termination: | ||
− | + | my_pthread_cancel(hd); | |
// respectively | // respectively | ||
//pthread_kill(); | //pthread_kill(); | ||
− | + | my_pthread_join(hd, NULL); | |
fprintf(stderr, "# "); | fprintf(stderr, "# "); | ||
Line 118: | Line 141: | ||
pthread_mutex_unlock(&activity_mutex_); | pthread_mutex_unlock(&activity_mutex_); | ||
− | + | // Terminate the process, if this was the last "activity"! | |
if (use_exit) | if (use_exit) | ||
exit(0); | exit(0); |
Revision as of 09:01, 28 September 2007
[cpp]
//
// gcc -g example.c -o main.bin -lpthread
//
- include <stdlib.h>
- include <stdio.h>
- include <pthread.h>
- include <string.h>
void my_pthread_cancel(pthread_t thread) {
int res; if (res = pthread_cancel(thread)) { fprintf(stderr, "%s - %s\n", __PRETTY_FUNCTION__, strerror(res)); abort(); }
}
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 inJoin_;
};
static pthread_mutex_t daemons_mutex_ = PTHREAD_MUTEX_INITIALIZER; static Daemon * firstDaemon_ = NULL; static unsigned int daemons_ = 0;
/* Unregister the given "Daemon" struct
and terminate this thread. */
static void daemon_terminate(Daemon * daemon) {
pthread_mutex_lock(&daemons_mutex_); -- daemons_;
if (daemon->next_) daemon->next_->previous_ = daemon->previous_;
if (daemon->previous_) daemon->previous_->next_ = daemon->next_;
if (firstDaemon_ == daemon) firstDaemon_ = daemon->next_;
/* This daemon is not in the list of daemons anymore, and therefor can't be joined, except if acively cancelled. */ if (!daemon->inJoin_) pthread_detach(pthread_self());
pthread_mutex_unlock(&daemons_mutex_);
free(daemon);
}
static void * daemon_func(Daemon * daemon) {
pthread_cleanup_push((void (*)(void *))daemon_terminate, daemon);
daemon->func_();
pthread_cleanup_pop(1);
return NULL;
}
/* Create a "Daemon" struct and register it.
Start the "Daemon". */
static void daemon_create_(void (*func)(void)) {
Daemon * daemon = malloc(sizeof(Daemon)); daemon->func_ = func; daemon->inJoin_ = 0;
pthread_mutex_lock(&daemons_mutex_);
daemon->previous_ = NULL; daemon->next_ = firstDaemon_;
if (firstDaemon_) firstDaemon_->previous_ = daemon;
firstDaemon_ = daemon; ++ daemons_;
pthread_mutex_unlock(&daemons_mutex_);
pthread_create(&daemon->hd_, NULL, (void *(*)(void *))daemon_func, daemon);
}
/* Ask all registered daemons for termination and join them. */ static void daemons_join_(void) {
fprintf(stderr, "Joining daemons ... ");
pthread_mutex_lock(&daemons_mutex_); while (firstDaemon_) { pthread_t hd; memcpy(&hd, &firstDaemon_->hd_, sizeof(pthread_t)); firstDaemon_->inJoin_ = 1;
pthread_mutex_unlock(&daemons_mutex_);
// We may want to cancel a thread actively during termination: my_pthread_cancel(hd); // respectively //pthread_kill();
my_pthread_join(hd, NULL); fprintf(stderr, "# ");
pthread_mutex_lock(&daemons_mutex_); } pthread_mutex_unlock(&daemons_mutex_);
fprintf(stderr, "done\n");
}
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 void log_threads(void) {
fprintf(stderr, "activities: %u daemons: %u\n", activities_, daemons_);
}
static void log_daemon_(void) {
while (1) { log_threads();
sleep(1); /* This is a cancellation point. */ }
pthread_exit(NULL);
}
static void some_daemon_(void) {
int n = 10; while (n) { sleep(1); if (rand() < (RAND_MAX / (daemons_ + 1))) daemon_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 / (daemons_ + 1))) daemon_create_(some_daemon_);
-- n; }
}
static void main_activity_(void) {
srand (time (0));
daemon_create_(log_daemon_); daemon_create_(some_daemon_); activity_create_(some_activity_);
sleep(3); daemon_create_(some_daemon_);
sleep(5);
}
int main(void) {
atexit(log_threads); atexit(daemons_join_);
main_activity_();
activity_terminate_();
return 0;
}