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
32namespace 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
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
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
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:
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
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