cwidget
0.5.16
|
00001 // threads.h -*-c++-*- 00002 // 00003 // Copyright (C) 2005-2009 Daniel Burrows 00004 // 00005 // This program is free software; you can redistribute it and/or 00006 // modify it under the terms of the GNU General Public License as 00007 // published by the Free Software Foundation; either version 2 of 00008 // the License, or (at your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 // General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with this program; see the file COPYING. If not, write to 00017 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 // Boston, MA 02111-1307, USA. 00019 // 00020 // A simple thread wrapper library. I'm not using the existing ones 00021 // in order to keep aptitude's dependency count low (as long as I 00022 // don't need too much out of it, this should be fairly 00023 // simple..right?). The API was inspired by that of boost::threads. 00024 00025 #ifndef THREADS_H 00026 #define THREADS_H 00027 00028 #include <errno.h> 00029 #include <cwidget/generic/util/exception.h> 00030 00031 namespace cwidget 00032 { 00038 namespace threads 00039 { 00041 class ThreadException : public util::Exception 00042 { 00043 }; 00044 00049 class ThreadCreateException : public ThreadException 00050 { 00051 int errnum; 00052 public: 00053 ThreadCreateException(int error) 00054 : errnum(error) 00055 { 00056 } 00057 00058 int get_errnum() const { return errnum; } 00059 00060 std::string errmsg() const; 00061 }; 00062 00064 class ThreadJoinException : public ThreadException 00065 { 00066 std::string reason; 00067 00068 int errnum; 00069 public: 00070 ThreadJoinException(const int error); 00071 00072 int get_errnum() const { return errnum; } 00073 std::string errmsg() const; 00074 }; 00075 00081 class ConditionNotLockedException : public ThreadException 00082 { 00083 public: 00084 std::string errmsg() const; 00085 }; 00086 00088 class DoubleLockException : public ThreadException 00089 { 00090 public: 00091 std::string errmsg() const; 00092 }; 00093 00100 class thread 00101 { 00102 pthread_t tid; 00103 bool joined; 00104 00105 thread(const thread &other); 00106 thread &operator=(const thread &other); 00107 00108 00109 00110 template<typename F> 00111 static void *bootstrap(void *p) 00112 { 00113 F thunk(*((F *) p)); 00114 00115 delete ((F *) p); 00116 00117 thunk(); 00118 00119 return 0; 00120 } 00121 00122 public: 00131 class attr 00132 { 00133 pthread_attr_t attrs; 00134 00135 friend class thread; 00136 public: 00137 attr() 00138 { 00139 pthread_attr_init(&attrs); 00140 } 00141 00142 // All attributes except detach state can be manipulated (detach 00143 // state is left at PTHREAD_CREATE_JOINABLE). 00144 00145 void set_inherit_sched(int i) 00146 { 00147 pthread_attr_setinheritsched(&attrs, i); 00148 } 00149 00150 int get_inherit_sched() const 00151 { 00152 int rval; 00153 pthread_attr_getinheritsched(&attrs, &rval); 00154 return rval; 00155 } 00156 00157 void set_sched_param(const sched_param &sp) 00158 { 00159 pthread_attr_setschedparam(&attrs, &sp); 00160 } 00161 00162 sched_param get_sched_param() const 00163 { 00164 sched_param rval; 00165 pthread_attr_getschedparam(&attrs, &rval); 00166 return rval; 00167 } 00168 00169 void set_sched_policy(int p) 00170 { 00171 pthread_attr_setschedpolicy(&attrs, p); 00172 } 00173 00174 int get_sched_policy() const 00175 { 00176 int rval; 00177 pthread_attr_getschedpolicy(&attrs, &rval); 00178 return rval; 00179 } 00180 00181 void set_scope(int p) 00182 { 00183 pthread_attr_setscope(&attrs, p); 00184 } 00185 00186 int get_scope() const 00187 { 00188 int rval; 00189 pthread_attr_getscope(&attrs, &rval); 00190 return rval; 00191 } 00192 00193 ~attr() 00194 { 00195 pthread_attr_destroy(&attrs); 00196 } 00197 }; 00198 00209 template<typename F> 00210 thread(const F &thunk, const attr &a = attr()) 00211 :joined(false) 00212 { 00213 // Create a thunk on the heap to pass to the new thread. 00214 F *tmp = new F(thunk); 00215 00216 if(pthread_create(&tid, &a.attrs, &thread::bootstrap<F>, tmp) != 0) 00217 { 00218 int errnum = errno; 00219 00220 delete tmp; 00221 00222 throw ThreadCreateException(errnum); 00223 } 00224 } 00225 00226 ~thread() 00227 { 00228 if(!joined) 00229 pthread_detach(tid); 00230 } 00231 00233 void join() 00234 { 00235 int rval = pthread_join(tid, NULL); 00236 00237 if(rval != 0) 00238 throw ThreadJoinException(rval); 00239 else 00240 joined = true; 00241 } 00242 00244 void cancel() 00245 { 00246 pthread_cancel(tid); 00247 } 00248 }; 00249 00262 template<typename F> 00263 struct noncopy_bootstrap 00264 { 00265 F &f; 00266 public: 00271 noncopy_bootstrap(F &_f) 00272 :f(_f) 00273 { 00274 } 00275 00277 void operator()() 00278 { 00279 f(); 00280 } 00281 }; 00282 00283 class condition; 00284 00285 // The mutex abstraction 00286 class mutex 00287 { 00288 public: 00289 class lock; 00290 class try_lock; 00291 00292 private: 00293 pthread_mutex_t m; 00294 00295 friend class lock; 00296 friend class try_lock; 00297 00298 // Conditions need to look inside mutexes and locks to find the 00299 // real mutex object so the underlying thread library can do an 00300 // atomic unlock-and-wait. 00301 friend class condition; 00302 00303 mutex(const mutex &other); 00304 mutex &operator=(const mutex &other); 00305 public: 00307 class attr 00308 { 00309 pthread_mutexattr_t attrs; 00310 00311 friend class mutex; 00312 00313 public: 00314 attr() 00315 { 00316 pthread_mutexattr_init(&attrs); 00317 } 00318 00319 attr(int kind) 00320 { 00321 pthread_mutexattr_init(&attrs); 00322 pthread_mutexattr_settype(&attrs, kind); 00323 } 00324 00325 ~attr() 00326 { 00327 pthread_mutexattr_destroy(&attrs); 00328 } 00329 00330 int settype(int kind) 00331 { 00332 return pthread_mutexattr_settype(&attrs, kind); 00333 } 00334 00335 int gettype() 00336 { 00337 int rval; 00338 pthread_mutexattr_gettype(&attrs, &rval); 00339 return rval; 00340 } 00341 }; 00342 00347 class lock 00348 { 00349 mutex &parent; 00350 00351 bool locked; 00352 00353 friend class condition; 00354 00355 lock(const lock &other); 00356 lock &operator=(const lock &other); 00357 public: 00358 lock(mutex &_parent) 00359 :parent(_parent), locked(false) 00360 { 00361 acquire(); 00362 } 00363 00365 void acquire() 00366 { 00367 if(locked) 00368 throw DoubleLockException(); 00369 00370 pthread_mutex_lock(&parent.m); 00371 locked = true; 00372 } 00373 00375 void release() 00376 { 00377 pthread_mutex_unlock(&parent.m); 00378 locked = false; 00379 } 00380 00381 bool get_locked() const 00382 { 00383 return locked; 00384 } 00385 00386 ~lock() 00387 { 00388 if(locked) 00389 pthread_mutex_unlock(&parent.m); 00390 } 00391 }; 00392 00394 class try_lock 00395 { 00396 mutex &parent; 00397 00398 bool locked; 00399 00400 friend class condition; 00401 00402 try_lock(const try_lock &other); 00403 try_lock &operator=(const try_lock &other); 00404 public: 00405 try_lock(mutex &_parent) 00406 :parent(_parent) 00407 { 00408 acquire(); 00409 } 00410 00411 ~try_lock() 00412 { 00413 if(locked) 00414 pthread_mutex_unlock(&parent.m); 00415 } 00416 00417 void acquire() 00418 { 00419 if(locked) 00420 throw DoubleLockException(); 00421 00422 locked = pthread_mutex_trylock(&parent.m); 00423 } 00424 00425 void release() 00426 { 00427 pthread_mutex_unlock(&parent.m); 00428 locked = false; 00429 } 00430 00431 bool get_locked() const 00432 { 00433 return locked; 00434 } 00435 }; 00436 00437 mutex() 00438 { 00439 pthread_mutex_init(&m, NULL); 00440 } 00441 00442 mutex(const attr &a) 00443 { 00444 pthread_mutex_init(&m, &a.attrs); 00445 } 00446 00447 ~mutex() 00448 { 00449 pthread_mutex_destroy(&m); 00450 } 00451 }; 00452 00456 class recursive_mutex : public mutex 00457 { 00458 public: 00459 recursive_mutex() 00460 :mutex(attr(PTHREAD_MUTEX_RECURSIVE)) 00461 { 00462 } 00463 }; 00464 00469 class condition 00470 { 00471 pthread_cond_t cond; 00472 public: 00473 condition() 00474 { 00475 pthread_cond_init(&cond, NULL); 00476 } 00477 00478 ~condition() 00479 { 00480 // Wakey wakey 00481 pthread_cond_broadcast(&cond); 00482 pthread_cond_destroy(&cond); 00483 } 00484 00485 void wake_one() 00486 { 00487 pthread_cond_signal(&cond); 00488 } 00489 00490 void wake_all() 00491 { 00492 pthread_cond_broadcast(&cond); 00493 } 00494 00501 template<typename Lock> 00502 void wait(const Lock &l) 00503 { 00504 if(!l.get_locked()) 00505 throw ConditionNotLockedException(); 00506 00507 pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock, &l.parent.m); 00508 pthread_cond_wait(&cond, &l.parent.m); 00509 pthread_cleanup_pop(0); 00510 } 00511 00520 template<typename Lock, typename Pred> 00521 void wait(const Lock &l, Pred p) 00522 { 00523 if(!l.get_locked()) 00524 throw ConditionNotLockedException(); 00525 00526 while(!p()) 00527 wait(l); 00528 } 00529 00545 template<typename Lock> 00546 bool timed_wait(const Lock &l, const timespec &until) 00547 { 00548 if(!l.get_locked()) 00549 throw ConditionNotLockedException(); 00550 00551 int rval; 00552 00553 pthread_cleanup_push((void(*)(void *))&pthread_mutex_unlock, &l.parent.m); 00554 while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR) 00555 ; 00556 pthread_cleanup_pop(0); 00557 00558 return rval != ETIMEDOUT; 00559 } 00560 00571 template<typename Lock, typename Pred> 00572 bool timed_wait(const Lock &l, const timespec &until, const Pred &p) 00573 { 00574 if(!l.get_locked()) 00575 throw ConditionNotLockedException(); 00576 00577 while(!p()) 00578 { 00579 if(!timed_wait(l, until)) 00580 return false; 00581 } 00582 00583 return true; 00584 } 00585 }; 00586 00598 template<typename T> 00599 class box 00600 { 00601 T val; 00602 bool filled; 00603 00604 condition cond; 00605 mutex m; 00606 00607 box(const box &other); 00608 box &operator=(const box &other); 00609 public: 00611 box() 00612 :filled(false) 00613 { 00614 } 00615 00617 box(const T &_val) 00618 :val(_val), filled(true) 00619 { 00620 } 00621 00625 T take(); 00626 00630 void put(const T &t); 00631 00638 bool try_take(T &out); 00639 00648 bool try_put(const T &t); 00649 00653 bool timed_take(T &out, const timespec &until); 00654 00658 bool timed_put(const T &t, const timespec &until); 00659 00664 template<typename Mutator> 00665 void update(const Mutator &m); 00666 }; 00667 00672 template<> 00673 class box<void> 00674 { 00675 bool filled; 00676 mutex m; 00677 condition cond; 00678 public: 00679 box() 00680 :filled(false) 00681 { 00682 } 00683 00684 box(bool _filled) 00685 :filled(_filled) 00686 { 00687 } 00688 00689 void take(); 00690 00691 void put(); 00692 00693 bool try_take(); 00694 bool try_put(); 00695 00696 bool timed_take(const timespec &until); 00697 bool timed_put(const timespec &until); 00698 00699 template<typename Mutator> 00700 void update(const Mutator &m) 00701 { 00702 take(); 00703 try 00704 { 00705 m(); 00706 } 00707 catch(...) 00708 { 00709 put(); 00710 throw; 00711 } 00712 00713 put(); 00714 } 00715 }; 00716 00718 struct bool_ref_pred 00719 { 00720 const bool &b; 00721 public: 00722 bool_ref_pred(const bool &_b) 00723 :b(_b) 00724 { 00725 } 00726 00727 bool operator()() const 00728 { 00729 return b; 00730 } 00731 }; 00732 00734 struct not_bool_ref_pred 00735 { 00736 const bool &b; 00737 public: 00738 not_bool_ref_pred(const bool &_b) 00739 :b(_b) 00740 { 00741 } 00742 00743 bool operator()() const 00744 { 00745 return !b; 00746 } 00747 }; 00748 00749 template<typename T> 00750 inline 00751 T box<T>::take() 00752 { 00753 mutex::lock l(m); 00754 00755 cond.wait(l, bool_ref_pred(filled)); 00756 00757 filled = false; 00758 00759 // Interesting question: does l get released before or after the 00760 // copy? To be safe, I explicitly copy before I return. 00761 T rval = val; 00762 return rval; 00763 } 00764 00765 inline 00766 void box<void>::take() 00767 { 00768 mutex::lock l(m); 00769 cond.wait(l, bool_ref_pred(filled)); 00770 filled = false; 00771 } 00772 00773 template<typename T> 00774 inline 00775 bool box<T>::try_take(T &out) 00776 { 00777 mutex::lock l(m); 00778 00779 if(filled) 00780 { 00781 filled = false; 00782 out = val; 00783 return true; 00784 } 00785 else 00786 return false; 00787 } 00788 00789 inline 00790 bool box<void>::try_take() 00791 { 00792 mutex::lock l(m); 00793 00794 if(filled) 00795 { 00796 filled = false; 00797 return true; 00798 } 00799 else 00800 return false; 00801 } 00802 00803 template<typename T> 00804 inline 00805 bool box<T>::timed_take(T &out, const timespec &until) 00806 { 00807 mutex::lock l(m); 00808 00809 if(cond.timed_wait(l, until, bool_ref_pred(filled))) 00810 { 00811 filled = false; 00812 out = val; 00813 return true; 00814 } 00815 else 00816 return false; 00817 } 00818 00819 inline 00820 bool box<void>::timed_take(const timespec &until) 00821 { 00822 mutex::lock l(m); 00823 00824 if(cond.timed_wait(l, until, bool_ref_pred(filled))) 00825 { 00826 filled = false; 00827 return true; 00828 } 00829 else 00830 return false; 00831 } 00832 00833 template<typename T> 00834 inline 00835 void box<T>::put(const T &new_val) 00836 { 00837 mutex::lock l(m); 00838 00839 cond.wait(l, not_bool_ref_pred(filled)); 00840 00841 filled = true; 00842 val = new_val; 00843 cond.wake_one(); 00844 } 00845 00846 inline 00847 void box<void>::put() 00848 { 00849 mutex::lock l(m); 00850 00851 cond.wait(l, not_bool_ref_pred(filled)); 00852 00853 filled = true; 00854 cond.wake_one(); 00855 } 00856 00857 template<typename T> 00858 inline 00859 bool box<T>::try_put(const T &new_val) 00860 { 00861 mutex::lock l(m); 00862 00863 if(!filled) 00864 { 00865 filled = true; 00866 val = new_val; 00867 cond.wake_one(); 00868 return true; 00869 } 00870 else 00871 return false; 00872 } 00873 00874 inline 00875 bool box<void>::try_put() 00876 { 00877 mutex::lock l(m); 00878 00879 if(!filled) 00880 { 00881 filled = true; 00882 cond.wake_one(); 00883 return true; 00884 } 00885 else 00886 return false; 00887 } 00888 00889 template<typename T> 00890 inline 00891 bool box<T>::timed_put(const T &new_val, const timespec &until) 00892 { 00893 mutex::lock l(m); 00894 00895 if(cond.timed_wait(l, until, not_bool_ref_pred(filled))) 00896 { 00897 filled = true; 00898 val = new_val; 00899 cond.wake_one(); 00900 return true; 00901 } 00902 else 00903 return false; 00904 } 00905 00906 inline 00907 bool box<void>::timed_put(const timespec &until) 00908 { 00909 mutex::lock l(m); 00910 00911 if(cond.timed_wait(l, until, not_bool_ref_pred(filled))) 00912 { 00913 filled = true; 00914 cond.wake_one(); 00915 return true; 00916 } 00917 else 00918 return false; 00919 } 00920 00921 template<typename T> 00922 template<typename Mutator> 00923 inline 00924 void box<T>::update(const Mutator &m) 00925 { 00926 mutex::lock l(m); 00927 00928 cond.wait(l, bool_ref_pred(filled)); 00929 00930 T new_val = m(val); 00931 00932 val = new_val; 00933 cond.wake_one(); 00934 } 00935 00936 // A ptr_box is like a box, but it wraps a pointer to its internal 00937 // object. When a filled ptr_box is destroyed, it deletes the 00938 // pointer that it contains. 00939 template<typename T> 00940 class ptr_box 00941 { 00942 box<T *> b; 00943 public: 00944 ptr_box() 00945 { 00946 } 00947 00948 ptr_box(const T *val) 00949 :b(val) 00950 { 00951 } 00952 00953 ~ptr_box() 00954 { 00955 T *x; 00956 00957 if(b.try_get(x)) 00958 delete x; 00959 } 00960 00961 T *take() 00962 { 00963 return b.take(); 00964 } 00965 00966 bool try_take(const T * &out) 00967 { 00968 return b.try_take(out); 00969 } 00970 00971 bool timed_take(const T * &out, const timespec &until) 00972 { 00973 return b.timed_take(out); 00974 } 00975 00976 void put(const T *in) 00977 { 00978 b.put(in); 00979 } 00980 00981 bool try_put(const T *in) 00982 { 00983 return b.try_put(in); 00984 } 00985 00986 bool timed_put(const T *in, const timespec &until) 00987 { 00988 return b.timed_put(in, until); 00989 } 00990 }; 00991 00992 // A utility that proxies for noncopyable thread bootstrap 00993 // objects. The only requirement is that the pointer passed 00994 // to the constructor must not be destroyed until the thread 00995 // completes. 00996 template<typename F> 00997 class bootstrap_proxy 00998 { 00999 F *f; 01000 public: 01001 bootstrap_proxy(F *_f) 01002 : f(_f) 01003 { 01004 } 01005 01006 void operator()() const 01007 { 01008 (*f)(); 01009 } 01010 }; 01011 01012 template<typename F> 01013 bootstrap_proxy<F> make_bootstrap_proxy(F *f) 01014 { 01015 return bootstrap_proxy<F>(f); 01016 } 01017 } 01018 } 01019 01020 #endif // THREADS_H 01021