00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
00143
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
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
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
00299
00300
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
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
00760
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
00937
00938
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
00993
00994
00995
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