// --------------------------------------------------------------------------- // - 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; } }