libstdc++
mutex
Go to the documentation of this file.
1 // <mutex> -*- C++ -*-
2 
3 // Copyright (C) 2003-2017 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file include/mutex
26  * This is a Standard C++ Library header.
27  */
28 
29 #ifndef _GLIBCXX_MUTEX
30 #define _GLIBCXX_MUTEX 1
31 
32 #pragma GCC system_header
33 
34 #if __cplusplus < 201103L
35 # include <bits/c++0x_warning.h>
36 #else
37 
38 #include <tuple>
39 #include <chrono>
40 #include <exception>
41 #include <type_traits>
42 #include <system_error>
43 #include <bits/std_mutex.h>
44 #if ! _GTHREAD_USE_MUTEX_TIMEDLOCK
45 # include <condition_variable>
46 # include <thread>
47 #endif
48 #ifndef _GLIBCXX_HAVE_TLS
49 # include <bits/std_function.h>
50 #endif
51 
52 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
53 
54 namespace std _GLIBCXX_VISIBILITY(default)
55 {
56 _GLIBCXX_BEGIN_NAMESPACE_VERSION
57 
58  /**
59  * @ingroup mutexes
60  * @{
61  */
62 
63 #ifdef _GLIBCXX_HAS_GTHREADS
64 
65  // Common base class for std::recursive_mutex and std::recursive_timed_mutex
66  class __recursive_mutex_base
67  {
68  protected:
69  typedef __gthread_recursive_mutex_t __native_type;
70 
71  __recursive_mutex_base(const __recursive_mutex_base&) = delete;
72  __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
73 
74 #ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
75  __native_type _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;
76 
77  __recursive_mutex_base() = default;
78 #else
79  __native_type _M_mutex;
80 
81  __recursive_mutex_base()
82  {
83  // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
84  __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
85  }
86 
87  ~__recursive_mutex_base()
88  { __gthread_recursive_mutex_destroy(&_M_mutex); }
89 #endif
90  };
91 
92  /// The standard recursive mutex type.
93  class recursive_mutex : private __recursive_mutex_base
94  {
95  public:
96  typedef __native_type* native_handle_type;
97 
98  recursive_mutex() = default;
99  ~recursive_mutex() = default;
100 
101  recursive_mutex(const recursive_mutex&) = delete;
102  recursive_mutex& operator=(const recursive_mutex&) = delete;
103 
104  void
105  lock()
106  {
107  int __e = __gthread_recursive_mutex_lock(&_M_mutex);
108 
109  // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
110  if (__e)
111  __throw_system_error(__e);
112  }
113 
114  bool
115  try_lock() noexcept
116  {
117  // XXX EINVAL, EAGAIN, EBUSY
118  return !__gthread_recursive_mutex_trylock(&_M_mutex);
119  }
120 
121  void
122  unlock()
123  {
124  // XXX EINVAL, EAGAIN, EBUSY
125  __gthread_recursive_mutex_unlock(&_M_mutex);
126  }
127 
128  native_handle_type
129  native_handle() noexcept
130  { return &_M_mutex; }
131  };
132 
133 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
134  template<typename _Derived>
135  class __timed_mutex_impl
136  {
137  protected:
138  typedef chrono::high_resolution_clock __clock_t;
139 
140  template<typename _Rep, typename _Period>
141  bool
142  _M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
143  {
144  using chrono::steady_clock;
145  auto __rt = chrono::duration_cast<steady_clock::duration>(__rtime);
146  if (ratio_greater<steady_clock::period, _Period>())
147  ++__rt;
148  return _M_try_lock_until(steady_clock::now() + __rt);
149  }
150 
151  template<typename _Duration>
152  bool
153  _M_try_lock_until(const chrono::time_point<__clock_t,
154  _Duration>& __atime)
155  {
156  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
157  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
158 
159  __gthread_time_t __ts = {
160  static_cast<std::time_t>(__s.time_since_epoch().count()),
161  static_cast<long>(__ns.count())
162  };
163 
164  return static_cast<_Derived*>(this)->_M_timedlock(__ts);
165  }
166 
167  template<typename _Clock, typename _Duration>
168  bool
169  _M_try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
170  {
171  auto __rtime = __atime - _Clock::now();
172  return _M_try_lock_until(__clock_t::now() + __rtime);
173  }
174  };
175 
176  /// The standard timed mutex type.
177  class timed_mutex
178  : private __mutex_base, public __timed_mutex_impl<timed_mutex>
179  {
180  public:
181  typedef __native_type* native_handle_type;
182 
183  timed_mutex() = default;
184  ~timed_mutex() = default;
185 
186  timed_mutex(const timed_mutex&) = delete;
187  timed_mutex& operator=(const timed_mutex&) = delete;
188 
189  void
190  lock()
191  {
192  int __e = __gthread_mutex_lock(&_M_mutex);
193 
194  // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
195  if (__e)
196  __throw_system_error(__e);
197  }
198 
199  bool
200  try_lock() noexcept
201  {
202  // XXX EINVAL, EAGAIN, EBUSY
203  return !__gthread_mutex_trylock(&_M_mutex);
204  }
205 
206  template <class _Rep, class _Period>
207  bool
208  try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
209  { return _M_try_lock_for(__rtime); }
210 
211  template <class _Clock, class _Duration>
212  bool
213  try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
214  { return _M_try_lock_until(__atime); }
215 
216  void
217  unlock()
218  {
219  // XXX EINVAL, EAGAIN, EBUSY
220  __gthread_mutex_unlock(&_M_mutex);
221  }
222 
223  native_handle_type
224  native_handle() noexcept
225  { return &_M_mutex; }
226 
227  private:
228  friend class __timed_mutex_impl<timed_mutex>;
229 
230  bool
231  _M_timedlock(const __gthread_time_t& __ts)
232  { return !__gthread_mutex_timedlock(&_M_mutex, &__ts); }
233  };
234 
235  /// recursive_timed_mutex
237  : private __recursive_mutex_base,
238  public __timed_mutex_impl<recursive_timed_mutex>
239  {
240  public:
241  typedef __native_type* native_handle_type;
242 
243  recursive_timed_mutex() = default;
244  ~recursive_timed_mutex() = default;
245 
247  recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
248 
249  void
250  lock()
251  {
252  int __e = __gthread_recursive_mutex_lock(&_M_mutex);
253 
254  // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
255  if (__e)
256  __throw_system_error(__e);
257  }
258 
259  bool
260  try_lock() noexcept
261  {
262  // XXX EINVAL, EAGAIN, EBUSY
263  return !__gthread_recursive_mutex_trylock(&_M_mutex);
264  }
265 
266  template <class _Rep, class _Period>
267  bool
268  try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
269  { return _M_try_lock_for(__rtime); }
270 
271  template <class _Clock, class _Duration>
272  bool
273  try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
274  { return _M_try_lock_until(__atime); }
275 
276  void
277  unlock()
278  {
279  // XXX EINVAL, EAGAIN, EBUSY
280  __gthread_recursive_mutex_unlock(&_M_mutex);
281  }
282 
283  native_handle_type
284  native_handle() noexcept
285  { return &_M_mutex; }
286 
287  private:
288  friend class __timed_mutex_impl<recursive_timed_mutex>;
289 
290  bool
291  _M_timedlock(const __gthread_time_t& __ts)
292  { return !__gthread_recursive_mutex_timedlock(&_M_mutex, &__ts); }
293  };
294 
295 #else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
296 
297  /// timed_mutex
299  {
300  mutex _M_mut;
301  condition_variable _M_cv;
302  bool _M_locked = false;
303 
304  public:
305 
306  timed_mutex() = default;
307  ~timed_mutex() { __glibcxx_assert( !_M_locked ); }
308 
309  timed_mutex(const timed_mutex&) = delete;
310  timed_mutex& operator=(const timed_mutex&) = delete;
311 
312  void
313  lock()
314  {
315  unique_lock<mutex> __lk(_M_mut);
316  _M_cv.wait(__lk, [&]{ return !_M_locked; });
317  _M_locked = true;
318  }
319 
320  bool
321  try_lock()
322  {
323  lock_guard<mutex> __lk(_M_mut);
324  if (_M_locked)
325  return false;
326  _M_locked = true;
327  return true;
328  }
329 
330  template<typename _Rep, typename _Period>
331  bool
332  try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
333  {
334  unique_lock<mutex> __lk(_M_mut);
335  if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
336  return false;
337  _M_locked = true;
338  return true;
339  }
340 
341  template<typename _Clock, typename _Duration>
342  bool
343  try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
344  {
345  unique_lock<mutex> __lk(_M_mut);
346  if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
347  return false;
348  _M_locked = true;
349  return true;
350  }
351 
352  void
353  unlock()
354  {
355  lock_guard<mutex> __lk(_M_mut);
356  __glibcxx_assert( _M_locked );
357  _M_locked = false;
358  _M_cv.notify_one();
359  }
360  };
361 
362  /// recursive_timed_mutex
364  {
365  mutex _M_mut;
366  condition_variable _M_cv;
367  thread::id _M_owner;
368  unsigned _M_count = 0;
369 
370  // Predicate type that tests whether the current thread can lock a mutex.
371  struct _Can_lock
372  {
373  // Returns true if the mutex is unlocked or is locked by _M_caller.
374  bool
375  operator()() const noexcept
376  { return _M_mx->_M_count == 0 || _M_mx->_M_owner == _M_caller; }
377 
378  const recursive_timed_mutex* _M_mx;
379  thread::id _M_caller;
380  };
381 
382  public:
383 
384  recursive_timed_mutex() = default;
385  ~recursive_timed_mutex() { __glibcxx_assert( _M_count == 0 ); }
386 
388  recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
389 
390  void
391  lock()
392  {
393  auto __id = this_thread::get_id();
394  _Can_lock __can_lock{this, __id};
395  unique_lock<mutex> __lk(_M_mut);
396  _M_cv.wait(__lk, __can_lock);
397  if (_M_count == -1u)
398  __throw_system_error(EAGAIN); // [thread.timedmutex.recursive]/3
399  _M_owner = __id;
400  ++_M_count;
401  }
402 
403  bool
404  try_lock()
405  {
406  auto __id = this_thread::get_id();
407  _Can_lock __can_lock{this, __id};
408  lock_guard<mutex> __lk(_M_mut);
409  if (!__can_lock())
410  return false;
411  if (_M_count == -1u)
412  return false;
413  _M_owner = __id;
414  ++_M_count;
415  return true;
416  }
417 
418  template<typename _Rep, typename _Period>
419  bool
420  try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
421  {
422  auto __id = this_thread::get_id();
423  _Can_lock __can_lock{this, __id};
424  unique_lock<mutex> __lk(_M_mut);
425  if (!_M_cv.wait_for(__lk, __rtime, __can_lock))
426  return false;
427  if (_M_count == -1u)
428  return false;
429  _M_owner = __id;
430  ++_M_count;
431  return true;
432  }
433 
434  template<typename _Clock, typename _Duration>
435  bool
436  try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
437  {
438  auto __id = this_thread::get_id();
439  _Can_lock __can_lock{this, __id};
440  unique_lock<mutex> __lk(_M_mut);
441  if (!_M_cv.wait_until(__lk, __atime, __can_lock))
442  return false;
443  if (_M_count == -1u)
444  return false;
445  _M_owner = __id;
446  ++_M_count;
447  return true;
448  }
449 
450  void
451  unlock()
452  {
453  lock_guard<mutex> __lk(_M_mut);
454  __glibcxx_assert( _M_owner == this_thread::get_id() );
455  __glibcxx_assert( _M_count > 0 );
456  if (--_M_count == 0)
457  {
458  _M_owner = {};
459  _M_cv.notify_one();
460  }
461  }
462  };
463 
464 #endif
465 #endif // _GLIBCXX_HAS_GTHREADS
466 
467  template<typename _Lock>
468  inline unique_lock<_Lock>
469  __try_to_lock(_Lock& __l)
470  { return unique_lock<_Lock>{__l, try_to_lock}; }
471 
472  template<int _Idx, bool _Continue = true>
473  struct __try_lock_impl
474  {
475  template<typename... _Lock>
476  static void
477  __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
478  {
479  __idx = _Idx;
480  auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
481  if (__lock.owns_lock())
482  {
483  constexpr bool __cont = _Idx + 2 < sizeof...(_Lock);
484  using __try_locker = __try_lock_impl<_Idx + 1, __cont>;
485  __try_locker::__do_try_lock(__locks, __idx);
486  if (__idx == -1)
487  __lock.release();
488  }
489  }
490  };
491 
492  template<int _Idx>
493  struct __try_lock_impl<_Idx, false>
494  {
495  template<typename... _Lock>
496  static void
497  __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
498  {
499  __idx = _Idx;
500  auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
501  if (__lock.owns_lock())
502  {
503  __idx = -1;
504  __lock.release();
505  }
506  }
507  };
508 
509  /** @brief Generic try_lock.
510  * @param __l1 Meets Mutex requirements (try_lock() may throw).
511  * @param __l2 Meets Mutex requirements (try_lock() may throw).
512  * @param __l3 Meets Mutex requirements (try_lock() may throw).
513  * @return Returns -1 if all try_lock() calls return true. Otherwise returns
514  * a 0-based index corresponding to the argument that returned false.
515  * @post Either all arguments are locked, or none will be.
516  *
517  * Sequentially calls try_lock() on each argument.
518  */
519  template<typename _Lock1, typename _Lock2, typename... _Lock3>
520  int
521  try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3)
522  {
523  int __idx;
524  auto __locks = std::tie(__l1, __l2, __l3...);
525  __try_lock_impl<0>::__do_try_lock(__locks, __idx);
526  return __idx;
527  }
528 
529  /** @brief Generic lock.
530  * @param __l1 Meets Mutex requirements (try_lock() may throw).
531  * @param __l2 Meets Mutex requirements (try_lock() may throw).
532  * @param __l3 Meets Mutex requirements (try_lock() may throw).
533  * @throw An exception thrown by an argument's lock() or try_lock() member.
534  * @post All arguments are locked.
535  *
536  * All arguments are locked via a sequence of calls to lock(), try_lock()
537  * and unlock(). If the call exits via an exception any locks that were
538  * obtained will be released.
539  */
540  template<typename _L1, typename _L2, typename... _L3>
541  void
542  lock(_L1& __l1, _L2& __l2, _L3&... __l3)
543  {
544  while (true)
545  {
546  using __try_locker = __try_lock_impl<0, sizeof...(_L3) != 0>;
547  unique_lock<_L1> __first(__l1);
548  int __idx;
549  auto __locks = std::tie(__l2, __l3...);
550  __try_locker::__do_try_lock(__locks, __idx);
551  if (__idx == -1)
552  {
553  __first.release();
554  return;
555  }
556  }
557  }
558 
559 #if __cplusplus > 201402L
560 #define __cpp_lib_scoped_lock 201707
561  /** @brief A scoped lock type for multiple lockable objects.
562  *
563  * A scoped_lock controls mutex ownership within a scope, releasing
564  * ownership in the destructor.
565  */
566  template<typename... _MutexTypes>
567  class scoped_lock
568  {
569  public:
570  explicit scoped_lock(_MutexTypes&... __m) : _M_devices(std::tie(__m...))
571  { std::lock(__m...); }
572 
573  explicit scoped_lock(adopt_lock_t, _MutexTypes&... __m) noexcept
574  : _M_devices(std::tie(__m...))
575  { } // calling thread owns mutex
576 
577  ~scoped_lock()
578  {
579  std::apply([](_MutexTypes&... __m) {
580  char __i[] __attribute__((__unused__)) = { (__m.unlock(), 0)... };
581  }, _M_devices);
582  }
583 
584  scoped_lock(const scoped_lock&) = delete;
585  scoped_lock& operator=(const scoped_lock&) = delete;
586 
587  private:
588  tuple<_MutexTypes&...> _M_devices;
589  };
590 
591  template<>
592  class scoped_lock<>
593  {
594  public:
595  explicit scoped_lock() = default;
596  explicit scoped_lock(adopt_lock_t) noexcept { }
597  ~scoped_lock() = default;
598 
599  scoped_lock(const scoped_lock&) = delete;
600  scoped_lock& operator=(const scoped_lock&) = delete;
601  };
602 
603  template<typename _Mutex>
604  class scoped_lock<_Mutex>
605  {
606  public:
607  using mutex_type = _Mutex;
608 
609  explicit scoped_lock(mutex_type& __m) : _M_device(__m)
610  { _M_device.lock(); }
611 
612  explicit scoped_lock(adopt_lock_t, mutex_type& __m) noexcept
613  : _M_device(__m)
614  { } // calling thread owns mutex
615 
616  ~scoped_lock()
617  { _M_device.unlock(); }
618 
619  scoped_lock(const scoped_lock&) = delete;
620  scoped_lock& operator=(const scoped_lock&) = delete;
621 
622  private:
623  mutex_type& _M_device;
624  };
625 #endif // C++17
626 
627 #ifdef _GLIBCXX_HAS_GTHREADS
628  /// once_flag
629  struct once_flag
630  {
631  private:
632  typedef __gthread_once_t __native_type;
633  __native_type _M_once = __GTHREAD_ONCE_INIT;
634 
635  public:
636  /// Constructor
637  constexpr once_flag() noexcept = default;
638 
639  /// Deleted copy constructor
640  once_flag(const once_flag&) = delete;
641  /// Deleted assignment operator
642  once_flag& operator=(const once_flag&) = delete;
643 
644  template<typename _Callable, typename... _Args>
645  friend void
646  call_once(once_flag& __once, _Callable&& __f, _Args&&... __args);
647  };
648 
649 #ifdef _GLIBCXX_HAVE_TLS
650  extern __thread void* __once_callable;
651  extern __thread void (*__once_call)();
652 #else
653  extern function<void()> __once_functor;
654 
655  extern void
656  __set_once_functor_lock_ptr(unique_lock<mutex>*);
657 
658  extern mutex&
659  __get_once_mutex();
660 #endif
661 
662  extern "C" void __once_proxy(void);
663 
664  /// call_once
665  template<typename _Callable, typename... _Args>
666  void
667  call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
668  {
669  // _GLIBCXX_RESOLVE_LIB_DEFECTS
670  // 2442. call_once() shouldn't DECAY_COPY()
671  auto __callable = [&] {
672  std::__invoke(std::forward<_Callable>(__f),
673  std::forward<_Args>(__args)...);
674  };
675 #ifdef _GLIBCXX_HAVE_TLS
676  __once_callable = std::__addressof(__callable);
677  __once_call = []{ (*(decltype(__callable)*)__once_callable)(); };
678 #else
679  unique_lock<mutex> __functor_lock(__get_once_mutex());
680  __once_functor = __callable;
681  __set_once_functor_lock_ptr(&__functor_lock);
682 #endif
683 
684  int __e = __gthread_once(&__once._M_once, &__once_proxy);
685 
686 #ifndef _GLIBCXX_HAVE_TLS
687  if (__functor_lock)
688  __set_once_functor_lock_ptr(0);
689 #endif
690 
691  if (__e)
692  __throw_system_error(__e);
693  }
694 #endif // _GLIBCXX_HAS_GTHREADS
695 
696  // @} group mutexes
697 _GLIBCXX_END_NAMESPACE_VERSION
698 } // namespace
699 #endif // _GLIBCXX_USE_C99_STDINT_TR1
700 
701 #endif // C++11
702 
703 #endif // _GLIBCXX_MUTEX
The standard recursive mutex type.
Definition: mutex:93
thread::id
Definition: thread:77
time_point
Definition: chrono:67
recursive_timed_mutex
Definition: mutex:363
constexpr _Tp * __addressof(_Tp &__r) noexcept
Same as C++11 std::addressof.
Definition: move.h:47
condition_variable
ISO C++ entities toplevel namespace is std.
__thread void * __once_callable
Generic try_lock.
constexpr tuple< _Elements &... > tie(_Elements &... __args) noexcept
tie
Definition: tuple:1589
unique_lock< _Lock > __try_to_lock(_Lock &__l)
Generic try_lock.
Definition: mutex:469
__thread void(* __once_call)()
Generic try_lock.
constexpr __invoke_result< _Callable, _Args... >::type __invoke(_Callable &&__fn, _Args &&... __args) noexcept(__is_nothrow_invocable< _Callable, _Args... >::value)
Invoke a callable object.
Definition: invoke.h:89
Primary class template, tuple.
Definition: tuple:53
void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)
Generic lock.
Definition: mutex:542
Assume the calling thread has already obtained mutex ownership and manage it.
Definition: std_mutex.h:139
The standard mutex type.
Definition: std_mutex.h:86
void call_once(once_flag &__once, _Callable &&__f, _Args &&... __args)
call_once
Definition: mutex:667
_GLIBCXX17_INLINE constexpr try_to_lock_t try_to_lock
Tag used to prevent a scoped lock from blocking if a mutex is locked.
Definition: std_mutex.h:145
constexpr enable_if< __is_duration< _ToDur >::value, _ToDur >::type duration_cast(const duration< _Rep, _Period > &__d)
duration_cast
Definition: chrono:194
A movable scoped lock type.
Definition: std_mutex.h:185
constexpr enable_if< __is_duration< _ToDur >::value, time_point< _Clock, _ToDur > >::type time_point_cast(const time_point< _Clock, _Dur > &__t)
time_point_cast
Definition: chrono:667
duration
Definition: chrono:64
timed_mutex
Definition: mutex:298
void __once_proxy(void)
Generic try_lock.
once_flag
Definition: mutex:629
thread::id get_id() noexcept
get_id
Definition: thread:336
A simple scoped lock type.
Definition: std_mutex.h:156
int try_lock(_Lock1 &__l1, _Lock2 &__l2, _Lock3 &... __l3)
Generic try_lock.
Definition: mutex:521