libstdc++
concurrence.h
Go to the documentation of this file.
1 // Support for concurrent programing -*- C++ -*-
2 
3 // Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
4 // Free Software Foundation, Inc.
5 //
6 // This file is part of the GNU ISO C++ Library. This library is free
7 // software; you can redistribute it and/or modify it under the
8 // terms of the GNU General Public License as published by the
9 // Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 
17 // Under Section 7 of GPL version 3, you are granted additional
18 // permissions described in the GCC Runtime Library Exception, version
19 // 3.1, as published by the Free Software Foundation.
20 
21 // You should have received a copy of the GNU General Public License and
22 // a copy of the GCC Runtime Library Exception along with this program;
23 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 // <http://www.gnu.org/licenses/>.
25 
26 /** @file ext/concurrence.h
27  * This file is a GNU extension to the Standard C++ Library.
28  */
29 
30 #ifndef _CONCURRENCE_H
31 #define _CONCURRENCE_H 1
32 
33 #pragma GCC system_header
34 
35 #include <exception>
36 #include <bits/gthr.h>
37 #include <bits/functexcept.h>
38 #include <bits/cpp_type_traits.h>
39 #include <ext/type_traits.h>
40 
41 namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
42 {
43 _GLIBCXX_BEGIN_NAMESPACE_VERSION
44 
45  // Available locking policies:
46  // _S_single single-threaded code that doesn't need to be locked.
47  // _S_mutex multi-threaded code that requires additional support
48  // from gthr.h or abstraction layers in concurrence.h.
49  // _S_atomic multi-threaded code using atomic operations.
50  enum _Lock_policy { _S_single, _S_mutex, _S_atomic };
51 
52  // Compile time constant that indicates prefered locking policy in
53  // the current configuration.
54  static const _Lock_policy __default_lock_policy =
55 #ifdef __GTHREADS
56 #if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) \
57  && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4))
58  _S_atomic;
59 #else
60  _S_mutex;
61 #endif
62 #else
63  _S_single;
64 #endif
65 
66  // NB: As this is used in libsupc++, need to only depend on
67  // exception. No stdexception classes, no use of std::string.
68  class __concurrence_lock_error : public std::exception
69  {
70  public:
71  virtual char const*
72  what() const throw()
73  { return "__gnu_cxx::__concurrence_lock_error"; }
74  };
75 
76  class __concurrence_unlock_error : public std::exception
77  {
78  public:
79  virtual char const*
80  what() const throw()
81  { return "__gnu_cxx::__concurrence_unlock_error"; }
82  };
83 
84  class __concurrence_broadcast_error : public std::exception
85  {
86  public:
87  virtual char const*
88  what() const throw()
89  { return "__gnu_cxx::__concurrence_broadcast_error"; }
90  };
91 
92  class __concurrence_wait_error : public std::exception
93  {
94  public:
95  virtual char const*
96  what() const throw()
97  { return "__gnu_cxx::__concurrence_wait_error"; }
98  };
99 
100  // Substitute for concurrence_error object in the case of -fno-exceptions.
101  inline void
102  __throw_concurrence_lock_error()
103  {
104 #if __EXCEPTIONS
105  throw __concurrence_lock_error();
106 #else
107  __builtin_abort();
108 #endif
109  }
110 
111  inline void
112  __throw_concurrence_unlock_error()
113  {
114 #if __EXCEPTIONS
115  throw __concurrence_unlock_error();
116 #else
117  __builtin_abort();
118 #endif
119  }
120 
121 #ifdef __GTHREAD_HAS_COND
122  inline void
123  __throw_concurrence_broadcast_error()
124  {
125 #if __EXCEPTIONS
126  throw __concurrence_broadcast_error();
127 #else
128  __builtin_abort();
129 #endif
130  }
131 
132  inline void
133  __throw_concurrence_wait_error()
134  {
135 #if __EXCEPTIONS
136  throw __concurrence_wait_error();
137 #else
138  __builtin_abort();
139 #endif
140  }
141 #endif
142 
143  template<typename _Tp>
144  static inline void
145  __copy_gthr_type(_Tp& __to, const _Tp& __from)
146  {
147 #if defined __GXX_EXPERIMENTAL_CXX0X__ \
148  && defined _GLIBCXX_GTHREADS_NO_COPY_ASSIGN_IN_CXX11
149  __builtin_memcpy(&__to, &__from, sizeof(__to));
150 #else
151  __to = __from;
152 #endif
153  }
154 
155  class __mutex
156  {
157  private:
158  __gthread_mutex_t _M_mutex;
159 
160  __mutex(const __mutex&);
161  __mutex& operator=(const __mutex&);
162 
163  public:
164  __mutex()
165  {
166 #if __GTHREADS
167  if (__gthread_active_p())
168  {
169 #if defined __GTHREAD_MUTEX_INIT
170  __gthread_mutex_t __tmp = __GTHREAD_MUTEX_INIT;
171  __copy_gthr_type(_M_mutex, __tmp);
172 #else
173  __GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex);
174 #endif
175  }
176 #endif
177  }
178 
179 #if __GTHREADS && ! defined __GTHREAD_MUTEX_INIT
180  ~__mutex()
181  {
182  if (__gthread_active_p())
183  __gthread_mutex_destroy(&_M_mutex);
184  }
185 #endif
186 
187  void lock()
188  {
189 #if __GTHREADS
190  if (__gthread_active_p())
191  {
192  if (__gthread_mutex_lock(&_M_mutex) != 0)
193  __throw_concurrence_lock_error();
194  }
195 #endif
196  }
197 
198  void unlock()
199  {
200 #if __GTHREADS
201  if (__gthread_active_p())
202  {
203  if (__gthread_mutex_unlock(&_M_mutex) != 0)
204  __throw_concurrence_unlock_error();
205  }
206 #endif
207  }
208 
209  __gthread_mutex_t* gthread_mutex(void)
210  { return &_M_mutex; }
211  };
212 
213  class __recursive_mutex
214  {
215  private:
216  __gthread_recursive_mutex_t _M_mutex;
217 
218  __recursive_mutex(const __recursive_mutex&);
219  __recursive_mutex& operator=(const __recursive_mutex&);
220 
221  public:
222  __recursive_mutex()
223  {
224 #if __GTHREADS
225  if (__gthread_active_p())
226  {
227 #if defined __GTHREAD_RECURSIVE_MUTEX_INIT
228  __gthread_recursive_mutex_t __tmp = __GTHREAD_RECURSIVE_MUTEX_INIT;
229  __copy_gthr_type(_M_mutex, __tmp);
230 #else
231  __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
232 #endif
233  }
234 #endif
235  }
236 
237 #if __GTHREADS && ! defined __GTHREAD_RECURSIVE_MUTEX_INIT
238  ~__recursive_mutex()
239  {
240  if (__gthread_active_p())
241  _S_destroy(&_M_mutex);
242  }
243 #endif
244 
245  void lock()
246  {
247 #if __GTHREADS
248  if (__gthread_active_p())
249  {
250  if (__gthread_recursive_mutex_lock(&_M_mutex) != 0)
251  __throw_concurrence_lock_error();
252  }
253 #endif
254  }
255 
256  void unlock()
257  {
258 #if __GTHREADS
259  if (__gthread_active_p())
260  {
261  if (__gthread_recursive_mutex_unlock(&_M_mutex) != 0)
262  __throw_concurrence_unlock_error();
263  }
264 #endif
265  }
266 
267  __gthread_recursive_mutex_t* gthread_recursive_mutex(void)
268  { return &_M_mutex; }
269 
270 #if __GTHREADS && ! defined __GTHREAD_RECURSIVE_MUTEX_INIT
271  // FIXME: gthreads doesn't define __gthread_recursive_mutex_destroy
272  // so we need to obtain a __gthread_mutex_t to destroy
273  private:
274  template<typename _Mx, typename _Rm>
275  static void
276  _S_destroy_win32(_Mx* __mx, _Rm const* __rmx)
277  {
278  __mx->counter = __rmx->counter;
279  __mx->sema = __rmx->sema;
280  __gthread_mutex_destroy(__mx);
281  }
282 
283  // matches a gthr-win32.h recursive mutex
284  template<typename _Rm>
285  static typename __enable_if<(bool)sizeof(&_Rm::sema), void>::__type
286  _S_destroy(_Rm* __mx)
287  {
288  __gthread_mutex_t __tmp;
289  _S_destroy_win32(&__tmp, __mx);
290  }
291 
292  // matches a recursive mutex with a member 'actual'
293  template<typename _Rm>
294  static typename __enable_if<(bool)sizeof(&_Rm::actual), void>::__type
295  _S_destroy(_Rm* __mx)
296  { __gthread_mutex_destroy(&__mx->actual); }
297 
298  // matches when there's only one mutex type
299  template<typename _Rm>
300  static typename
301  __enable_if<std::__are_same<_Rm, __gthread_mutex_t>::__value,
302  void>::__type
303  _S_destroy(_Rm* __mx)
304  { __gthread_mutex_destroy(__mx); }
305 #endif
306  };
307 
308  /// Scoped lock idiom.
309  // Acquire the mutex here with a constructor call, then release with
310  // the destructor call in accordance with RAII style.
312  {
313  public:
314  typedef __mutex __mutex_type;
315 
316  private:
317  __mutex_type& _M_device;
318 
320  __scoped_lock& operator=(const __scoped_lock&);
321 
322  public:
323  explicit __scoped_lock(__mutex_type& __name) : _M_device(__name)
324  { _M_device.lock(); }
325 
326  ~__scoped_lock() throw()
327  { _M_device.unlock(); }
328  };
329 
330 #ifdef __GTHREAD_HAS_COND
331  class __cond
332  {
333  private:
334  __gthread_cond_t _M_cond;
335 
336  __cond(const __cond&);
337  __cond& operator=(const __cond&);
338 
339  public:
340  __cond()
341  {
342 #if __GTHREADS
343  if (__gthread_active_p())
344  {
345 #if defined __GTHREAD_COND_INIT
346  __gthread_cond_t __tmp = __GTHREAD_COND_INIT;
347  __copy_gthr_type(_M_cond, __tmp);
348 #else
349  __GTHREAD_COND_INIT_FUNCTION(&_M_cond);
350 #endif
351  }
352 #endif
353  }
354 
355 #if __GTHREADS && ! defined __GTHREAD_COND_INIT
356  ~__cond()
357  {
358  if (__gthread_active_p())
359  __gthread_cond_destroy(&_M_cond);
360  }
361 #endif
362 
363  void broadcast()
364  {
365 #if __GTHREADS
366  if (__gthread_active_p())
367  {
368  if (__gthread_cond_broadcast(&_M_cond) != 0)
369  __throw_concurrence_broadcast_error();
370  }
371 #endif
372  }
373 
374  void wait(__mutex *mutex)
375  {
376 #if __GTHREADS
377  {
378  if (__gthread_cond_wait(&_M_cond, mutex->gthread_mutex()) != 0)
379  __throw_concurrence_wait_error();
380  }
381 #endif
382  }
383 
384  void wait_recursive(__recursive_mutex *mutex)
385  {
386 #if __GTHREADS
387  {
388  if (__gthread_cond_wait_recursive(&_M_cond,
389  mutex->gthread_recursive_mutex())
390  != 0)
391  __throw_concurrence_wait_error();
392  }
393 #endif
394  }
395  };
396 #endif
397 
398 _GLIBCXX_END_NAMESPACE_VERSION
399 } // namespace
400 
401 #endif