cwidget  0.5.17
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 #include <pthread.h>
31 
32 namespace cwidget
33 {
39  namespace threads
40  {
43  {
44  };
45 
51  {
52  int errnum;
53  public:
54  ThreadCreateException(int error)
55  : errnum(error)
56  {
57  }
58 
59  int get_errnum() const { return errnum; }
60 
61  std::string errmsg() const;
62  };
63 
66  {
67  std::string reason;
68 
69  int errnum;
70  public:
71  ThreadJoinException(const int error);
72 
73  int get_errnum() const { return errnum; }
74  std::string errmsg() const;
75  };
76 
83  {
84  public:
85  std::string errmsg() const;
86  };
87 
90  {
91  public:
92  std::string errmsg() const;
93  };
94 
101  class thread
102  {
103  pthread_t tid;
104  bool joined;
105 
106  thread(const thread &other);
107  thread &operator=(const thread &other);
108 
109 
110 
111  template<typename F>
112  static void *bootstrap(void *p)
113  {
114  F thunk(*((F *) p));
115 
116  delete ((F *) p);
117 
118  thunk();
119 
120  return 0;
121  }
122 
123  public:
132  class attr
133  {
134  pthread_attr_t attrs;
135 
136  friend class thread;
137  public:
138  attr()
139  {
140  pthread_attr_init(&attrs);
141  }
142 
143  // All attributes except detach state can be manipulated (detach
144  // state is left at PTHREAD_CREATE_JOINABLE).
145 
146  void set_inherit_sched(int i)
147  {
148  pthread_attr_setinheritsched(&attrs, i);
149  }
150 
151  int get_inherit_sched() const
152  {
153  int rval;
154  pthread_attr_getinheritsched(&attrs, &rval);
155  return rval;
156  }
157 
158  void set_sched_param(const sched_param &sp)
159  {
160  pthread_attr_setschedparam(&attrs, &sp);
161  }
162 
163  sched_param get_sched_param() const
164  {
165  sched_param rval;
166  pthread_attr_getschedparam(&attrs, &rval);
167  return rval;
168  }
169 
170  void set_sched_policy(int p)
171  {
172  pthread_attr_setschedpolicy(&attrs, p);
173  }
174 
175  int get_sched_policy() const
176  {
177  int rval;
178  pthread_attr_getschedpolicy(&attrs, &rval);
179  return rval;
180  }
181 
182  void set_scope(int p)
183  {
184  pthread_attr_setscope(&attrs, p);
185  }
186 
187  int get_scope() const
188  {
189  int rval;
190  pthread_attr_getscope(&attrs, &rval);
191  return rval;
192  }
193 
194  ~attr()
195  {
196  pthread_attr_destroy(&attrs);
197  }
198  };
199 
210  template<typename F>
211  thread(const F &thunk, const attr &a = attr())
212  :joined(false)
213  {
214  // Create a thunk on the heap to pass to the new thread.
215  F *tmp = new F(thunk);
216 
217  if(pthread_create(&tid, &a.attrs, &thread::bootstrap<F>, tmp) != 0)
218  {
219  int errnum = errno;
220 
221  delete tmp;
222 
223  throw ThreadCreateException(errnum);
224  }
225  }
226 
227  ~thread()
228  {
229  if(!joined)
230  pthread_detach(tid);
231  }
232 
234  void join()
235  {
236  int rval = pthread_join(tid, NULL);
237 
238  if(rval != 0)
239  throw ThreadJoinException(rval);
240  else
241  joined = true;
242  }
243 
245  void cancel()
246  {
247  pthread_cancel(tid);
248  }
249  };
250 
263  template<typename F>
265  {
266  F &f;
267  public:
273  :f(_f)
274  {
275  }
276 
278  void operator()()
279  {
280  f();
281  }
282  };
283 
284  class condition;
285 
286  // The mutex abstraction
287  class mutex
288  {
289  public:
290  class lock;
291  class try_lock;
292 
293  private:
294  pthread_mutex_t m;
295 
296  friend class lock;
297  friend class try_lock;
298 
299  // Conditions need to look inside mutexes and locks to find the
300  // real mutex object so the underlying thread library can do an
301  // atomic unlock-and-wait.
302  friend class condition;
303 
304  mutex(const mutex &other);
305  mutex &operator=(const mutex &other);
306  public:
308  class attr
309  {
310  pthread_mutexattr_t attrs;
311 
312  friend class mutex;
313 
314  public:
315  attr()
316  {
317  pthread_mutexattr_init(&attrs);
318  }
319 
320  attr(int kind)
321  {
322  pthread_mutexattr_init(&attrs);
323  pthread_mutexattr_settype(&attrs, kind);
324  }
325 
326  ~attr()
327  {
328  pthread_mutexattr_destroy(&attrs);
329  }
330 
331  int settype(int kind)
332  {
333  return pthread_mutexattr_settype(&attrs, kind);
334  }
335 
336  int gettype()
337  {
338  int rval;
339  pthread_mutexattr_gettype(&attrs, &rval);
340  return rval;
341  }
342  };
343 
348  class lock
349  {
350  mutex &parent;
351 
352  bool locked;
353 
354  friend class condition;
355 
356  lock(const lock &other);
357  lock &operator=(const lock &other);
358  public:
359  lock(mutex &_parent)
360  :parent(_parent), locked(false)
361  {
362  acquire();
363  }
364 
366  void acquire()
367  {
368  if(locked)
369  throw DoubleLockException();
370 
371  pthread_mutex_lock(&parent.m);
372  locked = true;
373  }
374 
376  void release()
377  {
378  pthread_mutex_unlock(&parent.m);
379  locked = false;
380  }
381 
382  bool get_locked() const
383  {
384  return locked;
385  }
386 
387  ~lock()
388  {
389  if(locked)
390  pthread_mutex_unlock(&parent.m);
391  }
392  };
393 
395  class try_lock
396  {
397  mutex &parent;
398 
399  bool locked;
400 
401  friend class condition;
402 
403  try_lock(const try_lock &other);
404  try_lock &operator=(const try_lock &other);
405  public:
406  try_lock(mutex &_parent)
407  :parent(_parent)
408  {
409  acquire();
410  }
411 
412  ~try_lock()
413  {
414  if(locked)
415  pthread_mutex_unlock(&parent.m);
416  }
417 
418  void acquire()
419  {
420  if(locked)
421  throw DoubleLockException();
422 
423  locked = pthread_mutex_trylock(&parent.m);
424  }
425 
426  void release()
427  {
428  pthread_mutex_unlock(&parent.m);
429  locked = false;
430  }
431 
432  bool get_locked() const
433  {
434  return locked;
435  }
436  };
437 
438  mutex()
439  {
440  pthread_mutex_init(&m, NULL);
441  }
442 
443  mutex(const attr &a)
444  {
445  pthread_mutex_init(&m, &a.attrs);
446  }
447 
448  ~mutex()
449  {
450  pthread_mutex_destroy(&m);
451  }
452  };
453 
457  class recursive_mutex : public mutex
458  {
459  public:
461  :mutex(attr(PTHREAD_MUTEX_RECURSIVE))
462  {
463  }
464  };
465 
470  class condition
471  {
472  pthread_cond_t cond;
473  public:
474  condition()
475  {
476  pthread_cond_init(&cond, NULL);
477  }
478 
479  ~condition()
480  {
481  // Wakey wakey
482  pthread_cond_broadcast(&cond);
483  pthread_cond_destroy(&cond);
484  }
485 
486  void wake_one()
487  {
488  pthread_cond_signal(&cond);
489  }
490 
491  void wake_all()
492  {
493  pthread_cond_broadcast(&cond);
494  }
495 
502  template<typename Lock>
503  void wait(const Lock &l)
504  {
505  if(!l.get_locked())
507 
508  pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock, &l.parent.m);
509  pthread_cond_wait(&cond, &l.parent.m);
510  pthread_cleanup_pop(0);
511  }
512 
521  template<typename Lock, typename Pred>
522  void wait(const Lock &l, Pred p)
523  {
524  if(!l.get_locked())
526 
527  while(!p())
528  wait(l);
529  }
530 
546  template<typename Lock>
547  bool timed_wait(const Lock &l, const timespec &until)
548  {
549  if(!l.get_locked())
551 
552  int rval;
553 
554  pthread_cleanup_push((void(*)(void *))&pthread_mutex_unlock, &l.parent.m);
555  while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR)
556  ;
557  pthread_cleanup_pop(0);
558 
559  return rval != ETIMEDOUT;
560  }
561 
572  template<typename Lock, typename Pred>
573  bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
574  {
575  if(!l.get_locked())
577 
578  while(!p())
579  {
580  if(!timed_wait(l, until))
581  return false;
582  }
583 
584  return true;
585  }
586  };
587 
599  template<typename T>
600  class box
601  {
602  T val;
603  bool filled;
604 
605  condition cond;
606  mutex m;
607 
608  box(const box &other);
609  box &operator=(const box &other);
610  public:
612  box()
613  :filled(false)
614  {
615  }
616 
618  box(const T &_val)
619  :val(_val), filled(true)
620  {
621  }
622 
626  T take();
627 
631  void put(const T &t);
632 
639  bool try_take(T &out);
640 
649  bool try_put(const T &t);
650 
654  bool timed_take(T &out, const timespec &until);
655 
659  bool timed_put(const T &t, const timespec &until);
660 
665  template<typename Mutator>
666  void update(const Mutator &m);
667  };
668 
673  template<>
674  class box<void>
675  {
676  bool filled;
677  mutex m;
678  condition cond;
679  public:
680  box()
681  :filled(false)
682  {
683  }
684 
685  box(bool _filled)
686  :filled(_filled)
687  {
688  }
689 
690  void take();
691 
692  void put();
693 
694  bool try_take();
695  bool try_put();
696 
697  bool timed_take(const timespec &until);
698  bool timed_put(const timespec &until);
699 
700  template<typename Mutator>
701  void update(const Mutator &m)
702  {
703  take();
704  try
705  {
706  m();
707  }
708  catch(...)
709  {
710  put();
711  throw;
712  }
713 
714  put();
715  }
716  };
717 
720  {
721  const bool &b;
722  public:
723  bool_ref_pred(const bool &_b)
724  :b(_b)
725  {
726  }
727 
728  bool operator()() const
729  {
730  return b;
731  }
732  };
733 
736  {
737  const bool &b;
738  public:
739  not_bool_ref_pred(const bool &_b)
740  :b(_b)
741  {
742  }
743 
744  bool operator()() const
745  {
746  return !b;
747  }
748  };
749 
750  template<typename T>
751  inline
753  {
754  mutex::lock l(m);
755 
756  cond.wait(l, bool_ref_pred(filled));
757 
758  filled = false;
759 
760  // Interesting question: does l get released before or after the
761  // copy? To be safe, I explicitly copy before I return.
762  T rval = val;
763  return rval;
764  }
765 
766  inline
767  void box<void>::take()
768  {
769  mutex::lock l(m);
770  cond.wait(l, bool_ref_pred(filled));
771  filled = false;
772  }
773 
774  template<typename T>
775  inline
776  bool box<T>::try_take(T &out)
777  {
778  mutex::lock l(m);
779 
780  if(filled)
781  {
782  filled = false;
783  out = val;
784  return true;
785  }
786  else
787  return false;
788  }
789 
790  inline
791  bool box<void>::try_take()
792  {
793  mutex::lock l(m);
794 
795  if(filled)
796  {
797  filled = false;
798  return true;
799  }
800  else
801  return false;
802  }
803 
804  template<typename T>
805  inline
806  bool box<T>::timed_take(T &out, const timespec &until)
807  {
808  mutex::lock l(m);
809 
810  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
811  {
812  filled = false;
813  out = val;
814  return true;
815  }
816  else
817  return false;
818  }
819 
820  inline
821  bool box<void>::timed_take(const timespec &until)
822  {
823  mutex::lock l(m);
824 
825  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
826  {
827  filled = false;
828  return true;
829  }
830  else
831  return false;
832  }
833 
834  template<typename T>
835  inline
836  void box<T>::put(const T &new_val)
837  {
838  mutex::lock l(m);
839 
840  cond.wait(l, not_bool_ref_pred(filled));
841 
842  filled = true;
843  val = new_val;
844  cond.wake_one();
845  }
846 
847  inline
848  void box<void>::put()
849  {
850  mutex::lock l(m);
851 
852  cond.wait(l, not_bool_ref_pred(filled));
853 
854  filled = true;
855  cond.wake_one();
856  }
857 
858  template<typename T>
859  inline
860  bool box<T>::try_put(const T &new_val)
861  {
862  mutex::lock l(m);
863 
864  if(!filled)
865  {
866  filled = true;
867  val = new_val;
868  cond.wake_one();
869  return true;
870  }
871  else
872  return false;
873  }
874 
875  inline
876  bool box<void>::try_put()
877  {
878  mutex::lock l(m);
879 
880  if(!filled)
881  {
882  filled = true;
883  cond.wake_one();
884  return true;
885  }
886  else
887  return false;
888  }
889 
890  template<typename T>
891  inline
892  bool box<T>::timed_put(const T &new_val, const timespec &until)
893  {
894  mutex::lock l(m);
895 
896  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
897  {
898  filled = true;
899  val = new_val;
900  cond.wake_one();
901  return true;
902  }
903  else
904  return false;
905  }
906 
907  inline
908  bool box<void>::timed_put(const timespec &until)
909  {
910  mutex::lock l(m);
911 
912  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
913  {
914  filled = true;
915  cond.wake_one();
916  return true;
917  }
918  else
919  return false;
920  }
921 
922  template<typename T>
923  template<typename Mutator>
924  inline
925  void box<T>::update(const Mutator &m)
926  {
927  mutex::lock l(m);
928 
929  cond.wait(l, bool_ref_pred(filled));
930 
931  T new_val = m(val);
932 
933  val = new_val;
934  cond.wake_one();
935  }
936 
937  // A ptr_box is like a box, but it wraps a pointer to its internal
938  // object. When a filled ptr_box is destroyed, it deletes the
939  // pointer that it contains.
940  template<typename T>
941  class ptr_box
942  {
943  box<T *> b;
944  public:
945  ptr_box()
946  {
947  }
948 
949  ptr_box(const T *val)
950  :b(val)
951  {
952  }
953 
954  ~ptr_box()
955  {
956  T *x;
957 
958  if(b.try_get(x))
959  delete x;
960  }
961 
962  T *take()
963  {
964  return b.take();
965  }
966 
967  bool try_take(const T * &out)
968  {
969  return b.try_take(out);
970  }
971 
972  bool timed_take(const T * &out, const timespec &until)
973  {
974  return b.timed_take(out);
975  }
976 
977  void put(const T *in)
978  {
979  b.put(in);
980  }
981 
982  bool try_put(const T *in)
983  {
984  return b.try_put(in);
985  }
986 
987  bool timed_put(const T *in, const timespec &until)
988  {
989  return b.timed_put(in, until);
990  }
991  };
992 
993  // A utility that proxies for noncopyable thread bootstrap
994  // objects. The only requirement is that the pointer passed
995  // to the constructor must not be destroyed until the thread
996  // completes.
997  template<typename F>
999  {
1000  F *f;
1001  public:
1002  bootstrap_proxy(F *_f)
1003  : f(_f)
1004  {
1005  }
1006 
1007  void operator()() const
1008  {
1009  (*f)();
1010  }
1011  };
1012 
1013  template<typename F>
1014  bootstrap_proxy<F> make_bootstrap_proxy(F *f)
1015  {
1016  return bootstrap_proxy<F>(f);
1017  }
1018  }
1019 }
1020 
1021 #endif // THREADS_H
1022 
The base class for all thread-related exceptions.
Definition: threads.h:42
bool timed_wait(const Lock &l, const timespec &until)
Wait until either the condition is signalled or until the given time.
Definition: threads.h:547
Thrown when thread::join fails.
Definition: threads.h:65
Thrown when an error-checking mutex is locked twice.
Definition: threads.h:89
A system thread.
Definition: threads.h:101
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:925
bool try_put(const T &t)
If the box is empty, place a value in it; otherwise, do nothing.
Definition: threads.h:860
Wrap noncopyable objects to bootstrap threads.
Definition: threads.h:264
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:892
bool try_take(T &out)
If there is a value in the box, retrieve it immediately; otherwise do nothing.
Definition: threads.h:776
noncopy_bootstrap(F &_f)
Create a noncopyable bootstrap wrapper.
Definition: threads.h:272
void wait(const Lock &l, Pred p)
Wait until the given predicate returns true.
Definition: threads.h:522
The namespace containing everything defined by cwidget.
Definition: columnify.cc:26
Thrown when thread creation fails; according to pthread_create(3), this only occurs if there aren't e...
Definition: threads.h:50
void release()
Unlock the associated mutex.
Definition: threads.h:376
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:806
Definition: exception.h:37
Represents a non-blocking lock on a mutex.
Definition: threads.h:395
A mutex that is initialized to be recursive.
Definition: threads.h:457
A higher-level abstraction borrowed from Concurrent Haskell, which borrowed it from another language ...
Definition: threads.h:600
Represents a lock on a mutex.
Definition: threads.h:348
A mutex attributes object.
Definition: threads.h:308
void operator()()
Invoke F::operator() on the wrapped object.
Definition: threads.h:278
thread(const F &thunk, const attr &a=attr())
Create a new thread.
Definition: threads.h:211
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:573
box()
Create an empty box.
Definition: threads.h:612
Definition: threads.h:941
T take()
Retrieve the current value of this box.
Definition: threads.h:752
void put(const T &t)
Fill this box with a value.
Definition: threads.h:836
void acquire()
Lock the associated mutex.
Definition: threads.h:366
Definition: threads.h:287
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:503
Thrown when the mutex being used to wait on a condition is not locked.
Definition: threads.h:82
Definition: threads.h:998
Internal helper struct.
Definition: threads.h:719
Internal helper struct.
Definition: threads.h:735
void cancel()
Cancel this thread.
Definition: threads.h:245
Stores the attributes with which a thread is to be created.
Definition: threads.h:132
void join()
Wait for this thread to finish.
Definition: threads.h:234
box(const T &_val)
Create a box containing the given value.
Definition: threads.h:618
A abstraction over conditions.
Definition: threads.h:470