// ---------------------------------------------------------------------------
// - cthr.cxx -
// - standard system library - c thread function implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - This program is distributed in the hope that it will be useful, but -
// - without any warranty; without even the implied warranty of -
// - merchantability or fitness for a particular purpose. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "cthr.hpp"
#include "cerr.hpp"
#include "cthr.hxx"
namespace afnix {
// the thread info structure
struct s_thr {
pthread_t d_tid; // the thread id
t_thrmode d_mode; // the thread mode
t_thrfunc p_func; // the start function
t_thrdtor p_dtor; // the object destructor
void* p_args; // the start function argument
void* p_result; // the thread result
bool d_eflag; // the end flag for this thread
long d_count; // the reference count for this structure
s_thr* p_next; // next thread in list
s_thr* p_prev; // previous thread in list
// simple constructror
s_thr (void) {
d_mode = THR_NORMAL;
p_func = nilp;
p_args = nilp;
p_result = nilp;
d_eflag = false;
d_count = 1;
p_next = nilp;
p_prev = nilp;
}
~s_thr (void) {
if (p_dtor != nilp) p_dtor (p_result);
if (p_dtor != nilp) p_dtor (p_args);
}
};
// the main thread id
static pthread_t THR_TOP;
// the main thread object
static void* throbj = nilp;
// the thread system is initialized
static bool THR_SET = false;
// the thread structure key
static pthread_key_t KEY_TID;
// the control for one initialization
static pthread_once_t KEY_CTL = AFNIX_PTHREAD_ONCE_INIT;
// the threads linked list
static s_thr* thrlist = nilp;
// the global lock for the thread list
static pthread_mutex_t thrlock = AFNIX_PTHREAD_MUTEX_INITIALIZER;
// the condition variable during wait all
static pthread_cond_t cvwaita = AFNIX_PTHREAD_COND_INITIALIZER;
// the condition variable during start
static pthread_cond_t cvstart = AFNIX_PTHREAD_COND_INITIALIZER;
// this procedure marks the thread as finished
static void mark_thread_finished (s_thr* thr) {
if ((thr == nilp) || (thr->d_eflag == true)) return;
// get the thread lock
pthread_mutex_lock (&thrlock);
thr->d_eflag = true;
// signal we have changed some status
pthread_cond_signal (&cvwaita);
pthread_mutex_unlock (&thrlock);
}
// this procedure add a new thread in the thread list
static void insert_thread_list (s_thr* thr) {
if (thr == nilp) return;
// get the thread list lock
pthread_mutex_lock (&thrlock);
// increase the reference count and mark as started
thr->d_count++;
// insert in the list
thr->p_next = thrlist;
if (thrlist != nilp) thrlist->p_prev = thr;
thrlist = thr;
// signal we are ready and unlock
pthread_cond_signal (&cvstart);
pthread_mutex_unlock (&thrlock);
}
// this procedure removes a thread from the thread list
static void remove_thread_list (s_thr* thr) {
if (thr == nilp) return;
// get the thread list lock
pthread_mutex_lock (&thrlock);
if (thr->d_count > 1) {
thr->d_count--;
pthread_mutex_unlock (&thrlock);
return;
}
// remove from the list
if (thr == thrlist) {
thrlist = thr->p_next;
} else {
s_thr* prev = thr->p_prev;
s_thr* next = thr->p_next;
if (prev != nilp) prev->p_next = next;
if (next != nilp) next->p_prev = prev;
}
thr->p_next = nilp;
thr->p_prev = nilp;
// decrement the reference count and eventually remove
if (--thr->d_count == 0) delete thr;
// signal we have removed a thread
pthread_cond_broadcast (&cvwaita);
// release the lock
pthread_mutex_unlock (&thrlock);
}
// this procedure initialize the key when the first thread is started.
// this procedure is called by pthread_once
static void tid_key_once (void) {
// create the initial key
pthread_key_create(&KEY_TID, nilp);
THR_TOP = pthread_self ();
THR_SET = true;
// set the unexpected handler
c_errsetexpt (nilp);
}
// this procedure is the start routine for the thread - it takes
// the thr structure and register the key, insert the thread descriptor
// in the thread list and finally start the function
static void* thr_start (void* args) {
// finish to fill the structure
s_thr* thr = (s_thr*) args;
// map the key with the structure
pthread_setspecific (KEY_TID, (void*) thr);
// install the thread in the list
insert_thread_list (thr);
// run the procedure
try {
thr->p_result = thr->p_func (thr->p_args);
mark_thread_finished (thr);
} catch (...) {
mark_thread_finished (thr);
remove_thread_list (thr);
throw;
}
remove_thread_list (thr);
return nilp;
}
// create a new thread of control
void* c_thrstart (t_thrmode mode, t_thrfunc func, void* args,
t_thrdtor dtor) {
// initialize the tid key
pthread_once (&KEY_CTL, tid_key_once);
// set the thread according to the mode
pthread_attr_t attr;
if (pthread_attr_init (&attr) != 0) return nilp;
if (mode == THR_DAEMON)
if (pthread_attr_setdetachstate (&attr,PTHREAD_CREATE_DETACHED) != 0)
return nilp;
// create the thread data structure
s_thr* thr = new s_thr;
thr->d_mode = mode;
thr->p_func = func;
thr->p_args = args;
thr->p_dtor = dtor;
// take the lock - so we guard the start condition - the condition
// variable protect us against a race condition if the thr descritptor
// is destroyed before the thread is started (i.e in the list).
pthread_mutex_lock (&thrlock);
// run the thread
int status = pthread_create (&(thr->d_tid),&attr, thr_start, (void*) thr);
if (status != 0) {
pthread_mutex_unlock (&thrlock);
remove_thread_list (thr);
return nilp;
}
// wait until the thread is started
pthread_cond_wait (&cvstart, &thrlock);
pthread_mutex_unlock (&thrlock);
return (void*) thr;
}
// return true if the thread system is initialized
bool c_thralive (void) {
return THR_SET;
}
// return the thread id
void* c_thrself (void) {
return THR_SET ? pthread_getspecific (KEY_TID) : nilp;
}
// return true if two thread are equals
bool c_threqual (void* thr) {
if (THR_SET == false) return true;
pthread_t tid = (thr == nilp) ? THR_TOP : ((s_thr*) thr)->d_tid;
pthread_t sid = pthread_self ();
return (pthread_equal (tid,sid) == 0) ? false : true;
}
// return true if the thread is the master
bool c_thrmaster (void) {
return c_threqual (nilp);
}
// set the main thread object
void c_thrsetmain (void* object) {
if (c_thrmaster () == true) throbj = object;
}
// get the thread object
void* c_thrgetobj (void) {
s_thr* thr = (s_thr*) c_thrself ();
if (thr == nilp) return throbj;
return thr->p_args;
}
// return the thread result
void* c_thrgetres (void* thr) {
if (thr == nilp) return nilp;
s_thr* thread = (s_thr*) thr;
return thread->d_eflag ? thread->p_result : nilp;
}
// join a thread and wait for terminate
void c_thrwait (void* thr) {
s_thr* thread = (s_thr*) thr;
if (thread == nilp) return;
// make sure the thread is joinable
if (thread->d_mode == THR_DAEMON) return;
pthread_join (thread->d_tid, nilp);
if (thread->d_eflag == true) return;
// wait for the thread to be marked as finished
pthread_mutex_lock (&thrlock);
while (thread->d_eflag == false) {
pthread_cond_wait (&cvwaita, &thrlock);
if (thread->d_eflag == true) break;
}
pthread_mutex_unlock (&thrlock);
}
// exit a thread - but do not destroy the thr record
void c_threxit (void) {
pthread_exit (nilp);
}
// destroys a thread record
void c_thrdestroy (void* thr) {
s_thr* thread = (s_thr*) thr;
if (thread == nilp) return;
remove_thread_list (thread);
}
// wait for all threads in the thread list to terminate
void c_thrwaitall (void) {
// get the lock
pthread_mutex_lock (&thrlock);
while (true) {
// compute the wait flag
s_thr* thr = thrlist;
bool flag = false;
while (thr != nilp) {
if ((thr->d_mode == THR_NORMAL) && (thr->d_eflag == false)) {
flag = true;
break;
}
thr = thr->p_next;
}
// check if we have to wait
if (flag == true)
pthread_cond_wait (&cvwaita, &thrlock);
else
break;
}
// release the lock
pthread_mutex_unlock (&thrlock);
}
// create a new mutex in an unlocked state
void* c_mtxcreate (void) {
// mutex default attribute
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
// create the new mutex
pthread_mutex_t* mtx = new pthread_mutex_t;
if (mtx != 0) pthread_mutex_init (mtx, &attr);
return mtx;
}
// destroy the mutex argument
void c_mtxdestroy (void* mtx) {
if (mtx == 0) return;
pthread_mutex_t* mutex = (pthread_mutex_t*) mtx;
pthread_mutex_destroy (mutex);
delete mutex;
}
// lock the mutex argument
bool c_mtxlock (void* mtx) {
if (mtx == 0) return false;
pthread_mutex_t* mutex = (pthread_mutex_t*) mtx;
return (pthread_mutex_lock (mutex) == 0) ? true : false;
}
// unlock the mutex argument
bool c_mtxunlock (void* mtx) {
if (mtx == 0) return true;
pthread_mutex_t* mutex = (pthread_mutex_t*) mtx;
return (pthread_mutex_unlock (mutex) == 0) ? true : false;
}
// try to lock a mutex
bool c_mtxtry (void* mtx) {
if (mtx == 0) return false;
pthread_mutex_t* mutex = (pthread_mutex_t*) mtx;
return (pthread_mutex_trylock (mutex) == 0) ? true : false;
}
// create a new condition variable
void* c_tcvcreate (void) {
// condition variable default attribute
pthread_condattr_t attr;
pthread_condattr_init (&attr);
pthread_cond_t* tcv = new pthread_cond_t;
if (tcv != 0) pthread_cond_init (tcv, &attr);
return tcv;
}
// wait on a condition variable
void c_tcvwait (void* tcv, void* mtx) {
if ((tcv == 0) || (mtx == 0)) return;
pthread_cond_t* condv = (pthread_cond_t*) tcv;
pthread_mutex_t* mutex = (pthread_mutex_t*) mtx;
pthread_cond_wait (condv, mutex);
}
// signal with a condition variable
void c_tcvsignal (void* tcv) {
if (tcv == 0) return;
pthread_cond_t* condv = (pthread_cond_t*) tcv;
pthread_cond_signal (condv);
}
// broadcast with a condition variable
void c_tcvbdcast (void* tcv) {
if (tcv == 0) return;
pthread_cond_t* condv = (pthread_cond_t*) tcv;
pthread_cond_signal (condv);
}
// destroy a condition variable
void c_tcvdestroy (void* tcv) {
pthread_cond_t* condv = (pthread_cond_t*) tcv;
delete condv;
}
}
syntax highlighted by Code2HTML, v. 0.9.1