Fawkes API Fawkes Development Version
barrier.cpp
1
2/***************************************************************************
3 * barrier.cpp - Barrier
4 *
5 * Created: Thu Sep 15 00:33:13 2006
6 * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22 */
23
24#include <core/exception.h>
25#include <core/threading/barrier.h>
26
27#include <pthread.h>
28#include <unistd.h>
29
30// cf. http://people.redhat.com/drepper/posix-option-groups.html
31#if defined(_POSIX_BARRIERS) && (_POSIX_BARRIERS - 200112L) >= 0
32# define USE_POSIX_BARRIERS
33#else
34# undef USE_POSIX_BARRIERS
35# include <core/threading/mutex.h>
36# include <core/threading/wait_condition.h>
37#endif
38
39namespace fawkes {
40
41/// @cond INTERNALS
42class BarrierData
43{
44public:
45#ifdef USE_POSIX_BARRIERS
46 pthread_barrier_t barrier;
47#else
48 BarrierData() : threads_left(0), mutex(), waitcond(&mutex)
49 {
50 }
51
52 unsigned int threads_left;
53 Mutex mutex;
54 WaitCondition waitcond;
55#endif
56};
57/// @endcond
58
59/** @class Barrier core/threading/barrier.h
60 * A barrier is a synchronization tool which blocks until a given number of
61 * threads have reached the barrier.
62 *
63 * Consider you have three threads all doing work. In the end you have to merge
64 * their results. For this you need a point where all threads are finished. Of
65 * course you don't want another thread waiting and periodically checking if
66 * the other threads are finished, that will loose time - processing time and
67 * the time that passed between the threads being finished and the time when
68 * the controller checked again.
69 *
70 * For this problem we have a barrier. You initialize the barrier with the
71 * number of threads to wait for and then wait in all threads at a specific
72 * point. After this one of the threads can merge the result (and the others
73 * may wait for another "go-on barrier".
74 *
75 * The code in the thread would be
76 * @code
77 * virtual void run()
78 * {
79 * forever {
80 * do_something();
81 * result_barrier->wait();
82 * if ( master_thread ) {
83 * merge_results();
84 * }
85 * goon_barrier->wait();
86 * }
87 * }
88 * @endcode
89 *
90 * The threads would all get a reference to the same barriers and one would
91 * have master thread to be true.
92 *
93 * After the barrier has been passed (count threads waited) the barrier is
94 * reset automatically and can be re-used with the same amount of barrier
95 * waiters.
96 *
97 * The implementation of Barrier takes into account that PThread barriers are
98 * optional in POSIX. If barriers are not available they are emulated using a
99 * wait condition. Note however that on systems that do have both, barriers and
100 * wait conditions it has been observed that in a typical barrier scenario the
101 * POSIX barriers perform much better in many situations than a wait condition
102 * (processes tend to be rescheduled immediately if a barrier is reached, while
103 * with a wait condition they are rescheduled with lower priority and thus they
104 * delay may increase on a loaded system). Because of this on systems without
105 * real POSIX barriers the performance may be not as good as is expected.
106 *
107 * @ingroup Threading
108 * @author Tim Niemueller
109 */
110
111/** Constructor
112 * @param count the number of threads to wait for
113 */
114Barrier::Barrier(unsigned int count)
115{
116 _count = count;
117 if (_count == 0) {
118 throw Exception("Barrier count must be at least 1");
119 }
120 barrier_data = new BarrierData();
121#ifdef USE_POSIX_BARRIERS
122 pthread_barrier_init(&(barrier_data->barrier), NULL, _count);
123#else
124 barrier_data->threads_left = _count;
125#endif
126}
127
128/** Protected Constructor.
129 * Does not initialize any internal structures other than setting them to 0.
130 */
132{
133 _count = 0;
134 barrier_data = NULL;
135}
136
137/** Destructor */
139{
140 if (barrier_data) {
141#ifdef USE_POSIX_BARRIERS
142 pthread_barrier_destroy(&(barrier_data->barrier));
143#endif
144 delete barrier_data;
145 }
146}
147
148/** Wait for other threads.
149 * This method will block until as many threads have called wait as you have
150 * given count to the constructor.
151 */
152void
154{
155#ifdef USE_POSIX_BARRIERS
156 pthread_barrier_wait(&(barrier_data->barrier));
157#else
158 barrier_data->mutex.lock();
159
160 if (--barrier_data->threads_left == 0) {
161 barrier_data->threads_left = _count;
162 barrier_data->mutex.unlock();
163 barrier_data->waitcond.wake_all();
164 } else {
165 barrier_data->waitcond.wait();
166 barrier_data->mutex.unlock();
167 }
168
169#endif
170}
171
172/** Get number of threads this barrier will wait for.
173 * @return number of threads this barrier will wait for.
174 */
175unsigned int
177{
178 return _count;
179}
180
181} // end namespace fawkes
virtual ~Barrier()
Destructor.
Definition: barrier.cpp:138
unsigned int _count
Number of threads that are expected to wait for the barrier.
Definition: barrier.h:47
virtual void wait()
Wait for other threads.
Definition: barrier.cpp:153
unsigned int count()
Get number of threads this barrier will wait for.
Definition: barrier.cpp:176
Barrier()
Protected Constructor.
Definition: barrier.cpp:131
Base class for exceptions in Fawkes.
Definition: exception.h:36
Fawkes library namespace.