aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/cxxsupp/libcxx/include/__condition_variable/condition_variable.h
blob: 7cbf21fe7311e84926ee4b6d87638c2da3e42183 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
#define _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H

#include <__chrono/steady_clock.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__config>
#include <__mutex/mutex.h>
#include <__mutex/unique_lock.h>
#include <__system_error/system_error.h>
#include <__threading_support>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_floating_point.h>
#include <__utility/move.h>
#include <limits>
#include <ratio>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

#ifndef _LIBCPP_HAS_NO_THREADS

// enum class cv_status
_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)

class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
  __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;

public:
  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default;

#  ifdef _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION
  ~condition_variable() = default;
#  else
  ~condition_variable();
#  endif

  condition_variable(const condition_variable&)            = delete;
  condition_variable& operator=(const condition_variable&) = delete;

  void notify_one() _NOEXCEPT;
  void notify_all() _NOEXCEPT;

  void wait(unique_lock<mutex>& __lk) _NOEXCEPT;
  template <class _Predicate>
  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS void wait(unique_lock<mutex>& __lk, _Predicate __pred);

  template <class _Clock, class _Duration>
  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status
  wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t);

  template <class _Clock, class _Duration, class _Predicate>
  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS bool
  wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred);

  template <class _Rep, class _Period>
  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status
  wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d);

  template <class _Rep, class _Period, class _Predicate>
  bool _LIBCPP_HIDE_FROM_ABI
  wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);

  typedef __libcpp_condvar_t* native_handle_type;
  _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }

private:
  void
  __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;
#  if defined(_LIBCPP_HAS_COND_CLOCKWAIT)
  _LIBCPP_HIDE_FROM_ABI void
  __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT;
#  endif
  template <class _Clock>
  _LIBCPP_HIDE_FROM_ABI void
  __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT;
};
#endif // !_LIBCPP_HAS_NO_THREADS

template <class _Rep, class _Period>
inline _LIBCPP_HIDE_FROM_ABI __enable_if_t<is_floating_point<_Rep>::value, chrono::nanoseconds>
__safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
  using namespace chrono;
  using __ratio       = ratio_divide<_Period, nano>;
  using __ns_rep      = nanoseconds::rep;
  _Rep __result_float = __d.count() * __ratio::num / __ratio::den;

  _Rep __result_max = numeric_limits<__ns_rep>::max();
  if (__result_float >= __result_max) {
    return nanoseconds::max();
  }

  _Rep __result_min = numeric_limits<__ns_rep>::min();
  if (__result_float <= __result_min) {
    return nanoseconds::min();
  }

  return nanoseconds(static_cast<__ns_rep>(__result_float));
}

template <class _Rep, class _Period>
inline _LIBCPP_HIDE_FROM_ABI __enable_if_t<!is_floating_point<_Rep>::value, chrono::nanoseconds>
__safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
  using namespace chrono;
  if (__d.count() == 0) {
    return nanoseconds(0);
  }

  using __ratio         = ratio_divide<_Period, nano>;
  using __ns_rep        = nanoseconds::rep;
  __ns_rep __result_max = numeric_limits<__ns_rep>::max();
  if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) {
    return nanoseconds::max();
  }

  __ns_rep __result_min = numeric_limits<__ns_rep>::min();
  if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) {
    return nanoseconds::min();
  }

  __ns_rep __result = __d.count() * __ratio::num / __ratio::den;
  if (__result == 0) {
    return nanoseconds(1);
  }

  return nanoseconds(__result);
}

#ifndef _LIBCPP_HAS_NO_THREADS
template <class _Predicate>
void condition_variable::wait(unique_lock<mutex>& __lk, _Predicate __pred) {
  while (!__pred())
    wait(__lk);
}

template <class _Clock, class _Duration>
cv_status condition_variable::wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t) {
  using namespace chrono;
  using __clock_tp_ns = time_point<_Clock, nanoseconds>;

  typename _Clock::time_point __now = _Clock::now();
  if (__t <= __now)
    return cv_status::timeout;

  __clock_tp_ns __t_ns = __clock_tp_ns(std::__safe_nanosecond_cast(__t.time_since_epoch()));

  __do_timed_wait(__lk, __t_ns);
  return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
}

template <class _Clock, class _Duration, class _Predicate>
bool condition_variable::wait_until(
    unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {
  while (!__pred()) {
    if (wait_until(__lk, __t) == cv_status::timeout)
      return __pred();
  }
  return true;
}

template <class _Rep, class _Period>
cv_status condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
  using namespace chrono;
  if (__d <= __d.zero())
    return cv_status::timeout;
  using __ns_rep                   = nanoseconds::rep;
  steady_clock::time_point __c_now = steady_clock::now();

#  if defined(_LIBCPP_HAS_COND_CLOCKWAIT)
  using __clock_tp_ns     = time_point<steady_clock, nanoseconds>;
  __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
#  else
  using __clock_tp_ns     = time_point<system_clock, nanoseconds>;
  __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
#  endif

  __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();

  if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
    __do_timed_wait(__lk, __clock_tp_ns::max());
  } else {
    __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
  }

  return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
}

template <class _Rep, class _Period, class _Predicate>
inline bool
condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
  return wait_until(__lk, chrono::steady_clock::now() + __d, std::move(__pred));
}

#  if defined(_LIBCPP_HAS_COND_CLOCKWAIT)
inline void condition_variable::__do_timed_wait(
    unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds> __tp) _NOEXCEPT {
  using namespace chrono;
  if (!__lk.owns_lock())
    __throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked");
  nanoseconds __d = __tp.time_since_epoch();
  timespec __ts;
  seconds __s                 = duration_cast<seconds>(__d);
  using __ts_sec              = decltype(__ts.tv_sec);
  const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max();
  if (__s.count() < __ts_sec_max) {
    __ts.tv_sec  = static_cast<__ts_sec>(__s.count());
    __ts.tv_nsec = (__d - __s).count();
  } else {
    __ts.tv_sec  = __ts_sec_max;
    __ts.tv_nsec = giga::num - 1;
  }
  int __ec = pthread_cond_clockwait(&__cv_, __lk.mutex()->native_handle(), CLOCK_MONOTONIC, &__ts);
  if (__ec != 0 && __ec != ETIMEDOUT)
    __throw_system_error(__ec, "condition_variable timed_wait failed");
}
#  endif // _LIBCPP_HAS_COND_CLOCKWAIT

template <class _Clock>
inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,
                                                chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {
  wait_for(__lk, __tp - _Clock::now());
}

#endif // _LIBCPP_HAS_NO_THREADS

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H