Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/Assertions.h"
8 : #include "mozilla/CheckedInt.h"
9 :
10 : #include <errno.h>
11 : #include <pthread.h>
12 : #include <stdlib.h>
13 : #include <time.h>
14 : #include <unistd.h>
15 :
16 : #include "mozilla/PlatformConditionVariable.h"
17 : #include "mozilla/PlatformMutex.h"
18 : #include "MutexPlatformData_posix.h"
19 :
20 : using mozilla::CheckedInt;
21 : using mozilla::TimeDuration;
22 : using mozilla::TimeStamp;
23 :
24 : static const long NanoSecPerSec = 1000000000;
25 :
26 : // Android 32-bit & macOS 10.12 has the clock functions, but not pthread_condattr_setclock.
27 : #if defined(HAVE_CLOCK_MONOTONIC) && \
28 : !(defined(__ANDROID__) && !defined(__LP64__)) && !defined(__APPLE__)
29 : # define CV_USE_CLOCK_API
30 : #endif
31 :
32 : #ifdef CV_USE_CLOCK_API
33 : // The C++ specification defines std::condition_variable::wait_for in terms of
34 : // std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC.
35 : static const clockid_t WhichClock = CLOCK_MONOTONIC;
36 :
37 : // While timevaladd is widely available to work with timevals, the newer
38 : // timespec structure is largely lacking such conveniences. Thankfully, the
39 : // utilities available in MFBT make implementing our own quite easy.
40 : static void
41 379 : moz_timespecadd(struct timespec* lhs, struct timespec* rhs, struct timespec* result)
42 : {
43 : // Add nanoseconds. This may wrap, but not above 2 billion.
44 379 : MOZ_RELEASE_ASSERT(lhs->tv_nsec < NanoSecPerSec);
45 379 : MOZ_RELEASE_ASSERT(rhs->tv_nsec < NanoSecPerSec);
46 379 : result->tv_nsec = lhs->tv_nsec + rhs->tv_nsec;
47 :
48 : // Add seconds, checking for overflow in the platform specific time_t type.
49 379 : CheckedInt<time_t> sec = CheckedInt<time_t>(lhs->tv_sec) + rhs->tv_sec;
50 :
51 : // If nanoseconds overflowed, carry the result over into seconds.
52 379 : if (result->tv_nsec >= NanoSecPerSec) {
53 33 : MOZ_RELEASE_ASSERT(result->tv_nsec < 2 * NanoSecPerSec);
54 33 : result->tv_nsec -= NanoSecPerSec;
55 33 : sec += 1;
56 : }
57 :
58 : // Extracting the value asserts that there was no overflow.
59 379 : MOZ_RELEASE_ASSERT(sec.isValid());
60 379 : result->tv_sec = sec.value();
61 379 : }
62 : #endif
63 :
64 : struct mozilla::detail::ConditionVariableImpl::PlatformData
65 : {
66 : pthread_cond_t ptCond;
67 : };
68 :
69 349 : mozilla::detail::ConditionVariableImpl::ConditionVariableImpl()
70 : {
71 349 : pthread_cond_t* ptCond = &platformData()->ptCond;
72 :
73 : #ifdef CV_USE_CLOCK_API
74 : pthread_condattr_t attr;
75 349 : int r0 = pthread_condattr_init(&attr);
76 349 : MOZ_RELEASE_ASSERT(!r0);
77 :
78 349 : int r1 = pthread_condattr_setclock(&attr, WhichClock);
79 349 : MOZ_RELEASE_ASSERT(!r1);
80 :
81 349 : int r2 = pthread_cond_init(ptCond, &attr);
82 349 : MOZ_RELEASE_ASSERT(!r2);
83 :
84 349 : int r3 = pthread_condattr_destroy(&attr);
85 349 : MOZ_RELEASE_ASSERT(!r3);
86 : #else
87 : int r = pthread_cond_init(ptCond, NULL);
88 : MOZ_RELEASE_ASSERT(!r);
89 : #endif
90 349 : }
91 :
92 36 : mozilla::detail::ConditionVariableImpl::~ConditionVariableImpl()
93 : {
94 18 : int r = pthread_cond_destroy(&platformData()->ptCond);
95 18 : MOZ_RELEASE_ASSERT(r == 0);
96 18 : }
97 :
98 : void
99 2168 : mozilla::detail::ConditionVariableImpl::notify_one()
100 : {
101 2168 : int r = pthread_cond_signal(&platformData()->ptCond);
102 2168 : MOZ_RELEASE_ASSERT(r == 0);
103 2168 : }
104 :
105 : void
106 508 : mozilla::detail::ConditionVariableImpl::notify_all()
107 : {
108 508 : int r = pthread_cond_broadcast(&platformData()->ptCond);
109 508 : MOZ_RELEASE_ASSERT(r == 0);
110 508 : }
111 :
112 : void
113 410 : mozilla::detail::ConditionVariableImpl::wait(MutexImpl& lock)
114 : {
115 410 : pthread_cond_t* ptCond = &platformData()->ptCond;
116 410 : pthread_mutex_t* ptMutex = &lock.platformData()->ptMutex;
117 :
118 410 : int r = pthread_cond_wait(ptCond, ptMutex);
119 324 : MOZ_RELEASE_ASSERT(r == 0);
120 324 : }
121 :
122 : mozilla::detail::CVStatus
123 471 : mozilla::detail::ConditionVariableImpl::wait_for(MutexImpl& lock,
124 : const TimeDuration& a_rel_time)
125 : {
126 471 : if (a_rel_time == TimeDuration::Forever()) {
127 92 : wait(lock);
128 56 : return CVStatus::NoTimeout;
129 : }
130 :
131 379 : pthread_cond_t* ptCond = &platformData()->ptCond;
132 379 : pthread_mutex_t* ptMutex = &lock.platformData()->ptMutex;
133 : int r;
134 :
135 : // Clamp to 0, as time_t is unsigned.
136 758 : TimeDuration rel_time = a_rel_time < TimeDuration::FromSeconds(0)
137 : ? TimeDuration::FromSeconds(0)
138 379 : : a_rel_time;
139 :
140 : // Convert the duration to a timespec.
141 : struct timespec rel_ts;
142 379 : rel_ts.tv_sec = static_cast<time_t>(rel_time.ToSeconds());
143 379 : rel_ts.tv_nsec = static_cast<uint64_t>(rel_time.ToMicroseconds() * 1000.0) % NanoSecPerSec;
144 :
145 : #ifdef CV_USE_CLOCK_API
146 : struct timespec now_ts;
147 379 : r = clock_gettime(WhichClock, &now_ts);
148 379 : MOZ_RELEASE_ASSERT(!r);
149 :
150 : struct timespec abs_ts;
151 379 : moz_timespecadd(&now_ts, &rel_ts, &abs_ts);
152 :
153 379 : r = pthread_cond_timedwait(ptCond, ptMutex, &abs_ts);
154 : #else
155 : // Our non-clock-supporting platforms, OS X and Android, do support waiting
156 : // on a condition variable with a relative timeout.
157 : r = pthread_cond_timedwait_relative_np(ptCond, ptMutex, &rel_ts);
158 : #endif
159 :
160 372 : if (r == 0) {
161 278 : return CVStatus::NoTimeout;
162 : }
163 94 : MOZ_RELEASE_ASSERT(r == ETIMEDOUT);
164 94 : return CVStatus::Timeout;
165 : }
166 :
167 : mozilla::detail::ConditionVariableImpl::PlatformData*
168 3832 : mozilla::detail::ConditionVariableImpl::platformData()
169 : {
170 : static_assert(sizeof platformData_ >= sizeof(PlatformData),
171 : "platformData_ is too small");
172 3832 : return reinterpret_cast<PlatformData*>(platformData_);
173 : }
|