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
41namespace __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  class __mutex
144  {
145  private:
146#if __GTHREADS && defined __GTHREAD_MUTEX_INIT
147    __gthread_mutex_t _M_mutex = __GTHREAD_MUTEX_INIT;
148#else
149    __gthread_mutex_t _M_mutex;
150#endif
151
152    __mutex(const __mutex&);
153    __mutex& operator=(const __mutex&);
154
155  public:
156    __mutex()
157    {
158#if __GTHREADS && ! defined __GTHREAD_MUTEX_INIT
159      if (__gthread_active_p())
160	__GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex);
161#endif
162    }
163
164#if __GTHREADS && ! defined __GTHREAD_MUTEX_INIT
165    ~__mutex()
166    {
167      if (__gthread_active_p())
168	__gthread_mutex_destroy(&_M_mutex);
169    }
170#endif
171
172    void lock()
173    {
174#if __GTHREADS
175      if (__gthread_active_p())
176	{
177	  if (__gthread_mutex_lock(&_M_mutex) != 0)
178	    __throw_concurrence_lock_error();
179	}
180#endif
181    }
182
183    void unlock()
184    {
185#if __GTHREADS
186      if (__gthread_active_p())
187	{
188	  if (__gthread_mutex_unlock(&_M_mutex) != 0)
189	    __throw_concurrence_unlock_error();
190	}
191#endif
192    }
193
194    __gthread_mutex_t* gthread_mutex(void)
195      { return &_M_mutex; }
196  };
197
198  class __recursive_mutex
199  {
200  private:
201#if __GTHREADS && defined __GTHREAD_RECURSIVE_MUTEX_INIT
202    __gthread_recursive_mutex_t _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;
203#else
204    __gthread_recursive_mutex_t _M_mutex;
205#endif
206
207    __recursive_mutex(const __recursive_mutex&);
208    __recursive_mutex& operator=(const __recursive_mutex&);
209
210  public:
211    __recursive_mutex()
212    {
213#if __GTHREADS && ! defined __GTHREAD_RECURSIVE_MUTEX_INIT
214      if (__gthread_active_p())
215	__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
216#endif
217    }
218
219#if __GTHREADS && ! defined __GTHREAD_RECURSIVE_MUTEX_INIT
220    ~__recursive_mutex()
221    {
222      if (__gthread_active_p())
223	_S_destroy(&_M_mutex);
224    }
225#endif
226
227    void lock()
228    {
229#if __GTHREADS
230      if (__gthread_active_p())
231	{
232	  if (__gthread_recursive_mutex_lock(&_M_mutex) != 0)
233	    __throw_concurrence_lock_error();
234	}
235#endif
236    }
237
238    void unlock()
239    {
240#if __GTHREADS
241      if (__gthread_active_p())
242	{
243	  if (__gthread_recursive_mutex_unlock(&_M_mutex) != 0)
244	    __throw_concurrence_unlock_error();
245	}
246#endif
247    }
248
249    __gthread_recursive_mutex_t* gthread_recursive_mutex(void)
250    { return &_M_mutex; }
251
252#if __GTHREADS && ! defined __GTHREAD_RECURSIVE_MUTEX_INIT
253    // FIXME: gthreads doesn't define __gthread_recursive_mutex_destroy
254    // so we need to obtain a __gthread_mutex_t to destroy
255  private:
256    template<typename _Mx, typename _Rm>
257      static void
258      _S_destroy_win32(_Mx* __mx, _Rm const* __rmx)
259      {
260        __mx->counter = __rmx->counter;
261        __mx->sema = __rmx->sema;
262        __gthread_mutex_destroy(__mx);
263      }
264
265    // matches a gthr-win32.h recursive mutex
266    template<typename _Rm>
267      static typename __enable_if<(bool)sizeof(&_Rm::sema), void>::__type
268      _S_destroy(_Rm* __mx)
269      {
270        __gthread_mutex_t __tmp;
271        _S_destroy_win32(&__tmp, __mx);
272      }
273
274    // matches a recursive mutex with a member 'actual'
275    template<typename _Rm>
276      static typename __enable_if<(bool)sizeof(&_Rm::actual), void>::__type
277      _S_destroy(_Rm* __mx)
278      { __gthread_mutex_destroy(&__mx->actual); }
279
280    // matches when there's only one mutex type
281    template<typename _Rm>
282      static typename
283      __enable_if<std::__are_same<_Rm, __gthread_mutex_t>::__value,
284        void>::__type
285      _S_destroy(_Rm* __mx)
286      { __gthread_mutex_destroy(__mx); }
287#endif
288  };
289
290  /// Scoped lock idiom.
291  // Acquire the mutex here with a constructor call, then release with
292  // the destructor call in accordance with RAII style.
293  class __scoped_lock
294  {
295  public:
296    typedef __mutex __mutex_type;
297
298  private:
299    __mutex_type& _M_device;
300
301    __scoped_lock(const __scoped_lock&);
302    __scoped_lock& operator=(const __scoped_lock&);
303
304  public:
305    explicit __scoped_lock(__mutex_type& __name) : _M_device(__name)
306    { _M_device.lock(); }
307
308    ~__scoped_lock() throw()
309    { _M_device.unlock(); }
310  };
311
312#ifdef __GTHREAD_HAS_COND
313  class __cond
314  {
315  private:
316#if __GTHREADS && defined __GTHREAD_COND_INIT
317    __gthread_cond_t _M_cond = __GTHREAD_COND_INIT;
318#else
319    __gthread_cond_t _M_cond;
320#endif
321
322    __cond(const __cond&);
323    __cond& operator=(const __cond&);
324
325  public:
326    __cond()
327    {
328#if __GTHREADS && ! defined __GTHREAD_COND_INIT
329      if (__gthread_active_p())
330	__GTHREAD_COND_INIT_FUNCTION(&_M_cond);
331#endif
332    }
333
334#if __GTHREADS && ! defined __GTHREAD_COND_INIT
335    ~__cond()
336    {
337      if (__gthread_active_p())
338	__gthread_cond_destroy(&_M_cond);
339    }
340#endif
341
342    void broadcast()
343    {
344#if __GTHREADS
345      if (__gthread_active_p())
346	{
347	  if (__gthread_cond_broadcast(&_M_cond) != 0)
348	    __throw_concurrence_broadcast_error();
349	}
350#endif
351    }
352
353    void wait(__mutex *mutex)
354    {
355#if __GTHREADS
356      {
357	  if (__gthread_cond_wait(&_M_cond, mutex->gthread_mutex()) != 0)
358	    __throw_concurrence_wait_error();
359      }
360#endif
361    }
362
363    void wait_recursive(__recursive_mutex *mutex)
364    {
365#if __GTHREADS
366      {
367	  if (__gthread_cond_wait_recursive(&_M_cond,
368					    mutex->gthread_recursive_mutex())
369	      != 0)
370	    __throw_concurrence_wait_error();
371      }
372#endif
373    }
374  };
375#endif
376
377_GLIBCXX_END_NAMESPACE_VERSION
378} // namespace
379
380#endif
381