threads.h

00001 // threads.h                                              -*-c++-*-
00002 //
00003 //   Copyright (C) 2005-2008 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     public:
00052       std::string errmsg() const;
00053     };
00054 
00056     class ThreadJoinException : public ThreadException
00057     {
00058       std::string reason;
00059     public:
00060       ThreadJoinException(const int error);
00061 
00062       std::string errmsg() const;
00063     };
00064 
00070     class ConditionNotLockedException : public ThreadException
00071     {
00072     public:
00073       std::string errmsg() const;
00074     };
00075 
00077     class DoubleLockException : public ThreadException
00078     {
00079     public:
00080       std::string errmsg() const;
00081     };
00082 
00089     class thread
00090     {
00091       pthread_t tid;
00092       bool joined;
00093 
00094       thread(const thread &other);
00095       thread &operator=(const thread &other);
00096 
00097 
00098 
00099       template<typename F>
00100       static void *bootstrap(void *p)
00101       {
00102         F thunk(*((F *) p));
00103 
00104         delete ((F *) p);
00105 
00106         thunk();
00107 
00108         return 0;
00109       }
00110 
00111     public:
00120       class attr
00121       {
00122         pthread_attr_t attrs;
00123 
00124         friend class thread;
00125       public:
00126         attr()
00127         {
00128           pthread_attr_init(&attrs);
00129         }
00130 
00131         // All attributes except detach state can be manipulated (detach
00132         // state is left at PTHREAD_CREATE_JOINABLE).
00133 
00134         void set_inherit_sched(int i)
00135         {
00136           pthread_attr_setinheritsched(&attrs, i);
00137         }
00138 
00139         int get_inherit_sched() const
00140         {
00141           int rval;
00142           pthread_attr_getinheritsched(&attrs, &rval);
00143           return rval;
00144         }
00145 
00146         void set_sched_param(const sched_param &sp)
00147         {
00148           pthread_attr_setschedparam(&attrs, &sp);
00149         }
00150 
00151         sched_param get_sched_param() const
00152         {
00153           sched_param rval;
00154           pthread_attr_getschedparam(&attrs, &rval);
00155           return rval;
00156         }
00157 
00158         void set_sched_policy(int p)
00159         {
00160           pthread_attr_setschedpolicy(&attrs, p);
00161         }
00162 
00163         int get_sched_policy() const
00164         {
00165           int rval;
00166           pthread_attr_getschedpolicy(&attrs, &rval);
00167           return rval;
00168         }
00169 
00170         void set_scope(int p)
00171         {
00172           pthread_attr_setscope(&attrs, p);
00173         }
00174 
00175         int get_scope() const
00176         {
00177           int rval;
00178           pthread_attr_getscope(&attrs, &rval);
00179           return rval;
00180         }
00181 
00182         ~attr()
00183         {
00184           pthread_attr_destroy(&attrs);
00185         }
00186       };
00187 
00198       template<typename F>
00199       thread(const F &thunk, const attr &a = attr())
00200         :joined(false)
00201       {
00202         // Create a thunk on the heap to pass to the new thread.
00203         F *tmp = new F(thunk);
00204 
00205         if(pthread_create(&tid, &a.attrs, &thread::bootstrap<F>, tmp) != 0)
00206           {
00207             delete tmp;
00208 
00209             throw ThreadCreateException();
00210           }
00211       }
00212 
00213       ~thread()
00214       {
00215         if(!joined)
00216           pthread_detach(tid);
00217       }
00218 
00220       void join()
00221       {
00222         int rval = pthread_join(tid, NULL);
00223 
00224         if(rval != 0)
00225           throw ThreadJoinException(rval);
00226         else
00227           joined = true;
00228       }
00229 
00231       void cancel()
00232       {
00233         pthread_cancel(tid);
00234       }
00235     };
00236 
00249     template<typename F>
00250     struct noncopy_bootstrap
00251     {
00252       F &f;
00253     public:
00258       noncopy_bootstrap(F &_f)
00259         :f(_f)
00260       {
00261       }
00262 
00264       void operator()()
00265       {
00266         f();
00267       }
00268     };
00269 
00270     class condition;
00271 
00272     // The mutex abstraction
00273     class mutex
00274     {
00275     public:
00276       class lock;
00277       class try_lock;
00278 
00279     private:
00280       pthread_mutex_t m;
00281 
00282       friend class lock;
00283       friend class try_lock;
00284 
00285       // Conditions need to look inside mutexes and locks to find the
00286       // real mutex object so the underlying thread library can do an
00287       // atomic unlock-and-wait.
00288       friend class condition;
00289 
00290       mutex(const mutex &other);
00291       mutex &operator=(const mutex &other);
00292     public:
00294       class attr
00295       {
00296         pthread_mutexattr_t attrs;
00297 
00298         friend class mutex;
00299 
00300       public:
00301         attr()
00302         {
00303           pthread_mutexattr_init(&attrs);
00304         }
00305 
00306         attr(int kind)
00307         {
00308           pthread_mutexattr_init(&attrs);
00309           pthread_mutexattr_settype(&attrs, kind);
00310         }
00311 
00312         ~attr()
00313         {
00314           pthread_mutexattr_destroy(&attrs);
00315         }
00316 
00317         int settype(int kind)
00318         {
00319           return pthread_mutexattr_settype(&attrs, kind);
00320         }
00321 
00322         int gettype()
00323         {
00324           int rval;
00325           pthread_mutexattr_gettype(&attrs, &rval);
00326           return rval;
00327         }
00328       };
00329 
00334       class lock
00335       {
00336         mutex &parent;
00337 
00338         bool locked;
00339 
00340         friend class condition;
00341 
00342         lock(const lock &other);
00343         lock &operator=(const lock &other);
00344       public:
00345         lock(mutex &_parent)
00346           :parent(_parent), locked(false)
00347         {
00348           acquire();
00349         }
00350 
00352         void acquire()
00353         {
00354           if(locked)
00355             throw DoubleLockException();
00356 
00357           pthread_mutex_lock(&parent.m);
00358           locked = true;
00359         }
00360 
00362         void release()
00363         {
00364           pthread_mutex_unlock(&parent.m);
00365           locked = false;
00366         }
00367 
00368         bool get_locked() const
00369         {
00370           return locked;
00371         }
00372 
00373         ~lock()
00374         {
00375           if(locked)
00376             pthread_mutex_unlock(&parent.m);
00377         }
00378       };
00379 
00381       class try_lock
00382       {
00383         mutex &parent;
00384 
00385         bool locked;
00386 
00387         friend class condition;
00388 
00389         try_lock(const try_lock &other);
00390         try_lock &operator=(const try_lock &other);
00391       public:
00392         try_lock(mutex &_parent)
00393           :parent(_parent)
00394         {
00395           acquire();
00396         }
00397 
00398         ~try_lock()
00399         {
00400           if(locked)
00401             pthread_mutex_unlock(&parent.m);
00402         }
00403 
00404         void acquire()
00405         {
00406           if(locked)
00407             throw DoubleLockException();
00408 
00409           locked = pthread_mutex_trylock(&parent.m);
00410         }
00411 
00412         void release()
00413         {
00414           pthread_mutex_unlock(&parent.m);
00415           locked = false;
00416         }
00417 
00418         bool get_locked() const
00419         {
00420           return locked;
00421         }
00422       };
00423 
00424       mutex()
00425       {
00426         pthread_mutex_init(&m, NULL);
00427       }
00428 
00429       mutex(const attr &a)
00430       {
00431         pthread_mutex_init(&m, &a.attrs);
00432       }
00433 
00434       ~mutex()
00435       {
00436         pthread_mutex_destroy(&m);
00437       }
00438     };
00439 
00443     class recursive_mutex : public mutex
00444     {
00445     public:
00446       recursive_mutex()
00447         :mutex(attr(PTHREAD_MUTEX_RECURSIVE))
00448       {
00449       }
00450     };
00451 
00456     class condition
00457     {
00458       pthread_cond_t cond;
00459     public:
00460       condition()
00461       {
00462         pthread_cond_init(&cond, NULL);
00463       }
00464 
00465       ~condition()
00466       {
00467         // Wakey wakey
00468         pthread_cond_broadcast(&cond);
00469         pthread_cond_destroy(&cond);
00470       }
00471 
00472       void wake_one()
00473       {
00474         pthread_cond_signal(&cond);
00475       }
00476 
00477       void wake_all()
00478       {
00479         pthread_cond_broadcast(&cond);
00480       }
00481 
00488       template<typename Lock>
00489       void wait(const Lock &l)
00490       {
00491         if(!l.get_locked())
00492           throw ConditionNotLockedException();
00493 
00494         pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock, &l.parent.m);
00495         pthread_cond_wait(&cond, &l.parent.m);
00496         pthread_cleanup_pop(0);
00497       }
00498 
00507       template<typename Lock, typename Pred>
00508       void wait(const Lock &l, Pred p)
00509       {
00510         if(!l.get_locked())
00511           throw ConditionNotLockedException();
00512 
00513         while(!p())
00514           wait(l);
00515       }
00516 
00532       template<typename Lock>
00533       bool timed_wait(const Lock &l, const timespec &until)
00534       {
00535         if(!l.get_locked())
00536           throw ConditionNotLockedException();
00537 
00538         int rval;
00539 
00540         pthread_cleanup_push((void(*)(void *))&pthread_mutex_unlock, &l.parent.m);
00541         while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR)
00542           ;
00543         pthread_cleanup_pop(0);
00544 
00545         return rval != ETIMEDOUT;
00546       }
00547 
00558       template<typename Lock, typename Pred>
00559       bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
00560       {
00561         if(!l.get_locked())
00562           throw ConditionNotLockedException();
00563 
00564         while(!p())
00565           {
00566             if(!timed_wait(l, until))
00567               return false;
00568           }
00569 
00570         return true;
00571       }
00572     };
00573 
00585     template<typename T>
00586     class box
00587     {
00588       T val;
00589       bool filled;
00590 
00591       condition cond;
00592       mutex m;
00593 
00594       box(const box &other);
00595       box &operator=(const box &other);
00596     public:
00598       box()
00599         :filled(false)
00600       {
00601       }
00602 
00604       box(const T &_val)
00605         :val(_val), filled(true)
00606       {
00607       }
00608 
00612       T take();
00613 
00617       void put(const T &t);
00618 
00625       bool try_take(T &out);
00626 
00635       bool try_put(const T &t);
00636 
00640       bool timed_take(T &out, const timespec &until);
00641 
00645       bool timed_put(const T &t, const timespec &until);
00646 
00651       template<typename Mutator>
00652       void update(const Mutator &m);
00653     };
00654 
00659     template<>
00660     class box<void>
00661     {
00662       bool filled;
00663       mutex m;
00664       condition cond;
00665     public:
00666       box()
00667         :filled(false)
00668       {
00669       }
00670 
00671       box(bool _filled)
00672         :filled(_filled)
00673       {
00674       }
00675 
00676       void take();
00677 
00678       void put();
00679 
00680       bool try_take();
00681       bool try_put();
00682 
00683       bool timed_take(const timespec &until);
00684       bool timed_put(const timespec &until);
00685 
00686       template<typename Mutator>
00687       void update(const Mutator &m)
00688       {
00689         take();
00690         try
00691           {
00692             m();
00693           }
00694         catch(...)
00695           {
00696             put();
00697             throw;
00698           }
00699 
00700         put();
00701       }
00702     };
00703 
00705     struct bool_ref_pred
00706     {
00707       const bool &b;
00708     public:
00709       bool_ref_pred(const bool &_b)
00710         :b(_b)
00711       {
00712       }
00713 
00714       bool operator()() const
00715       {
00716         return b;
00717       }
00718     };
00719 
00721     struct not_bool_ref_pred
00722     {
00723       const bool &b;
00724     public:
00725       not_bool_ref_pred(const bool &_b)
00726         :b(_b)
00727       {
00728       }
00729 
00730       bool operator()() const
00731       {
00732         return !b;
00733       }
00734     };
00735 
00736     template<typename T>
00737     inline
00738     T box<T>::take()
00739     {
00740       mutex::lock l(m);
00741 
00742       cond.wait(l, bool_ref_pred(filled));
00743 
00744       filled = false;
00745 
00746       // Interesting question: does l get released before or after the
00747       // copy?  To be safe, I explicitly copy before I return.
00748       T rval = val;
00749       return rval;
00750     }
00751 
00752     inline
00753     void box<void>::take()
00754     {
00755       mutex::lock l(m);
00756       cond.wait(l, bool_ref_pred(filled));
00757       filled = false;
00758     }
00759 
00760     template<typename T>
00761     inline
00762     bool box<T>::try_take(T &out)
00763     {
00764       mutex::lock l(m);
00765 
00766       if(filled)
00767         {
00768           filled = false;
00769           out = val;
00770           return true;
00771         }
00772       else
00773         return false;
00774     }
00775 
00776     inline
00777     bool box<void>::try_take()
00778     {
00779       mutex::lock l(m);
00780 
00781       if(filled)
00782         {
00783           filled = false;
00784           return true;
00785         }
00786       else
00787         return false;
00788     }
00789 
00790     template<typename T>
00791     inline
00792     bool box<T>::timed_take(T &out, const timespec &until)
00793     {
00794       mutex::lock l(m);
00795 
00796       if(cond.timed_wait(l, until, bool_ref_pred(filled)))
00797         {
00798           filled = false;
00799           out = val;
00800           return true;
00801         }
00802       else
00803         return false;
00804     }
00805 
00806     inline
00807     bool box<void>::timed_take(const timespec &until)
00808     {
00809       mutex::lock l(m);
00810 
00811       if(cond.timed_wait(l, until, bool_ref_pred(filled)))
00812         {
00813           filled = false;
00814           return true;
00815         }
00816       else
00817         return false;
00818     }
00819 
00820     template<typename T>
00821     inline
00822     void box<T>::put(const T &new_val)
00823     {
00824       mutex::lock l(m);
00825 
00826       cond.wait(l, not_bool_ref_pred(filled));
00827 
00828       filled = true;
00829       val = new_val;
00830       cond.wake_one();
00831     }
00832 
00833     inline
00834     void box<void>::put()
00835     {
00836       mutex::lock l(m);
00837 
00838       cond.wait(l, not_bool_ref_pred(filled));
00839 
00840       filled = true;
00841       cond.wake_one();
00842     }
00843 
00844     template<typename T>
00845     inline
00846     bool box<T>::try_put(const T &new_val)
00847     {
00848       mutex::lock l(m);
00849 
00850       if(!filled)
00851         {
00852           filled = true;
00853           val = new_val;
00854           cond.wake_one();
00855           return true;
00856         }
00857       else
00858         return false;
00859     }
00860 
00861     inline
00862     bool box<void>::try_put()
00863     {
00864       mutex::lock l(m);
00865 
00866       if(!filled)
00867         {
00868           filled = true;
00869           cond.wake_one();
00870           return true;
00871         }
00872       else
00873         return false;
00874     }
00875 
00876     template<typename T>
00877     inline
00878     bool box<T>::timed_put(const T &new_val, const timespec &until)
00879     {
00880       mutex::lock l(m);
00881 
00882       if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
00883         {
00884           filled = true;
00885           val = new_val;
00886           cond.wake_one();
00887           return true;
00888         }
00889       else
00890         return false;
00891     }
00892 
00893     inline
00894     bool box<void>::timed_put(const timespec &until)
00895     {
00896       mutex::lock l(m);
00897 
00898       if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
00899         {
00900           filled = true;
00901           cond.wake_one();
00902           return true;
00903         }
00904       else
00905         return false;
00906     }
00907 
00908     template<typename T>
00909     template<typename Mutator>
00910     inline
00911     void box<T>::update(const Mutator &m)
00912     {
00913       mutex::lock l(m);
00914 
00915       cond.wait(l, bool_ref_pred(filled));
00916 
00917       T new_val = m(val);
00918 
00919       val = new_val;
00920       cond.wake_one();
00921     }
00922 
00923     // A ptr_box is like a box, but it wraps a pointer to its internal
00924     // object.  When a filled ptr_box is destroyed, it deletes the
00925     // pointer that it contains.
00926     template<typename T>
00927     class ptr_box
00928     {
00929       box<T *> b;
00930     public:
00931       ptr_box()
00932       {
00933       }
00934 
00935       ptr_box(const T *val)
00936         :b(val)
00937       {
00938       }
00939 
00940       ~ptr_box()
00941       {
00942         T *x;
00943 
00944         if(b.try_get(x))
00945           delete x;
00946       }
00947 
00948       T *take()
00949       {
00950         return b.take();
00951       }
00952 
00953       bool try_take(const T * &out)
00954       {
00955         return b.try_take(out);
00956       }
00957 
00958       bool timed_take(const T * &out, const timespec &until)
00959       {
00960         return b.timed_take(out);
00961       }
00962 
00963       void put(const T *in)
00964       {
00965         b.put(in);
00966       }
00967 
00968       bool try_put(const T *in)
00969       {
00970         return b.try_put(in);
00971       }
00972 
00973       bool timed_put(const T *in, const timespec &until)
00974       {
00975         return b.timed_put(in, until);
00976       }
00977     };
00978 
00979     // A utility that proxies for noncopyable thread bootstrap
00980     // objects.  The only requirement is that the pointer passed
00981     // to the constructor must not be destroyed until the thread
00982     // completes.
00983     template<typename F>
00984     class bootstrap_proxy
00985     {
00986       F *f;
00987     public:
00988       bootstrap_proxy(F *_f)
00989         : f(_f)
00990       {
00991       }
00992 
00993       void operator()() const
00994       {
00995         (*f)();
00996       }
00997     };
00998 
00999     template<typename F>
01000     bootstrap_proxy<F> make_bootstrap_proxy(F *f)
01001     {
01002       return bootstrap_proxy<F>(f);
01003     }
01004   }
01005 }
01006 
01007 #endif // THREADS_H
01008 

Generated on Sat Jun 12 14:51:02 2010 for cwidget by  doxygen 1.5.6