threads.h

00001 // threads.h                                              -*-c++-*-
00002 //
00003 //   Copyright (C) 2005-2007 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 
00485       template<typename Lock>
00486       void wait(const Lock &l)
00487       {
00488         if(!l.get_locked())
00489           throw ConditionNotLockedException();
00490 
00491         pthread_cond_wait(&cond, &l.parent.m);
00492       }
00493 
00495       template<typename Lock, typename Pred>
00496       void wait(const Lock &l, Pred p)
00497       {
00498         if(!l.get_locked())
00499           throw ConditionNotLockedException();
00500 
00501         while(!p())
00502           wait(l);
00503       }
00504 
00514       template<typename Lock>
00515       bool timed_wait(const Lock &l, const timespec &until)
00516       {
00517         if(!l.get_locked())
00518           throw ConditionNotLockedException();
00519 
00520         int rval;
00521 
00522         // Ignore EINTR for the time being.
00523         while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR)
00524           ;
00525 
00526         return rval != ETIMEDOUT;
00527       }
00528 
00532       template<typename Lock, typename Pred>
00533       bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
00534       {
00535         if(!l.get_locked())
00536           throw ConditionNotLockedException();
00537 
00538         while(!p())
00539           {
00540             if(!timed_wait(l, until))
00541               return false;
00542           }
00543 
00544         return true;
00545       }
00546     };
00547 
00559     template<typename T>
00560     class box
00561     {
00562       T val;
00563       bool filled;
00564 
00565       condition cond;
00566       mutex m;
00567 
00568       box(const box &other);
00569       box &operator=(const box &other);
00570     public:
00572       box()
00573         :filled(false)
00574       {
00575       }
00576 
00578       box(const T &_val)
00579         :val(_val), filled(true)
00580       {
00581       }
00582 
00586       T take();
00587 
00591       void put(const T &t);
00592 
00599       bool try_take(T &out);
00600 
00609       bool try_put(const T &t);
00610 
00614       bool timed_take(T &out, const timespec &until);
00615 
00619       bool timed_put(const T &t, const timespec &until);
00620 
00625       template<typename Mutator>
00626       void update(const Mutator &m);
00627     };
00628 
00633     template<>
00634     class box<void>
00635     {
00636       bool filled;
00637       mutex m;
00638       condition cond;
00639     public:
00640       box()
00641         :filled(false)
00642       {
00643       }
00644 
00645       box(bool _filled)
00646         :filled(_filled)
00647       {
00648       }
00649 
00650       void take();
00651 
00652       void put();
00653 
00654       bool try_take();
00655       bool try_put();
00656 
00657       bool timed_take(const timespec &until);
00658       bool timed_put(const timespec &until);
00659 
00660       template<typename Mutator>
00661       void update(const Mutator &m)
00662       {
00663         take();
00664         try
00665           {
00666             m();
00667           }
00668         catch(...)
00669           {
00670             put();
00671             throw;
00672           }
00673 
00674         put();
00675       }
00676     };
00677 
00679     struct bool_ref_pred
00680     {
00681       const bool &b;
00682     public:
00683       bool_ref_pred(const bool &_b)
00684         :b(_b)
00685       {
00686       }
00687 
00688       bool operator()() const
00689       {
00690         return b;
00691       }
00692     };
00693 
00695     struct not_bool_ref_pred
00696     {
00697       const bool &b;
00698     public:
00699       not_bool_ref_pred(const bool &_b)
00700         :b(_b)
00701       {
00702       }
00703 
00704       bool operator()() const
00705       {
00706         return !b;
00707       }
00708     };
00709 
00710     template<typename T>
00711     inline
00712     T box<T>::take()
00713     {
00714       mutex::lock l(m);
00715 
00716       cond.wait(l, bool_ref_pred(filled));
00717 
00718       filled = false;
00719 
00720       // Interesting question: does l get released before or after the
00721       // copy?  To be safe, I explicitly copy before I return.
00722       T rval = val;
00723       return rval;
00724     }
00725 
00726     inline
00727     void box<void>::take()
00728     {
00729       mutex::lock l(m);
00730       cond.wait(l, bool_ref_pred(filled));
00731       filled = false;
00732     }
00733 
00734     template<typename T>
00735     inline
00736     bool box<T>::try_take(T &out)
00737     {
00738       mutex::lock l(m);
00739 
00740       if(filled)
00741         {
00742           filled = false;
00743           out = val;
00744           return true;
00745         }
00746       else
00747         return false;
00748     }
00749 
00750     inline
00751     bool box<void>::try_take()
00752     {
00753       mutex::lock l(m);
00754 
00755       if(filled)
00756         {
00757           filled = false;
00758           return true;
00759         }
00760       else
00761         return false;
00762     }
00763 
00764     template<typename T>
00765     inline
00766     bool box<T>::timed_take(T &out, const timespec &until)
00767     {
00768       mutex::lock l(m);
00769 
00770       if(cond.timed_wait(l, until, bool_ref_pred(filled)))
00771         {
00772           filled = false;
00773           out = val;
00774           return true;
00775         }
00776       else
00777         return false;
00778     }
00779 
00780     inline
00781     bool box<void>::timed_take(const timespec &until)
00782     {
00783       mutex::lock l(m);
00784 
00785       if(cond.timed_wait(l, until, bool_ref_pred(filled)))
00786         {
00787           filled = false;
00788           return true;
00789         }
00790       else
00791         return false;
00792     }
00793 
00794     template<typename T>
00795     inline
00796     void box<T>::put(const T &new_val)
00797     {
00798       mutex::lock l(m);
00799 
00800       cond.wait(l, not_bool_ref_pred(filled));
00801 
00802       filled = true;
00803       val = new_val;
00804       cond.wake_one();
00805     }
00806 
00807     inline
00808     void box<void>::put()
00809     {
00810       mutex::lock l(m);
00811 
00812       cond.wait(l, not_bool_ref_pred(filled));
00813 
00814       filled = true;
00815       cond.wake_one();
00816     }
00817 
00818     template<typename T>
00819     inline
00820     bool box<T>::try_put(const T &new_val)
00821     {
00822       mutex::lock l(m);
00823 
00824       if(!filled)
00825         {
00826           filled = true;
00827           val = new_val;
00828           cond.wake_one();
00829           return true;
00830         }
00831       else
00832         return false;
00833     }
00834 
00835     inline
00836     bool box<void>::try_put()
00837     {
00838       mutex::lock l(m);
00839 
00840       if(!filled)
00841         {
00842           filled = true;
00843           cond.wake_one();
00844           return true;
00845         }
00846       else
00847         return false;
00848     }
00849 
00850     template<typename T>
00851     inline
00852     bool box<T>::timed_put(const T &new_val, const timespec &until)
00853     {
00854       mutex::lock l(m);
00855 
00856       if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
00857         {
00858           filled = true;
00859           val = new_val;
00860           cond.wake_one();
00861           return true;
00862         }
00863       else
00864         return false;
00865     }
00866 
00867     inline
00868     bool box<void>::timed_put(const timespec &until)
00869     {
00870       mutex::lock l(m);
00871 
00872       if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
00873         {
00874           filled = true;
00875           cond.wake_one();
00876           return true;
00877         }
00878       else
00879         return false;
00880     }
00881 
00882     template<typename T>
00883     template<typename Mutator>
00884     inline
00885     void box<T>::update(const Mutator &m)
00886     {
00887       mutex::lock l(m);
00888 
00889       cond.wait(l, bool_ref_pred(filled));
00890 
00891       T new_val = m(val);
00892 
00893       val = new_val;
00894       cond.wake_one();
00895     }
00896 
00897     // A ptr_box is like a box, but it wraps a pointer to its internal
00898     // object.  When a filled ptr_box is destroyed, it deletes the
00899     // pointer that it contains.
00900     template<typename T>
00901     class ptr_box
00902     {
00903       box<T *> b;
00904     public:
00905       ptr_box()
00906       {
00907       }
00908 
00909       ptr_box(const T *val)
00910         :b(val)
00911       {
00912       }
00913 
00914       ~ptr_box()
00915       {
00916         T *x;
00917 
00918         if(b.try_get(x))
00919           delete x;
00920       }
00921 
00922       T *take()
00923       {
00924         return b.take();
00925       }
00926 
00927       bool try_take(const T * &out)
00928       {
00929         return b.try_take(out);
00930       }
00931 
00932       bool timed_take(const T * &out, const timespec &until)
00933       {
00934         return b.timed_take(out);
00935       }
00936 
00937       void put(const T *in)
00938       {
00939         b.put(in);
00940       }
00941 
00942       bool try_put(const T *in)
00943       {
00944         return b.try_put(in);
00945       }
00946 
00947       bool timed_put(const T *in, const timespec &until)
00948       {
00949         return b.timed_put(in, until);
00950       }
00951     };
00952 
00953     // A utility that proxies for noncopyable thread bootstrap
00954     // objects.  The only requirement is that the pointer passed
00955     // to the constructor must not be destroyed until the thread
00956     // completes.
00957     template<typename F>
00958     class bootstrap_proxy
00959     {
00960       F *f;
00961     public:
00962       bootstrap_proxy(F *_f)
00963         : f(_f)
00964       {
00965       }
00966 
00967       void operator()() const
00968       {
00969         (*f)();
00970       }
00971     };
00972 
00973     template<typename F>
00974     bootstrap_proxy<F> make_bootstrap_proxy(F *f)
00975     {
00976       return bootstrap_proxy<F>(f);
00977     }
00978   }
00979 }
00980 
00981 #endif // THREADS_H
00982 

Generated on Fri Nov 16 03:33:05 2007 for cwidget by  doxygen 1.5.1