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