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 <pthread.h>
30 #include <cwidget/generic/util/exception.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  ~attr()
144  {
145  pthread_attr_destroy(&attrs);
146  }
147  };
148 
159  template<typename F>
160  thread(const F &thunk, const attr &a = attr())
161  :joined(false)
162  {
163  // Create a thunk on the heap to pass to the new thread.
164  F *tmp = new F(thunk);
165 
166  if(pthread_create(&tid, &a.attrs, &thread::bootstrap<F>, tmp) != 0)
167  {
168  int errnum = errno;
169 
170  delete tmp;
171 
172  throw ThreadCreateException(errnum);
173  }
174  }
175 
176  ~thread()
177  {
178  if(!joined)
179  pthread_detach(tid);
180  }
181 
183  void join()
184  {
185  int rval = pthread_join(tid, NULL);
186 
187  if(rval != 0)
188  throw ThreadJoinException(rval);
189  else
190  joined = true;
191  }
192 
194  void cancel()
195  {
196  pthread_cancel(tid);
197  }
198  };
199 
212  template<typename F>
214  {
215  F &f;
216  public:
222  :f(_f)
223  {
224  }
225 
227  void operator()()
228  {
229  f();
230  }
231  };
232 
233  class condition;
234 
235  // The mutex abstraction
236  class mutex
237  {
238  public:
239  class lock;
240  class try_lock;
241 
242  private:
243  pthread_mutex_t m;
244 
245  friend class lock;
246  friend class try_lock;
247 
248  // Conditions need to look inside mutexes and locks to find the
249  // real mutex object so the underlying thread library can do an
250  // atomic unlock-and-wait.
251  friend class condition;
252 
253  mutex(const mutex &other);
254  mutex &operator=(const mutex &other);
255  public:
257  class attr
258  {
259  pthread_mutexattr_t attrs;
260 
261  friend class mutex;
262 
263  public:
264  attr()
265  {
266  pthread_mutexattr_init(&attrs);
267  }
268 
269  attr(int kind)
270  {
271  pthread_mutexattr_init(&attrs);
272  pthread_mutexattr_settype(&attrs, kind);
273  }
274 
275  ~attr()
276  {
277  pthread_mutexattr_destroy(&attrs);
278  }
279 
280  int settype(int kind)
281  {
282  return pthread_mutexattr_settype(&attrs, kind);
283  }
284 
285  int gettype()
286  {
287  int rval;
288  pthread_mutexattr_gettype(&attrs, &rval);
289  return rval;
290  }
291  };
292 
297  class lock
298  {
299  mutex &parent;
300 
301  bool locked;
302 
303  friend class condition;
304 
305  lock(const lock &other);
306  lock &operator=(const lock &other);
307  public:
308  lock(mutex &_parent)
309  :parent(_parent), locked(false)
310  {
311  acquire();
312  }
313 
315  void acquire()
316  {
317  if(locked)
318  throw DoubleLockException();
319 
320  pthread_mutex_lock(&parent.m);
321  locked = true;
322  }
323 
325  void release()
326  {
327  pthread_mutex_unlock(&parent.m);
328  locked = false;
329  }
330 
331  bool get_locked() const
332  {
333  return locked;
334  }
335 
336  ~lock()
337  {
338  if(locked)
339  pthread_mutex_unlock(&parent.m);
340  }
341  };
342 
344  class try_lock
345  {
346  mutex &parent;
347 
348  bool locked;
349 
350  friend class condition;
351 
352  try_lock(const try_lock &other);
353  try_lock &operator=(const try_lock &other);
354  public:
355  try_lock(mutex &_parent)
356  :parent(_parent)
357  {
358  acquire();
359  }
360 
361  ~try_lock()
362  {
363  if(locked)
364  pthread_mutex_unlock(&parent.m);
365  }
366 
367  void acquire()
368  {
369  if(locked)
370  throw DoubleLockException();
371 
372  locked = pthread_mutex_trylock(&parent.m);
373  }
374 
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 
387  mutex()
388  {
389  pthread_mutex_init(&m, NULL);
390  }
391 
392  mutex(const attr &a)
393  {
394  pthread_mutex_init(&m, &a.attrs);
395  }
396 
397  ~mutex()
398  {
399  pthread_mutex_destroy(&m);
400  }
401  };
402 
406  class recursive_mutex : public mutex
407  {
408  public:
410  :mutex(attr(PTHREAD_MUTEX_RECURSIVE))
411  {
412  }
413  };
414 
419  class condition
420  {
421  pthread_cond_t cond;
422  public:
423  condition()
424  {
425  pthread_cond_init(&cond, NULL);
426  }
427 
428  ~condition()
429  {
430  // Wakey wakey
431  pthread_cond_broadcast(&cond);
432  pthread_cond_destroy(&cond);
433  }
434 
435  void wake_one()
436  {
437  pthread_cond_signal(&cond);
438  }
439 
440  void wake_all()
441  {
442  pthread_cond_broadcast(&cond);
443  }
444 
451  template<typename Lock>
452  void wait(const Lock &l)
453  {
454  if(!l.get_locked())
456 
457  pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock, &l.parent.m);
458  pthread_cond_wait(&cond, &l.parent.m);
459  pthread_cleanup_pop(0);
460  }
461 
470  template<typename Lock, typename Pred>
471  void wait(const Lock &l, Pred p)
472  {
473  if(!l.get_locked())
475 
476  while(!p())
477  wait(l);
478  }
479 
495  template<typename Lock>
496  bool timed_wait(const Lock &l, const timespec &until)
497  {
498  if(!l.get_locked())
500 
501  int rval;
502 
503  pthread_cleanup_push((void(*)(void *))&pthread_mutex_unlock, &l.parent.m);
504  while((rval = pthread_cond_timedwait(&cond, &l.parent.m, &until)) == EINTR)
505  ;
506  pthread_cleanup_pop(0);
507 
508  return rval != ETIMEDOUT;
509  }
510 
521  template<typename Lock, typename Pred>
522  bool timed_wait(const Lock &l, const timespec &until, const Pred &p)
523  {
524  if(!l.get_locked())
526 
527  while(!p())
528  {
529  if(!timed_wait(l, until))
530  return false;
531  }
532 
533  return true;
534  }
535  };
536 
548  template<typename T>
549  class box
550  {
551  T val;
552  bool filled;
553 
554  condition cond;
555  mutex m;
556 
557  box(const box &other);
558  box &operator=(const box &other);
559  public:
561  box()
562  :filled(false)
563  {
564  }
565 
567  box(const T &_val)
568  :val(_val), filled(true)
569  {
570  }
571 
575  T take();
576 
580  void put(const T &t);
581 
588  bool try_take(T &out);
589 
598  bool try_put(const T &t);
599 
603  bool timed_take(T &out, const timespec &until);
604 
608  bool timed_put(const T &t, const timespec &until);
609 
614  template<typename Mutator>
615  void update(const Mutator &m);
616  };
617 
622  template<>
623  class box<void>
624  {
625  bool filled;
626  mutex m;
627  condition cond;
628  public:
629  box()
630  :filled(false)
631  {
632  }
633 
634  box(bool _filled)
635  :filled(_filled)
636  {
637  }
638 
639  void take();
640 
641  void put();
642 
643  bool try_take();
644  bool try_put();
645 
646  bool timed_take(const timespec &until);
647  bool timed_put(const timespec &until);
648 
649  template<typename Mutator>
650  void update(const Mutator &m)
651  {
652  take();
653  try
654  {
655  m();
656  }
657  catch(...)
658  {
659  put();
660  throw;
661  }
662 
663  put();
664  }
665  };
666 
669  {
670  const bool &b;
671  public:
672  bool_ref_pred(const bool &_b)
673  :b(_b)
674  {
675  }
676 
677  bool operator()() const
678  {
679  return b;
680  }
681  };
682 
685  {
686  const bool &b;
687  public:
688  not_bool_ref_pred(const bool &_b)
689  :b(_b)
690  {
691  }
692 
693  bool operator()() const
694  {
695  return !b;
696  }
697  };
698 
699  template<typename T>
700  inline
702  {
703  mutex::lock l(m);
704 
705  cond.wait(l, bool_ref_pred(filled));
706 
707  filled = false;
708 
709  // Interesting question: does l get released before or after the
710  // copy? To be safe, I explicitly copy before I return.
711  T rval = val;
712  return rval;
713  }
714 
715  inline
716  void box<void>::take()
717  {
718  mutex::lock l(m);
719  cond.wait(l, bool_ref_pred(filled));
720  filled = false;
721  }
722 
723  template<typename T>
724  inline
725  bool box<T>::try_take(T &out)
726  {
727  mutex::lock l(m);
728 
729  if(filled)
730  {
731  filled = false;
732  out = val;
733  return true;
734  }
735  else
736  return false;
737  }
738 
739  inline
740  bool box<void>::try_take()
741  {
742  mutex::lock l(m);
743 
744  if(filled)
745  {
746  filled = false;
747  return true;
748  }
749  else
750  return false;
751  }
752 
753  template<typename T>
754  inline
755  bool box<T>::timed_take(T &out, const timespec &until)
756  {
757  mutex::lock l(m);
758 
759  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
760  {
761  filled = false;
762  out = val;
763  return true;
764  }
765  else
766  return false;
767  }
768 
769  inline
770  bool box<void>::timed_take(const timespec &until)
771  {
772  mutex::lock l(m);
773 
774  if(cond.timed_wait(l, until, bool_ref_pred(filled)))
775  {
776  filled = false;
777  return true;
778  }
779  else
780  return false;
781  }
782 
783  template<typename T>
784  inline
785  void box<T>::put(const T &new_val)
786  {
787  mutex::lock l(m);
788 
789  cond.wait(l, not_bool_ref_pred(filled));
790 
791  filled = true;
792  val = new_val;
793  cond.wake_one();
794  }
795 
796  inline
797  void box<void>::put()
798  {
799  mutex::lock l(m);
800 
801  cond.wait(l, not_bool_ref_pred(filled));
802 
803  filled = true;
804  cond.wake_one();
805  }
806 
807  template<typename T>
808  inline
809  bool box<T>::try_put(const T &new_val)
810  {
811  mutex::lock l(m);
812 
813  if(!filled)
814  {
815  filled = true;
816  val = new_val;
817  cond.wake_one();
818  return true;
819  }
820  else
821  return false;
822  }
823 
824  inline
825  bool box<void>::try_put()
826  {
827  mutex::lock l(m);
828 
829  if(!filled)
830  {
831  filled = true;
832  cond.wake_one();
833  return true;
834  }
835  else
836  return false;
837  }
838 
839  template<typename T>
840  inline
841  bool box<T>::timed_put(const T &new_val, const timespec &until)
842  {
843  mutex::lock l(m);
844 
845  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
846  {
847  filled = true;
848  val = new_val;
849  cond.wake_one();
850  return true;
851  }
852  else
853  return false;
854  }
855 
856  inline
857  bool box<void>::timed_put(const timespec &until)
858  {
859  mutex::lock l(m);
860 
861  if(cond.timed_wait(l, until, not_bool_ref_pred(filled)))
862  {
863  filled = true;
864  cond.wake_one();
865  return true;
866  }
867  else
868  return false;
869  }
870 
871  template<typename T>
872  template<typename Mutator>
873  inline
874  void box<T>::update(const Mutator &m)
875  {
876  mutex::lock l(m);
877 
878  cond.wait(l, bool_ref_pred(filled));
879 
880  T new_val = m(val);
881 
882  val = new_val;
883  cond.wake_one();
884  }
885 
886  // A utility that proxies for noncopyable thread bootstrap
887  // objects. The only requirement is that the pointer passed
888  // to the constructor must not be destroyed until the thread
889  // completes.
890  template<typename F>
892  {
893  F *f;
894  public:
895  bootstrap_proxy(F *_f)
896  : f(_f)
897  {
898  }
899 
900  void operator()() const
901  {
902  (*f)();
903  }
904  };
905 
906  template<typename F>
907  bootstrap_proxy<F> make_bootstrap_proxy(F *f)
908  {
909  return bootstrap_proxy<F>(f);
910  }
911  }
912 }
913 
914 #endif // THREADS_H
915 
Thrown when the mutex being used to wait on a condition is not locked.
Definition: threads.h:83
Thrown when an error-checking mutex is locked twice.
Definition: threads.h:90
Thrown when thread creation fails; according to pthread_create(3), this only occurs if there aren't e...
Definition: threads.h:51
The base class for all thread-related exceptions.
Definition: threads.h:43
Thrown when thread::join fails.
Definition: threads.h:66
Definition: threads.h:892
A higher-level abstraction borrowed from Concurrent Haskell, which borrowed it from another language ...
Definition: threads.h:550
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:755
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:874
box()
Create an empty box.
Definition: threads.h:561
box(const T &_val)
Create a box containing the given value.
Definition: threads.h:567
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:841
bool try_put(const T &t)
If the box is empty, place a value in it; otherwise, do nothing.
Definition: threads.h:809
void put(const T &t)
Fill this box with a value.
Definition: threads.h:785
bool try_take(T &out)
If there is a value in the box, retrieve it immediately; otherwise do nothing.
Definition: threads.h:725
T take()
Retrieve the current value of this box.
Definition: threads.h:701
A abstraction over conditions.
Definition: threads.h:420
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:452
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:522
void wait(const Lock &l, Pred p)
Wait until the given predicate returns true.
Definition: threads.h:471
bool timed_wait(const Lock &l, const timespec &until)
Wait until either the condition is signalled or until the given time.
Definition: threads.h:496
A mutex attributes object.
Definition: threads.h:258
Represents a lock on a mutex.
Definition: threads.h:298
void release()
Unlock the associated mutex.
Definition: threads.h:325
void acquire()
Lock the associated mutex.
Definition: threads.h:315
Represents a non-blocking lock on a mutex.
Definition: threads.h:345
Definition: threads.h:237
A mutex that is initialized to be recursive.
Definition: threads.h:407
Stores the attributes with which a thread is to be created.
Definition: threads.h:133
A system thread.
Definition: threads.h:102
void cancel()
Cancel this thread.
Definition: threads.h:194
thread(const F &thunk, const attr &a=attr())
Create a new thread.
Definition: threads.h:160
void join()
Wait for this thread to finish.
Definition: threads.h:183
Definition: exception.h:38
The namespace containing everything defined by cwidget.
Definition: columnify.cc:28
Internal helper struct.
Definition: threads.h:669
Wrap noncopyable objects to bootstrap threads.
Definition: threads.h:214
void operator()()
Invoke F::operator() on the wrapped object.
Definition: threads.h:227
noncopy_bootstrap(F &_f)
Create a noncopyable bootstrap wrapper.
Definition: threads.h:221
Internal helper struct.
Definition: threads.h:685