posix/thread.hh

Go to the documentation of this file.
00001 //
00002 // $Id: thread.hh,v 1.5 2003/10/30 15:05:13 cholm Exp $ 
00003 //  
00004 //  threadmm::posix::thread
00005 //  Copyright (C) 2002 Christian Holm Christensen <cholm@nbi.dk> 
00006 //
00007 //  This library is free software; you can redistribute it and/or 
00008 //  modify it under the terms of the GNU Lesser General Public License 
00009 //  as published by the Free Software Foundation; either version 2.1 
00010 //  of the License, or (at your option) any later version. 
00011 //
00012 //  This library is distributed in the hope that it will be useful, 
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
00015 //  Lesser General Public License for more details. 
00016 // 
00017 //  You should have received a copy of the GNU Lesser General Public 
00018 //  License along with this library; if not, write to the Free 
00019 //  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
00020 //  02111-1307 USA 
00021 //
00022 #ifndef THREADMM_posix_thread
00023 #define THREADMM_posix_thread
00024 
00031 #ifndef THREADMM_thread
00032 # error Do not include threadmm/posix/thread.hh directly
00033 #endif
00034 
00035 #ifndef __PTHREAD_H__
00036 #include <pthread.h>
00037 #endif
00038 #ifndef __STDEXCEPT__
00039 #include <stdexcept>
00040 #endif
00041 #ifndef __CERRNO__
00042 #include <cerrno>
00043 #endif
00044 #ifndef __CASSERT__
00045 #include <cassert>
00046 #endif
00047 #ifndef THREADMM_thread_specific
00048 # include <threadmm/thread_specific.hh>
00049 #endif
00050 
00051 namespace threadmm 
00052 {
00053   namespace posix 
00054   {
00055     template <class C>
00056     struct thread_create 
00057     {
00061       C* on_null() volatile 
00062       {
00063   pthread_t pth     = pthread_self();
00064   C*        self    = new C;
00065   self->_thread     = pth;
00066   self->_is_running = true;
00067 #if HAVE_DECL_PTHREAD_GETATTR_NP > 0
00068   pthread_attr_t attr;
00069   pthread_getattr_np(pth, &attr);
00070   self->_attr = attr;
00071 #endif
00072   return self;
00073       }
00074     };
00076     template <typename C>
00077     struct thread_cleanup 
00078     {
00081       static void on_cleanup(C*) {}
00082     };
00083     
00089     template <typename Client>
00090     class thread 
00091     {
00092     private:
00093       template <typename C>
00094       friend struct thread_create;
00097 #if 0
00098       template <class C>
00099       struct create 
00100       {
00104   C* on_null() volatile 
00105   {
00106     pthread_t pth     = pthread_self();
00107     C*        self    = new C;
00108     self->_thread     = pth;
00109     self->_is_running = true;
00110 #if HAVE_DECL_PTHREAD_GETATTR_NP > 0
00111     pthread_attr_t attr;
00112     pthread_getattr_np(pth, &attr);
00113     self->_attr = attr;
00114 #endif
00115     return self;
00116   }
00117       };
00119       template <typename C>
00120       struct cleanup 
00121       {
00124   static void on_cleanup(C*) {}
00125       };
00126 #endif
00127     public:
00128       typedef Client client_type;
00129       typedef ::threadmm::thread_specific<client_type, 
00130             thread_create, 
00131             thread_cleanup> pool_type;
00132     private:
00133       pthread_t        _thread;     
00134       pthread_attr_t   _attr;     
00135       bool             _is_running; 
00136       static pool_type _pool;     
00141       static void* dispatch(void* arg);
00142 
00144       void init_kind(int flags);
00146       void init_guard_size(size_t n);
00148       void init_stack(void* address, size_t& n);
00149     protected:
00150       thread() : _is_running(false) {}
00153       void run(client_type* client);
00154     public:
00155       thread(int    flags,
00156        int    policy=regular,
00157        int    priority=0,
00158        int    gsize=-1, 
00159        void*  address=0,
00160        size_t ssize=0);
00161       virtual ~thread();
00162 
00163       // virtual void* operator()() = 0;
00165       int kind() const;
00166 
00168       void* stack(size_t& n) const;
00169       
00171       size_t guard_size() const;
00172 
00174       int scheduling(int flag, int priority);
00176       int scheduling(int& flag) const;
00177 
00179       void cancel();
00181       void detach();
00185       bool equal(const client_type& other);
00192       void* join();
00193 
00195 
00197       static void exit(void* retval); 
00199       static void kill_others(); 
00200 
00202       static int cancellation(int type);
00204       static int concurrency();
00206       static int concurrency(int c);
00208       static bool disable_cancellation();
00210       static bool enable_cancellation(); 
00211 
00213       static client_type& self();
00215       static void test_cancel();
00217       static void yield(); 
00225       static bool at_fork(void (*prep)(), void (*par)(), void (*chi)());
00226     };
00227 
00228     //________________________________________________________________
00229     template <typename Client>
00230     typename thread<Client>::pool_type thread<Client>::_pool;
00231 
00232     //________________________________________________________________
00233     template <typename Client>
00234     inline void* 
00235     thread<Client>::dispatch(void* arg) 
00236     {
00237       // Having entered the thread, and registered the object in
00238       // thread specific memory, we can execute the object. 
00239       void* ret = 0;
00240       client_type* t = static_cast<client_type*>(arg);
00241       assert(static_cast<void*>(t) == arg);
00242       try {
00243   // First thing to do, inside this function, is to register in
00244   // thread specific memory, the object that executed this
00245   // thread.  In that way, we can simply use the thread specific
00246   // memory to retrive the object in the member function self.
00247   // Much easier and safer than using some
00248   // static (shudder) list of all threads.  I go the inspiration
00249   // from the implamenation from the CThreads library. 
00250   // pthread_once(&_once, dispatcher_init);
00251   _pool.assign(t);
00252   assert(_pool.ptr() == t);
00253   if (!t) throw std::invalid_argument("No thread");
00254   ret = t->operator()();
00255       }
00256       catch (std::exception& e) {
00257   // Let user-derived class handle the exception.
00258   if (t) { 
00259     t->on_exception(e);
00260     t->_is_running = false;
00261   }
00262   return ret;
00263   // Or let terminate do it's job
00264   // else throw;
00265       }
00266       catch (...) {
00267   // Let user-derived class handle the exception.
00268   if (t) { 
00269     t->on_exception();
00270     t->_is_running = false;
00271   }
00272   // Let terminate do it's job
00273   throw;
00274       }
00275       t->_is_running = false;
00276       return ret;
00277     }
00278     //________________________________________________________________
00279     template <typename Client>
00280     inline thread<Client>::thread(int    flags,
00281           int    policy,
00282           int    priority,
00283           int    gsize, 
00284           void*  address,
00285           size_t ssize)
00286       : _is_running(false)
00287     {
00288       pthread_attr_init(&_attr);
00289 
00290       this->scheduling(policy, priority);
00291       this->init_kind(flags);
00292       if (gsize >= 0) this->init_guard_size(size_t(gsize));
00293       if (address)    this->init_stack(address, ssize);
00294     }
00295 
00296     //________________________________________________________________
00297     template <typename Client>
00298     inline thread<Client>::~thread() 
00299     {
00300       pthread_attr_destroy(&_attr);
00301       _pool.assign(0);
00302       if (_is_running) thread<Client>::exit(0);
00303     }
00304     
00305     //________________________________________________________________
00306     template <typename Client>
00307     inline void thread<Client>::init_kind(int flags)
00308     {
00309       int ret = 0;
00310       if (flags & detached) 
00311   ret = pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_DETACHED); 
00312       else 
00313   ret = pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_JOINABLE); 
00314       
00315       if (flags & inherit) 
00316   ret = pthread_attr_setinheritsched(&_attr, PTHREAD_INHERIT_SCHED);
00317       else  
00318   ret = pthread_attr_setinheritsched(&_attr, PTHREAD_EXPLICIT_SCHED);
00319       
00320       if (flags & process) 
00321   ret = pthread_attr_setscope(&_attr, PTHREAD_SCOPE_PROCESS);
00322       else  
00323   ret = pthread_attr_setscope(&_attr, PTHREAD_SCOPE_SYSTEM);
00324       if (ret == ENOTSUP) 
00325   throw std::runtime_error("scope not supported");
00326     }
00327 
00328     //________________________________________________________________
00329     template <typename Client>
00330     inline int thread<Client>::kind() const 
00331     {
00332       int flag = 0;
00333       
00334       int detach;
00335       pthread_attr_getdetachstate(&_attr, &detach);
00336       switch (detach){
00337       case PTHREAD_CREATE_DETACHED: 
00338   flag |= detached; break;
00339       case PTHREAD_CREATE_JOINABLE: 
00340   flag |= joinable; break;
00341       }
00342       
00343       int inher;
00344       pthread_attr_getinheritsched(&_attr, &inher);
00345       switch(inher) {
00346       case PTHREAD_INHERIT_SCHED:   
00347   flag |= inherit; break;
00348       case PTHREAD_EXPLICIT_SCHED:  
00349   flag |= own; break; 
00350       }
00351       
00352       int scope;
00353       pthread_attr_getscope(&_attr, &scope);
00354       switch (scope) {
00355       case PTHREAD_SCOPE_PROCESS:   
00356   flag |= process; break;
00357       case PTHREAD_SCOPE_SYSTEM:    
00358   flag |= system; break;
00359       }
00360       return flag;
00361     }
00362       
00363     //________________________________________________________________
00364     template <typename Client>
00365     inline void thread<Client>::init_stack(void* address, size_t &n) 
00366     {
00367       int ret = 0;
00368       void* a = address;
00369 #if HAVE_DECL_PTHREAD_ATTR_SETSTACK > 0
00370       ret = pthread_attr_setstack(&_attr, a, n);
00371 #else
00372 # if HAVE_DECL_PTHREAD_ATTR_SETSTACKADDR > 0
00373       ret = pthread_attr_setstackaddr(&_attr, a);
00374 # else
00375       a = 0;
00376 # endif
00377       if (!ret) pthread_attr_setstacksize(&_attr, n);
00378 #endif
00379       if (ret == EINVAL) 
00380   throw std::invalid_argument("invalid stack address and/or size");
00381     }
00382     
00383     //________________________________________________________________
00384     template <typename Client>
00385     inline void* thread<Client>::stack(size_t& n) const 
00386     {
00387       void* address = 0;
00388 #if HAVE_DECL_PTHREAD_ATTR_GETSTACK > 0
00389       pthread_attr_getstack(&_attr, &address, &n);
00390 #else
00391 # if HAVE_DECL_PTHREAD_ATTR_GETSTACKADDR > 0
00392       pthread_attr_getstackaddr(&_attr, &address);
00393 #endif
00394       pthread_attr_getstacksize(&_attr, &n);
00395 #endif
00396       return address;
00397     }
00398     
00399     //________________________________________________________________
00400     template <typename Client>
00401     inline void thread<Client>::init_guard_size(size_t n) 
00402     {
00403       int ret = 0;
00404 #if HAVE_DECL_PTHREAD_ATTR_SETGUARDSIZE > 0
00405       ret = pthread_attr_setguardsize(&_attr, n);
00406 #else 
00407       n = 0;
00408 #endif
00409       if (ret == EINVAL) 
00410   throw std::invalid_argument("invalid stack address and/or size");
00411     }
00412 
00413     //________________________________________________________________
00414     template <typename Client>
00415     inline size_t thread<Client>::guard_size() const
00416     {
00417       size_t n = 0;
00418 #if HAVE_DECL_PTHREAD_ATTR_GETGUARDSIZE > 0
00419       pthread_attr_getguardsize(&_attr, &n);
00420 #endif
00421       return n;
00422     }
00423     
00424     //________________________________________________________________
00425     template <typename Client>
00426     inline int thread<Client>::scheduling(int flag, int priority)
00427     {
00428       struct sched_param sp;
00429       int                ret    = 0;
00430       int                policy = 0;
00431       sp.sched_priority = priority;
00432       if      (flag & fifo)        policy = SCHED_FIFO;
00433       else if (flag & round_robin) policy = SCHED_RR;
00434       else                                                policy = SCHED_OTHER;
00435       
00436       if (!_is_running) { 
00437   ret = pthread_attr_setschedpolicy(&_attr, policy);
00438   ret = pthread_attr_setschedparam(&_attr, &sp);
00439       }
00440       else  
00441   ret = pthread_setschedparam(_thread, policy, &sp);
00442 
00443       switch (ret) {
00444       case EINVAL: throw std::invalid_argument("invalid policy"); break;
00445       case EPERM:  throw std::runtime_error("permission denied"); break;
00446       case ESRCH:  throw std::invalid_argument("thread not running"); break;
00447       case EFAULT: throw std::runtime_error("memory out of bounds"); break;
00448       }
00449       return sp.sched_priority;
00450     }
00451 
00452     //________________________________________________________________
00453     template <typename Client>
00454     inline int thread<Client>::scheduling(int& policy) const
00455     {
00456       struct sched_param sp;
00457       int                ret = 0;
00458       if (!_is_running) {
00459   ret = pthread_attr_getschedpolicy(&_attr, &policy); 
00460   ret = pthread_attr_getschedparam(&_attr, &sp);
00461       }
00462       else 
00463   pthread_getschedparam(_thread, &policy, &sp);
00464       switch (policy) {
00465       case SCHED_FIFO:   policy = fifo;        break;
00466       case SCHED_RR:     policy = round_robin; break;
00467       case SCHED_OTHER:  policy = regular;     break;
00468       }
00469       switch (ret) {
00470       case ESRCH:  throw std::invalid_argument("thread not running"); break;
00471       case EFAULT: throw std::runtime_error("invalid memory"); break;
00472       }
00473       return sp.sched_priority;
00474     }
00475 
00476     //________________________________________________________________
00477     template <typename Client>
00478     inline void thread<Client>::cancel()
00479     {
00480       int ret = pthread_cancel(_thread);
00481       switch (ret) {
00482       case ESRCH: throw std::runtime_error("no such thread");
00483       }
00484     }
00485 
00486     //________________________________________________________________
00487     template <typename Client>
00488     inline void thread<Client>::detach()
00489     {
00490       int ret = pthread_detach(_thread);
00491       switch (ret) {
00492       case ESRCH: throw std::runtime_error("no such thread");
00493       case EINVAL: throw std::runtime_error("thread is already detached");
00494       }
00495     }
00496 
00497     //________________________________________________________________
00498     template <typename Client>
00499     inline void thread<Client>::run(client_type* client)
00500     {
00501       if (_is_running) return;
00502 #if 0
00503       std::cout << "Client: " << std::hex << client 
00504     << " Self: " << this << std::endl;
00505 #endif
00506       int ret= pthread_create(&_thread, &_attr, dispatch, 
00507             static_cast<void*>(client));
00508       switch (ret) {
00509       case EPERM:  throw std::runtime_error("permission denied");
00510       case EAGAIN: throw std::runtime_error("out of resources");
00511       }
00512       _is_running = true;
00513     }
00514 
00515     //________________________________________________________________
00516     template <typename Client>
00517     inline bool thread<Client>::equal(const client_type& other) 
00518     {
00519       return pthread_equal(_thread, other._thread) == 0 ? false : true;
00520     }
00521 
00522     //________________________________________________________________
00523     template <typename Client>
00524     inline int thread<Client>::cancellation(int type) 
00525     {
00526       int old = 0;
00527       switch (type) {
00528       case deferred: 
00529   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,&old); break;
00530       case asynchronous:
00531   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&old); break;
00532       default:
00533   throw std::invalid_argument("unknown cancellation type");    
00534       }
00535       switch(old) {
00536       case PTHREAD_CANCEL_DEFERRED:     return deferred;
00537       case PTHREAD_CANCEL_ASYNCHRONOUS: return asynchronous;
00538       }
00539       return 0;
00540     }
00541 
00542     //________________________________________________________________
00543     template <typename Client>
00544     inline int thread<Client>::concurrency()
00545     {
00546 #if HAVE_DECL_PTHREAD_GETCONCURRENCY > 0
00547       return pthread_getconcurrency();
00548 #else
00549       return 0;
00550 #endif
00551     }
00552 
00553     //________________________________________________________________
00554     template <typename Client>
00555     inline int thread<Client>::concurrency(int c)
00556     {
00557 #if HAVE_DECL_PTHREAD_SETCONCURRENCY > 0
00558       pthread_setconcurrency(c);
00559       return c;
00560 #else
00561       return 0;
00562 #endif
00563     }
00564 
00565     //________________________________________________________________
00566     template <typename Client>
00567     inline bool thread<Client>::disable_cancellation()
00568     {
00569       int old = 0;
00570       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
00571       switch (old) {
00572       case PTHREAD_CANCEL_ENABLE: return true;
00573       case PTHREAD_CANCEL_DISABLE: return false;
00574       }
00575       return true;
00576     }
00577 
00578     //________________________________________________________________
00579     template <typename Client>
00580     inline bool thread<Client>::enable_cancellation() 
00581     {
00582       int old = 0;
00583       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
00584       switch (old) {
00585       case PTHREAD_CANCEL_ENABLE: return true;
00586       case PTHREAD_CANCEL_DISABLE: return false;
00587       }
00588       return true;
00589     }
00590 
00591     //________________________________________________________________
00592     template <typename Client>
00593     inline void thread<Client>::exit(void* retval)
00594     {
00595       self()._is_running = false;
00596       // For some odd reason, this will cause `std::terminate()' to be
00597       // called, if optimistion is turned on 
00598       pthread_exit(retval);
00599     }
00600 
00601     //________________________________________________________________
00602     template <typename Client>
00603     inline void* thread<Client>::join() 
00604     {
00605       assert(!equal(self()));
00606       if (equal(self())) 
00607   throw std::runtime_error("cannot join to self, would deadlock");
00608       void* retval;
00609       int ret = pthread_join(_thread, &retval);
00610       switch (ret) {
00611       case ESRCH:   throw std::invalid_argument("no such thread"); 
00612       case EINVAL:  throw std::invalid_argument("thread detached or joined");
00613       case EDEADLK: throw std::runtime_error("would deadlock");
00614       }
00615       return retval;
00616     }
00617 
00618     //________________________________________________________________
00619     template <typename Client>
00620     inline void thread<Client>::kill_others() 
00621     {
00622 #if HAVE_DECL_PTHREAD_KILL_OTHER_THREADS_NP > 0
00623       pthread_kill_other_threads_np();
00624 #endif 
00625     }
00626 
00627     //________________________________________________________________
00628     template <typename Client>
00629     inline typename thread<Client>::client_type& thread<Client>::self() 
00630     {
00631       client_type* s = _pool.ptr();
00632       return *s;
00633     }
00634 
00635 
00636     //________________________________________________________________
00637     template <typename Client>
00638     inline void thread<Client>::test_cancel() 
00639     {
00640       pthread_testcancel();
00641     }
00642 
00643 
00644     //________________________________________________________________
00645     template <typename Client>
00646     inline void thread<Client>::yield() 
00647     {
00648 #if HAVE_DECL_PTHREAD_YIELD > 0
00649       pthread_yield();
00650 #else
00651       struct timespec request = { 0, 1000 };
00652       nanosleep(&request, NULL);
00653 #endif
00654     }
00655 
00656     //________________________________________________________________
00657     template <typename Client>
00658     inline bool thread<Client>::at_fork(void(*prep)(), void(*par)(), 
00659           void(*chi)())
00660     {
00661 #if HAVE_DECL_PTHREAD_ATFORK > 0
00662       int ret = pthread_atfork(prep, par, chi);
00663       return ret == 0 ? true : false;
00664 #else
00665       void (*f1)() = prep; // Smak warnings
00666       void (*f2)() = par;  // Smak warnings
00667       void (*f3)() = chi;  // Smak warnings
00668       f1 = f2 = f3 = 0;
00669       return false;
00670 #endif
00671     }
00672   }
00673 }
00674 
00675 #endif
00676 //____________________________________________________________________
00677 //
00678 // EOF
00679 //
00680 
00681        
Top of page
Last update Tue Nov 9 12:40:50 2004
Christian Holm
Created by DoxyGen 1.3.9.1