|
posix/thread.hhGo 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 Last update Tue Nov 9 12:40:50 2004 Christian Holm Created by DoxyGen 1.3.9.1 |