cwidget 0.5.16

threads.h

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