cwidget  0.5.18
threads.h
1 // threads.h -*-c++-*-
2 //
3 // Copyright (C) 2005-2009 Daniel Burrows
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of
8 // the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; see the file COPYING. If not, write to
17 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 // Boston, MA 02111-1307, USA.
19 //
20 // A simple thread wrapper library. I'm not using the existing ones
21 // in order to keep aptitude's dependency count low (as long as I
22 // don't need too much out of it, this should be fairly
23 // simple..right?). The API was inspired by that of boost::threads.
24 
25 #ifndef THREADS_H
26 #define THREADS_H
27 
28 #include <errno.h>
29 #include <cwidget/generic/util/exception.h>
30 
31 namespace cwidget
32 {
38  namespace threads
39  {
42  {
43  };
44 
50  {
51  int errnum;
52  public:
53  ThreadCreateException(int error)
54  : errnum(error)
55  {
56  }
57 
58  int get_errnum() const { return errnum; }
59 
60  std::string errmsg() const;
61  };
62 
65  {
66  std::string reason;
67 
68  int errnum;
69  public:
70  ThreadJoinException(const int error);
71 
72  int get_errnum() const { return errnum; }
73  std::string errmsg() const;
74  };
75 
82  {
83  public:
84  std::string errmsg() const;
85  };
86 
89  {
90  public:
91  std::string errmsg() const;
92  };
93 
100  class thread
101  {
102  pthread_t tid;
103  bool joined;
104 
105  thread(const thread &other);
106  thread &operator=(const thread &other);
107 
108 
109 
110  template<typename F>
111  static void *bootstrap(void *p)
112  {
113  F thunk(*((F *) p));
114 
115  delete ((F *) p);
116 
117  thunk();
118 
119  return 0;
120  }
121 
122  public:
131  class attr
132  {
133  pthread_attr_t attrs;
134 
135  friend class thread;
136  public:
137  attr()
138  {
139  pthread_attr_init(&attrs);
140  }
141 
142  ~attr()
143  {
144  pthread_attr_destroy(&attrs);
145  }
146  };
147 
158  template<typename F>
159  thread(const F &thunk, const attr &a = attr())
160  :joined(false)
161  {
162  // Create a thunk on the heap to pass to the new thread.
163  F *tmp = new F(thunk);
164 
165  if(pthread_create(&tid, &a.attrs, &thread::bootstrap<F>, tmp) != 0)
166  {
167  int errnum = errno;
168 
169  delete tmp;
170 
171  throw ThreadCreateException(errnum);
172  }
173  }
174 
175  ~thread()
176  {
177  if(!joined)
178  pthread_detach(tid);
179  }
180 
182  void join()
183  {
184  int rval = pthread_join(tid, NULL);
185 
186  if(rval != 0)
187  throw ThreadJoinException(rval);
188  else
189  joined = true;
190  }
191 
193  void cancel()
194  {
195  pthread_cancel(tid);
196  }
197  };
198 
211  template<typename F>
213  {
214  F &f;
215  public:
221  :f(_f)
222  {
223  }
224 
226  void operator()()
227  {
228  f();
229  }
230  };
231 
232  class condition;
233 
234  // The mutex abstraction
235  class mutex
236  {
237  public:
238  class lock;
239  class try_lock;
240 
241  private:
242  pthread_mutex_t m;
243 
244  friend class lock;
245  friend class try_lock;
246 
247  // Conditions need to look inside mutexes and locks to find the
248  // real mutex object so the underlying thread library can do an
249  // atomic unlock-and-wait.
250  friend class condition;
251 
252  mutex(const mutex &other);
253  mutex &operator=(const mutex &other);
254  public:
256  class attr
257  {
258  pthread_mutexattr_t attrs;
259 
260  friend class mutex;
261 
262  public:
263  attr()
264  {
265  pthread_mutexattr_init(&attrs);
266  }
267 
268  attr(int kind)
269  {
270  pthread_mutexattr_init(&attrs);
271  pthread_mutexattr_settype(&attrs, kind);
272  }
273 
274  ~attr()
275  {
276  pthread_mutexattr_destroy(&attrs);
277  }
278 
279  int settype(int kind)
280  {
281  return pthread_mutexattr_settype(&attrs, kind);
282  }
283 
284  int gettype()
285  {
286  int rval;
287  pthread_mutexattr_gettype(&attrs, &rval);
288  return rval;
289  }
290  };
291 
296  class lock
297  {
298  mutex &parent;
299 
300  bool locked;
301 
302  friend class condition;
303 
304  lock(const lock &other);
305  lock &operator=(const lock &other);
306  public:
307  lock(mutex &_parent)
308  :parent(_parent), locked(false)
309  {
310  acquire();
311  }
312 
314  void acquire()
315  {
316  if(locked)
317  throw DoubleLockException();
318 
319  pthread_mutex_lock(&parent.m);
320  locked = true;
321  }
322 
324  void release()
325  {
326  pthread_mutex_unlock(&parent.m);
327  locked = false;
328  }
329 
330  bool get_locked() const
331  {
332  return locked;
333  }
334 
335  ~lock()
336  {
337  if(locked)
338  pthread_mutex_unlock(&parent.m);
339  }
340  };
341 
343  class try_lock
344  {
345  mutex &parent;
346 
347  bool locked;
348 
349  friend class condition;
350 
351  try_lock(const try_lock &other);
352  try_lock &operator=(const try_lock &other);
353  public:
354  try_lock(mutex &_parent)
355  :parent(_parent)
356  {
357  acquire();
358  }
359 
360  ~try_lock()
361  {
362  if(locked)
363  pthread_mutex_unlock(&parent.m);
364  }
365 
366  void acquire()
367  {
368  if(locked)
369  throw DoubleLockException();
370 
371  locked = pthread_mutex_trylock(&parent.m);
372  }
373 
374  void release()
375  {
376  pthread_mutex_unlock(&parent.m);
377  locked = false;
378  }
379 
380  bool get_locked() const
381  {
382  return locked;
383  }
384  };
385 
386  mutex()
387  {
388  pthread_mutex_init(&m, NULL);
389  }
390 
391  mutex(const attr &a)
392  {
393  pthread_mutex_init(&m, &a.attrs);
394  }
395 
396  ~mutex()
397  {
398  pthread_mutex_destroy(&m);
399  }
400  };
401 
405  class recursive_mutex : public mutex
406  {
407  public:
409  :mutex(attr(PTHREAD_MUTEX_RECURSIVE))
410  {
411  }
412  };
413 
418  class condition
419  {
420  pthread_cond_t cond;
421  public:
422  condition()
423  {
424  pthread_cond_init(&cond, NULL);
425  }
426 
427  ~condition()
428  {
429  // Wakey wakey
430  pthread_cond_broadcast(&cond);
431  pthread_cond_destroy(&cond);
432  }
433 
434  void wake_one()
435  {
436  pthread_cond_signal(&cond);
437  }
438 
439  void wake_all()
440  {
441  pthread_cond_broadcast(&cond);
442  }
443 
450  template<typename Lock>
451  void wait(const Lock &l)
452  {
453  if(!l.get_locked())
455 
456  pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock, &l.parent.m);
457  pthread_cond_wait(&cond, &l.parent.m);
458  pthread_cleanup_pop(0);
459  }
460 
469  template<typename Lock, typename Pred>
470  void wait(const Lock &l, Pred p)
471  {
472  if(!l.get_locked())
474 
475  while(!p())
476  wait(l);
477  }
478 
494  template<typename Lock>
495  bool timed_wait(const Lock &l, const timespec &until)
496  {
497  if(!l.get_locked())
499 
500  int rval;
501 
502  pthread_cleanup_push((void(*)(void *))&pthread_mutex_unlock, &l.parent.m);
503  while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR)
504  ;
505  pthread_cleanup_pop(0);
506 
507  return rval != ETIMEDOUT;
508  }
509 
520  template<typename Lock, typename Pred>
521  bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
522  {
523  if(!l.get_locked())
525 
526  while(!p())
527  {
528  if(!timed_wait(l, until))
529  return false;
530  }
531 
532  return true;
533  }
534  };
535 
547  template<typename T>
548  class box
549  {
550  T val;
551  bool filled;
552 
553  condition cond;
554  mutex m;
555 
556  box(const box &other);
557  box &operator=(const box &other);
558  public:
560  box()
561  :filled(false)
562  {
563  }
564 
566  box(const T &_val)
567  :val(_val), filled(true)
568  {
569  }
570 
574  T take();
575 
579  void put(const T &t);
580 
587  bool try_take(T &out);
588 
597  bool try_put(const T &t);
598 
602  bool timed_take(T &out, const timespec &until);
603 
607  bool timed_put(const T &t, const timespec &until);
608 
613  template<typename Mutator>
614  void update(const Mutator &m);
615  };
616 
621  template<>
622  class box<void>
623  {
624  bool filled;
625  mutex m;
626  condition cond;
627  public:
628  box()
629  :filled(false)
630  {
631  }
632 
633  box(bool _filled)
634  :filled(_filled)
635  {
636  }
637 
638  void take();
639 
640  void put();
641 
642  bool try_take();
643  bool try_put();
644 
645  bool timed_take(const timespec &until);
646  bool timed_put(const timespec &until);
647 
648  template<typename Mutator>
649  void update(const Mutator &m)
650  {
651  take();
652  try
653  {
654  m();
655  }
656  catch(...)
657  {
658  put();
659  throw;
660  }
661 
662  put();
663  }
664  };
665 
668  {
669  const bool &b;
670  public:
671  bool_ref_pred(const bool &_b)
672  :b(_b)
673  {
674  }
675 
676  bool operator()() const
677  {
678  return b;
679  }
680  };
681 
684  {
685  const bool &b;
686  public:
687  not_bool_ref_pred(const bool &_b)
688  :b(_b)
689  {
690  }
691 
692  bool operator()() const
693  {
694  return !b;
695  }
696  };
697 
698  template<typename T>
699  inline
701  {
702  mutex::lock l(m);
703 
704  cond.wait(l, bool_ref_pred(filled));
705 
706  filled = false;
707 
708  // Interesting question: does l get released before or after the
709  // copy? To be safe, I explicitly copy before I return.
710  T rval = val;
711  return rval;
712  }
713 
714  inline
715  void box<void>::take()
716  {
717  mutex::lock l(m);
718  cond.wait(l, bool_ref_pred(filled));
719  filled = false;
720  }
721 
722  template<typename T>
723  inline
724  bool box<T>::try_take(T &out)
725  {
726  mutex::lock l(m);
727 
728  if(filled)
729  {
730  filled = false;
731  out = val;
732  return true;
733  }
734  else
735  return false;
736  }
737 
738  inline
739  bool box<void>::try_take()
740  {
741  mutex::lock l(m);
742 
743  if(filled)
744  {
745  filled = false;
746  return true;
747  }
748  else
749  return false;
750  }
751 
752  template<typename T>
753  inline
754  bool box<T>::timed_take(T &out, const timespec &until)
755  {
756  mutex::lock l(m);
757 
758  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
759  {
760  filled = false;
761  out = val;
762  return true;
763  }
764  else
765  return false;
766  }
767 
768  inline
769  bool box<void>::timed_take(const timespec &until)
770  {
771  mutex::lock l(m);
772 
773  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
774  {
775  filled = false;
776  return true;
777  }
778  else
779  return false;
780  }
781 
782  template<typename T>
783  inline
784  void box<T>::put(const T &new_val)
785  {
786  mutex::lock l(m);
787 
788  cond.wait(l, not_bool_ref_pred(filled));
789 
790  filled = true;
791  val = new_val;
792  cond.wake_one();
793  }
794 
795  inline
796  void box<void>::put()
797  {
798  mutex::lock l(m);
799 
800  cond.wait(l, not_bool_ref_pred(filled));
801 
802  filled = true;
803  cond.wake_one();
804  }
805 
806  template<typename T>
807  inline
808  bool box<T>::try_put(const T &new_val)
809  {
810  mutex::lock l(m);
811 
812  if(!filled)
813  {
814  filled = true;
815  val = new_val;
816  cond.wake_one();
817  return true;
818  }
819  else
820  return false;
821  }
822 
823  inline
824  bool box<void>::try_put()
825  {
826  mutex::lock l(m);
827 
828  if(!filled)
829  {
830  filled = true;
831  cond.wake_one();
832  return true;
833  }
834  else
835  return false;
836  }
837 
838  template<typename T>
839  inline
840  bool box<T>::timed_put(const T &new_val, const timespec &until)
841  {
842  mutex::lock l(m);
843 
844  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
845  {
846  filled = true;
847  val = new_val;
848  cond.wake_one();
849  return true;
850  }
851  else
852  return false;
853  }
854 
855  inline
856  bool box<void>::timed_put(const timespec &until)
857  {
858  mutex::lock l(m);
859 
860  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
861  {
862  filled = true;
863  cond.wake_one();
864  return true;
865  }
866  else
867  return false;
868  }
869 
870  template<typename T>
871  template<typename Mutator>
872  inline
873  void box<T>::update(const Mutator &m)
874  {
875  mutex::lock l(m);
876 
877  cond.wait(l, bool_ref_pred(filled));
878 
879  T new_val = m(val);
880 
881  val = new_val;
882  cond.wake_one();
883  }
884 
885  // A utility that proxies for noncopyable thread bootstrap
886  // objects. The only requirement is that the pointer passed
887  // to the constructor must not be destroyed until the thread
888  // completes.
889  template<typename F>
891  {
892  F *f;
893  public:
894  bootstrap_proxy(F *_f)
895  : f(_f)
896  {
897  }
898 
899  void operator()() const
900  {
901  (*f)();
902  }
903  };
904 
905  template<typename F>
906  bootstrap_proxy<F> make_bootstrap_proxy(F *f)
907  {
908  return bootstrap_proxy<F>(f);
909  }
910  }
911 }
912 
913 #endif // THREADS_H
914 
cwidget::threads::ThreadCreateException
Thrown when thread creation fails; according to pthread_create(3), this only occurs if there aren't e...
Definition: threads.h:49
cwidget::threads::thread::thread
thread(const F &thunk, const attr &a=attr())
Create a new thread.
Definition: threads.h:159
cwidget::threads::condition::wait
void wait(const Lock &l, Pred p)
Wait until the given predicate returns true.
Definition: threads.h:470
cwidget::threads::mutex::lock::release
void release()
Unlock the associated mutex.
Definition: threads.h:324
cwidget::threads::mutex::lock
Represents a lock on a mutex.
Definition: threads.h:296
cwidget::threads::box::try_take
bool try_take(T &out)
If there is a value in the box, retrieve it immediately; otherwise do nothing.
Definition: threads.h:724
cwidget::threads::bootstrap_proxy
Definition: threads.h:890
cwidget::threads::box::take
T take()
Retrieve the current value of this box.
Definition: threads.h:700
cwidget::threads::ThreadJoinException
Thrown when thread::join fails.
Definition: threads.h:64
cwidget::threads::box
A higher-level abstraction borrowed from Concurrent Haskell, which borrowed it from another language ...
Definition: threads.h:548
cwidget::threads::recursive_mutex
A mutex that is initialized to be recursive.
Definition: threads.h:405
cwidget::threads::not_bool_ref_pred
Internal helper struct.
Definition: threads.h:683
cwidget::threads::thread::attr
Stores the attributes with which a thread is to be created.
Definition: threads.h:131
cwidget::threads::mutex::attr
A mutex attributes object.
Definition: threads.h:256
cwidget::threads::box::put
void put(const T &t)
Fill this box with a value.
Definition: threads.h:784
cwidget::util::Exception
Definition: exception.h:37
cwidget::threads::box::timed_take
bool timed_take(T &out, const timespec &until)
As try_take(), but wait for the given amount of time before giving up.
Definition: threads.h:754
cwidget::threads::thread::cancel
void cancel()
Cancel this thread.
Definition: threads.h:193
cwidget::threads::ConditionNotLockedException
Thrown when the mutex being used to wait on a condition is not locked.
Definition: threads.h:81
cwidget::threads::condition
A abstraction over conditions.
Definition: threads.h:418
cwidget::threads::thread::join
void join()
Wait for this thread to finish.
Definition: threads.h:182
cwidget::threads::box::try_put
bool try_put(const T &t)
If the box is empty, place a value in it; otherwise, do nothing.
Definition: threads.h:808
cwidget::threads::ThreadException
The base class for all thread-related exceptions.
Definition: threads.h:41
cwidget::threads::mutex
Definition: threads.h:235
cwidget::threads::condition::timed_wait
bool timed_wait(const Lock &l, const timespec &until)
Wait until either the condition is signalled or until the given time.
Definition: threads.h:495
cwidget::threads::box::box
box(const T &_val)
Create a box containing the given value.
Definition: threads.h:566
cwidget::threads::condition::wait
void wait(const Lock &l)
Wait with the given guard (should be a lock type that is a friend of this condition object).
Definition: threads.h:451
cwidget::threads::noncopy_bootstrap::operator()
void operator()()
Invoke F::operator() on the wrapped object.
Definition: threads.h:226
cwidget::threads::bool_ref_pred
Internal helper struct.
Definition: threads.h:667
cwidget::threads::thread
A system thread.
Definition: threads.h:100
cwidget
The namespace containing everything defined by cwidget.
Definition: columnify.cc:27
cwidget::threads::DoubleLockException
Thrown when an error-checking mutex is locked twice.
Definition: threads.h:88
cwidget::threads::box::box
box()
Create an empty box.
Definition: threads.h:560
cwidget::threads::noncopy_bootstrap
Wrap noncopyable objects to bootstrap threads.
Definition: threads.h:212
cwidget::threads::noncopy_bootstrap::noncopy_bootstrap
noncopy_bootstrap(F &_f)
Create a noncopyable bootstrap wrapper.
Definition: threads.h:220
cwidget::threads::condition::timed_wait
bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
Wait either until the condition is signalled while the given predicate is true or until the given tim...
Definition: threads.h:521
cwidget::threads::box::timed_put
bool timed_put(const T &t, const timespec &until)
As try_put(), but wait for the given amount of time before giving up.
Definition: threads.h:840
cwidget::threads::mutex::try_lock
Represents a non-blocking lock on a mutex.
Definition: threads.h:343
cwidget::threads::box::update
void update(const Mutator &m)
Atomically modify the contents of the box; if an exception is thrown by the given function object,...
Definition: threads.h:873
cwidget::threads::mutex::lock::acquire
void acquire()
Lock the associated mutex.
Definition: threads.h:314