Difference between revisions of "User:Kr/A Thread's Life"

From Apache OpenOffice Wiki
Jump to: navigation, search
m (Incorporated code review feedback.)
Line 4: Line 4:
 
==Two types of Threads==
 
==Two types of Threads==
 
* Active threads - activities
 
* Active threads - activities
* Daemon threads - daemons (see [[wikipedia:Daemon]])
+
* Daemon threads - daemons (see [[wikipedia:Daemon %28computer software%29]])
  
 
===Activity===
 
===Activity===
 
* Activities keep the process alive.
 
* Activities keep the process alive.
 
* The last activity terminating leads to the termination of the entire process.
 
* The last activity terminating leads to the termination of the entire process.
* Every activity must be detached, as it is not going to be joined.
+
* An activity must be detached, as it is not going to be joined.
  
 
====Examples====
 
====Examples====
Line 16: Line 16:
 
===Daemon===
 
===Daemon===
 
* Daemons don't keep the process alive.
 
* Daemons don't keep the process alive.
* Any daemon needs to be joined (terminated / cancelled) when (indirectly) released by an activity.
+
* A daemon must be joinable, as an activity is going to join it, latest during termination.
* Every daemon must be joinable, as an activity is going to join it, latest during termination.
+
* A daemon needs to be eventually joined by an activity. The termination of a daemon may be enforced by asking for cancellation (pthread_cancel) or by killing (pthread_kill) it.
  
 
====Sleepy Daemons====
 
====Sleepy Daemons====
 +
Daemons waiting for an event (e.g. I/O, a condition) to do something, going to sleep afterwards again.
  
 
====Short lived Daemons====
 
====Short lived Daemons====
 +
Daemons doing something, terminating afterwards.
  
 
====Examples====
 
====Examples====

Revision as of 09:54, 6 September 2007

Status: draft


Two types of Threads

Activity

  • Activities keep the process alive.
  • The last activity terminating leads to the termination of the entire process.
  • An activity must be detached, as it is not going to be joined.

Examples

  • GUI dispatcher thread

Daemon

  • Daemons don't keep the process alive.
  • A daemon must be joinable, as an activity is going to join it, latest during termination.
  • A daemon needs to be eventually joined by an activity. The termination of a daemon may be enforced by asking for cancellation (pthread_cancel) or by killing (pthread_kill) it.

Sleepy Daemons

Daemons waiting for an event (e.g. I/O, a condition) to do something, going to sleep afterwards again.

Short lived Daemons

Daemons doing something, terminating afterwards.

Examples

  • cache flushing threads

Notes

  • Threads may dynamically switch from one type to the other and vice versa.
  • The "main" thread is an activity.

Example

//
// gcc -Wall -g example.c -o main.bin -lpthread
// 
 
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
 
 
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();
    }
}
 
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; 
 
 
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". */
static 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. */
static void daemons_cancelAndJoin_(void) {
    fprintf(stderr, "Joining 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_);
 
    daemons_join_(0);
    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. */
    }
}
 
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_cancelAndJoin_);
 
    main_activity_();
 
    activity_terminate_();
 
    return 0;
}
Personal tools