blob: 267deaffa356d7d534428035e974d6bfef15fec4 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// Copyright 2017 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Implementation of a small subset of Mutex and CondVar functionality
16// for platforms where the production implementation hasn't been fully
17// ported yet.
18
19#include "absl/synchronization/mutex.h"
20
21#if defined(_WIN32)
22#include <chrono> // NOLINT(build/c++11)
23#else
24#include <sys/time.h>
25#include <time.h>
26#endif
27
28#include <algorithm>
29
30#include "absl/base/internal/raw_logging.h"
31#include "absl/time/time.h"
32
33namespace absl {
34namespace synchronization_internal {
35
36namespace {
37
38// Return the current time plus the timeout.
39absl::Time DeadlineFromTimeout(absl::Duration timeout) {
40 return absl::Now() + timeout;
41}
42
43// Limit the deadline to a positive, 32-bit time_t value to accommodate
44// implementation restrictions. This also deals with InfinitePast and
45// InfiniteFuture.
46absl::Time LimitedDeadline(absl::Time deadline) {
47 deadline = std::max(absl::FromTimeT(0), deadline);
48 deadline = std::min(deadline, absl::FromTimeT(0x7fffffff));
49 return deadline;
50}
51
52} // namespace
53
54#if defined(_WIN32)
55
56MutexImpl::MutexImpl() {}
57
58MutexImpl::~MutexImpl() {
59 if (locked_) {
60 std_mutex_.unlock();
61 }
62}
63
64void MutexImpl::Lock() {
65 std_mutex_.lock();
66 locked_ = true;
67}
68
69bool MutexImpl::TryLock() {
70 bool locked = std_mutex_.try_lock();
71 if (locked) locked_ = true;
72 return locked;
73}
74
75void MutexImpl::Unlock() {
76 locked_ = false;
77 released_.SignalAll();
78 std_mutex_.unlock();
79}
80
81CondVarImpl::CondVarImpl() {}
82
83CondVarImpl::~CondVarImpl() {}
84
85void CondVarImpl::Signal() { std_cv_.notify_one(); }
86
87void CondVarImpl::SignalAll() { std_cv_.notify_all(); }
88
89void CondVarImpl::Wait(MutexImpl* mu) {
90 mu->released_.SignalAll();
91 std_cv_.wait(mu->std_mutex_);
92}
93
94bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) {
95 mu->released_.SignalAll();
96 time_t when = ToTimeT(deadline);
97 int64_t nanos = ToInt64Nanoseconds(deadline - absl::FromTimeT(when));
98 std::chrono::system_clock::time_point deadline_tp =
99 std::chrono::system_clock::from_time_t(when) +
100 std::chrono::duration_cast<std::chrono::system_clock::duration>(
101 std::chrono::nanoseconds(nanos));
102 auto deadline_since_epoch =
103 std::chrono::duration_cast<std::chrono::duration<double>>(
104 deadline_tp - std::chrono::system_clock::from_time_t(0));
105 return std_cv_.wait_until(mu->std_mutex_, deadline_tp) ==
106 std::cv_status::timeout;
107}
108
109#else // ! _WIN32
110
111MutexImpl::MutexImpl() {
112 ABSL_RAW_CHECK(pthread_mutex_init(&pthread_mutex_, nullptr) == 0,
113 "pthread error");
114}
115
116MutexImpl::~MutexImpl() {
117 if (locked_) {
118 ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error");
119 }
120 ABSL_RAW_CHECK(pthread_mutex_destroy(&pthread_mutex_) == 0, "pthread error");
121}
122
123void MutexImpl::Lock() {
124 ABSL_RAW_CHECK(pthread_mutex_lock(&pthread_mutex_) == 0, "pthread error");
125 locked_ = true;
126}
127
128bool MutexImpl::TryLock() {
129 bool locked = (0 == pthread_mutex_trylock(&pthread_mutex_));
130 if (locked) locked_ = true;
131 return locked;
132}
133
134void MutexImpl::Unlock() {
135 locked_ = false;
136 released_.SignalAll();
137 ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error");
138}
139
140CondVarImpl::CondVarImpl() {
141 ABSL_RAW_CHECK(pthread_cond_init(&pthread_cv_, nullptr) == 0,
142 "pthread error");
143}
144
145CondVarImpl::~CondVarImpl() {
146 ABSL_RAW_CHECK(pthread_cond_destroy(&pthread_cv_) == 0, "pthread error");
147}
148
149void CondVarImpl::Signal() {
150 ABSL_RAW_CHECK(pthread_cond_signal(&pthread_cv_) == 0, "pthread error");
151}
152
153void CondVarImpl::SignalAll() {
154 ABSL_RAW_CHECK(pthread_cond_broadcast(&pthread_cv_) == 0, "pthread error");
155}
156
157void CondVarImpl::Wait(MutexImpl* mu) {
158 mu->released_.SignalAll();
159 ABSL_RAW_CHECK(pthread_cond_wait(&pthread_cv_, &mu->pthread_mutex_) == 0,
160 "pthread error");
161}
162
163bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) {
164 mu->released_.SignalAll();
165 struct timespec ts = ToTimespec(deadline);
166 int rc = pthread_cond_timedwait(&pthread_cv_, &mu->pthread_mutex_, &ts);
167 if (rc == ETIMEDOUT) return true;
168 ABSL_RAW_CHECK(rc == 0, "pthread error");
169 return false;
170}
171
172#endif // ! _WIN32
173
174void MutexImpl::Await(const Condition& cond) {
175 if (cond.Eval()) return;
176 released_.SignalAll();
177 do {
178 released_.Wait(this);
179 } while (!cond.Eval());
180}
181
182bool MutexImpl::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
183 if (cond.Eval()) return true;
184 released_.SignalAll();
185 while (true) {
186 if (released_.WaitWithDeadline(this, deadline)) return false;
187 if (cond.Eval()) return true;
188 }
189}
190
191} // namespace synchronization_internal
192
193Mutex::Mutex() {}
194
195Mutex::~Mutex() {}
196
197void Mutex::Lock() { impl()->Lock(); }
198
199void Mutex::Unlock() { impl()->Unlock(); }
200
201bool Mutex::TryLock() { return impl()->TryLock(); }
202
203void Mutex::ReaderLock() { Lock(); }
204
205void Mutex::ReaderUnlock() { Unlock(); }
206
207void Mutex::Await(const Condition& cond) { impl()->Await(cond); }
208
209void Mutex::LockWhen(const Condition& cond) {
210 Lock();
211 Await(cond);
212}
213
214bool Mutex::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
215 return impl()->AwaitWithDeadline(
216 cond, synchronization_internal::LimitedDeadline(deadline));
217}
218
219bool Mutex::AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
220 return AwaitWithDeadline(
221 cond, synchronization_internal::DeadlineFromTimeout(timeout));
222}
223
224bool Mutex::LockWhenWithDeadline(const Condition& cond, absl::Time deadline) {
225 Lock();
226 return AwaitWithDeadline(cond, deadline);
227}
228
229bool Mutex::LockWhenWithTimeout(const Condition& cond, absl::Duration timeout) {
230 return LockWhenWithDeadline(
231 cond, synchronization_internal::DeadlineFromTimeout(timeout));
232}
233
234void Mutex::ReaderLockWhen(const Condition& cond) {
235 ReaderLock();
236 Await(cond);
237}
238
239bool Mutex::ReaderLockWhenWithTimeout(const Condition& cond,
240 absl::Duration timeout) {
241 return LockWhenWithTimeout(cond, timeout);
242}
243bool Mutex::ReaderLockWhenWithDeadline(const Condition& cond,
244 absl::Time deadline) {
245 return LockWhenWithDeadline(cond, deadline);
246}
247
248void Mutex::EnableDebugLog(const char*) {}
249void Mutex::EnableInvariantDebugging(void (*)(void*), void*) {}
250void Mutex::ForgetDeadlockInfo() {}
251void Mutex::AssertHeld() const {}
252void Mutex::AssertReaderHeld() const {}
253void Mutex::AssertNotHeld() const {}
254
255CondVar::CondVar() {}
256
257CondVar::~CondVar() {}
258
259void CondVar::Signal() { impl()->Signal(); }
260
261void CondVar::SignalAll() { impl()->SignalAll(); }
262
263void CondVar::Wait(Mutex* mu) { return impl()->Wait(mu->impl()); }
264
265bool CondVar::WaitWithDeadline(Mutex* mu, absl::Time deadline) {
266 return impl()->WaitWithDeadline(
267 mu->impl(), synchronization_internal::LimitedDeadline(deadline));
268}
269
270bool CondVar::WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
271 return WaitWithDeadline(mu, absl::Now() + timeout);
272}
273
274void CondVar::EnableDebugLog(const char*) {}
275
276#ifdef THREAD_SANITIZER
277extern "C" void __tsan_read1(void *addr);
278#else
279#define __tsan_read1(addr) // do nothing if TSan not enabled
280#endif
281
282// A function that just returns its argument, dereferenced
283static bool Dereference(void *arg) {
284 // ThreadSanitizer does not instrument this file for memory accesses.
285 // This function dereferences a user variable that can participate
286 // in a data race, so we need to manually tell TSan about this memory access.
287 __tsan_read1(arg);
288 return *(static_cast<bool *>(arg));
289}
290
291Condition::Condition() {} // null constructor, used for kTrue only
292const Condition Condition::kTrue;
293
294Condition::Condition(bool (*func)(void *), void *arg)
295 : eval_(&CallVoidPtrFunction),
296 function_(func),
297 method_(nullptr),
298 arg_(arg) {}
299
300bool Condition::CallVoidPtrFunction(const Condition *c) {
301 return (*c->function_)(c->arg_);
302}
303
304Condition::Condition(const bool *cond)
305 : eval_(CallVoidPtrFunction),
306 function_(Dereference),
307 method_(nullptr),
308 // const_cast is safe since Dereference does not modify arg
309 arg_(const_cast<bool *>(cond)) {}
310
311bool Condition::Eval() const {
312 // eval_ == null for kTrue
313 return (this->eval_ == nullptr) || (*this->eval_)(this);
314}
315
316void RegisterSymbolizer(bool (*)(const void*, char*, int)) {}
317
318} // namespace absl