Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: webrtc/system_wrappers/source/event_timer_posix.cc

Issue 1812533002: Fix race condition in EventTimerPosix (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fixed race in test code Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/system_wrappers/source/event_timer_posix.h" 11 #include "webrtc/system_wrappers/source/event_timer_posix.h"
12 12
13 #include <errno.h> 13 #include <errno.h>
14 #include <pthread.h> 14 #include <pthread.h>
15 #include <signal.h> 15 #include <signal.h>
16 #include <stdio.h> 16 #include <stdio.h>
17 #include <string.h> 17 #include <string.h>
18 #include <sys/time.h> 18 #include <sys/time.h>
19 #include <unistd.h> 19 #include <unistd.h>
20 20
21 #include "webrtc/base/checks.h" 21 #include "webrtc/base/checks.h"
22 22
23 namespace webrtc { 23 namespace webrtc {
24 24
25 // static 25 // static
26 EventTimerWrapper* EventTimerWrapper::Create() { 26 EventTimerWrapper* EventTimerWrapper::Create() {
27 return new EventTimerPosix(); 27 return new EventTimerPosix();
28 } 28 }
29 29
30 const long int E6 = 1000000; 30 const int64_t kNanosecondsPerMillisecond = 1000000;
31 const long int E9 = 1000 * E6; 31 const int64_t kNanosecondsPerSecond = 1000000000;
32 32
33 EventTimerPosix::EventTimerPosix() 33 EventTimerPosix::EventTimerPosix()
34 : event_set_(false), 34 : event_set_(false),
35 timer_thread_(nullptr), 35 timer_thread_(nullptr),
36 created_at_(), 36 created_at_(),
37 periodic_(false), 37 periodic_(false),
38 time_(0), 38 time_ms_(0),
39 count_(0) { 39 count_(0),
40 is_stopping_(false) {
40 pthread_mutexattr_t attr; 41 pthread_mutexattr_t attr;
41 pthread_mutexattr_init(&attr); 42 pthread_mutexattr_init(&attr);
42 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 43 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
43 pthread_mutex_init(&mutex_, &attr); 44 pthread_mutex_init(&mutex_, &attr);
44 pthread_condattr_t cond_attr; 45 pthread_condattr_t cond_attr;
45 pthread_condattr_init(&cond_attr); 46 pthread_condattr_init(&cond_attr);
46 // TODO(sprang): Remove HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC special case once 47 // TODO(sprang): Remove HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC special case once
47 // all supported Android platforms support pthread_condattr_setclock. 48 // all supported Android platforms support pthread_condattr_setclock.
48 // TODO(sprang): Add support for monotonic clock on Apple platforms. 49 // TODO(sprang): Add support for monotonic clock on Apple platforms.
49 #if !(defined(WEBRTC_MAC) || defined(WEBRTC_IOS)) && \ 50 #if !(defined(WEBRTC_MAC) || defined(WEBRTC_IOS)) && \
(...skipping 13 matching lines...) Expand all
63 64
64 // TODO(pbos): Make this void. 65 // TODO(pbos): Make this void.
65 bool EventTimerPosix::Set() { 66 bool EventTimerPosix::Set() {
66 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); 67 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
67 event_set_ = true; 68 event_set_ = true;
68 pthread_cond_signal(&cond_); 69 pthread_cond_signal(&cond_);
69 pthread_mutex_unlock(&mutex_); 70 pthread_mutex_unlock(&mutex_);
70 return true; 71 return true;
71 } 72 }
72 73
73 EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout) { 74 EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout_ms) {
74 int ret_val = 0; 75 int ret_val = 0;
75 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); 76 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
76 77
77 if (!event_set_) { 78 if (!event_set_) {
78 if (WEBRTC_EVENT_INFINITE != timeout) { 79 if (WEBRTC_EVENT_INFINITE != timeout_ms) {
79 timespec end_at; 80 timespec end_at;
80 #ifndef WEBRTC_MAC 81 #ifndef WEBRTC_MAC
81 clock_gettime(CLOCK_MONOTONIC, &end_at); 82 clock_gettime(CLOCK_MONOTONIC, &end_at);
82 #else 83 #else
83 timeval value; 84 timeval value;
84 struct timezone time_zone; 85 struct timezone time_zone;
85 time_zone.tz_minuteswest = 0; 86 time_zone.tz_minuteswest = 0;
86 time_zone.tz_dsttime = 0; 87 time_zone.tz_dsttime = 0;
87 gettimeofday(&value, &time_zone); 88 gettimeofday(&value, &time_zone);
88 TIMEVAL_TO_TIMESPEC(&value, &end_at); 89 TIMEVAL_TO_TIMESPEC(&value, &end_at);
89 #endif 90 #endif
90 end_at.tv_sec += timeout / 1000; 91 end_at.tv_sec += timeout_ms / 1000;
91 end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6; 92 end_at.tv_nsec += (timeout_ms % 1000) * kNanosecondsPerMillisecond;
92 93
93 if (end_at.tv_nsec >= E9) { 94 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
94 end_at.tv_sec++; 95 end_at.tv_sec++;
95 end_at.tv_nsec -= E9; 96 end_at.tv_nsec -= kNanosecondsPerSecond;
96 } 97 }
97 while (ret_val == 0 && !event_set_) { 98 while (ret_val == 0 && !event_set_) {
98 #if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) 99 #if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
99 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, &end_at); 100 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, &end_at);
100 #else 101 #else
101 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at); 102 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
102 #endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 103 #endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
103 } 104 }
104 } else { 105 } else {
105 while (ret_val == 0 && !event_set_) 106 while (ret_val == 0 && !event_set_)
106 ret_val = pthread_cond_wait(&cond_, &mutex_); 107 ret_val = pthread_cond_wait(&cond_, &mutex_);
107 } 108 }
108 } 109 }
109 110
110 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); 111 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
111 112
112 // Reset and signal if set, regardless of why the thread woke up. 113 // Reset and signal if set, regardless of why the thread woke up.
113 if (event_set_) { 114 if (event_set_) {
114 ret_val = 0; 115 ret_val = 0;
115 event_set_ = false; 116 event_set_ = false;
116 } 117 }
117 pthread_mutex_unlock(&mutex_); 118 pthread_mutex_unlock(&mutex_);
118 119
119 return ret_val == 0 ? kEventSignaled : kEventTimeout; 120 return ret_val == 0 ? kEventSignaled : kEventTimeout;
120 } 121 }
121 122
122 EventTypeWrapper EventTimerPosix::Wait(timespec* end_at) { 123 EventTypeWrapper EventTimerPosix::Wait(timespec* end_at, bool reset_event) {
123 int ret_val = 0; 124 int ret_val = 0;
124 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); 125 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
126 if (reset_event) {
127 // Only wake for new events or timeouts.
128 event_set_ = false;
129 }
125 130
126 while (ret_val == 0 && !event_set_) { 131 while (ret_val == 0 && !event_set_) {
127 #if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) 132 #if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
128 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, end_at); 133 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, end_at);
129 #else 134 #else
130 ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at); 135 ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
131 #endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 136 #endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
132 } 137 }
133 138
134 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); 139 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
135 140
136 // Reset and signal if set, regardless of why the thread woke up. 141 // Reset and signal if set, regardless of why the thread woke up.
137 if (event_set_) { 142 if (event_set_) {
138 ret_val = 0; 143 ret_val = 0;
139 event_set_ = false; 144 event_set_ = false;
140 } 145 }
141 pthread_mutex_unlock(&mutex_); 146 pthread_mutex_unlock(&mutex_);
142 147
143 return ret_val == 0 ? kEventSignaled : kEventTimeout; 148 return ret_val == 0 ? kEventSignaled : kEventTimeout;
144 } 149 }
145 150
146 bool EventTimerPosix::StartTimer(bool periodic, unsigned long time) { 151 rtc::PlatformThread* EventTimerPosix::CreateThread() {
152 const char* kThreadName = "WebRtc_event_timer_thread";
153 return new rtc::PlatformThread(Run, this, kThreadName);
154 }
155
156 bool EventTimerPosix::StartTimer(bool periodic, unsigned long time_ms) {
147 pthread_mutex_lock(&mutex_); 157 pthread_mutex_lock(&mutex_);
148 if (timer_thread_) { 158 if (timer_thread_) {
149 if (periodic_) { 159 if (periodic_) {
150 // Timer already started. 160 // Timer already started.
151 pthread_mutex_unlock(&mutex_); 161 pthread_mutex_unlock(&mutex_);
152 return false; 162 return false;
153 } else { 163 } else {
154 // New one shot timer 164 // New one shot timer.
155 time_ = time; 165 time_ms_ = time_ms;
156 created_at_.tv_sec = 0; 166 created_at_.tv_sec = 0;
157 timer_event_->Set(); 167 timer_event_->Set();
158 pthread_mutex_unlock(&mutex_); 168 pthread_mutex_unlock(&mutex_);
159 return true; 169 return true;
160 } 170 }
161 } 171 }
162 172
163 // Start the timer thread 173 // Start the timer thread.
164 timer_event_.reset(new EventTimerPosix()); 174 timer_event_.reset(new EventTimerPosix());
165 const char* thread_name = "WebRtc_event_timer_thread"; 175 timer_thread_.reset(CreateThread());
166 timer_thread_.reset(new rtc::PlatformThread(Run, this, thread_name));
167 periodic_ = periodic; 176 periodic_ = periodic;
168 time_ = time; 177 time_ms_ = time_ms;
169 timer_thread_->Start(); 178 timer_thread_->Start();
170 timer_thread_->SetPriority(rtc::kRealtimePriority); 179 timer_thread_->SetPriority(rtc::kRealtimePriority);
171 pthread_mutex_unlock(&mutex_); 180 pthread_mutex_unlock(&mutex_);
172 181
173 return true; 182 return true;
174 } 183 }
175 184
176 bool EventTimerPosix::Run(void* obj) { 185 bool EventTimerPosix::Run(void* obj) {
177 return static_cast<EventTimerPosix*>(obj)->Process(); 186 return static_cast<EventTimerPosix*>(obj)->Process();
178 } 187 }
179 188
180 bool EventTimerPosix::Process() { 189 bool EventTimerPosix::Process() {
181 pthread_mutex_lock(&mutex_); 190 pthread_mutex_lock(&mutex_);
191 if (is_stopping_)
192 return false;
182 if (created_at_.tv_sec == 0) { 193 if (created_at_.tv_sec == 0) {
183 #ifndef WEBRTC_MAC 194 #ifndef WEBRTC_MAC
184 clock_gettime(CLOCK_MONOTONIC, &created_at_); 195 RTC_CHECK_EQ(0, clock_gettime(CLOCK_MONOTONIC, &created_at_));
185 #else 196 #else
186 timeval value; 197 timeval value;
187 struct timezone time_zone; 198 struct timezone time_zone;
188 time_zone.tz_minuteswest = 0; 199 time_zone.tz_minuteswest = 0;
189 time_zone.tz_dsttime = 0; 200 time_zone.tz_dsttime = 0;
190 gettimeofday(&value, &time_zone); 201 gettimeofday(&value, &time_zone);
191 TIMEVAL_TO_TIMESPEC(&value, &created_at_); 202 TIMEVAL_TO_TIMESPEC(&value, &created_at_);
192 #endif 203 #endif
193 count_ = 0; 204 count_ = 0;
194 } 205 }
195 206
196 timespec end_at; 207 timespec end_at;
197 unsigned long long time = time_ * ++count_; 208 unsigned long long total_delta_ms = time_ms_ * ++count_;
198 end_at.tv_sec = created_at_.tv_sec + time / 1000; 209 if (!periodic_ && count_ >= 1) {
199 end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6; 210 // No need to wake up often if we're not going to signal waiting threads.
211 total_delta_ms =
212 std::min<uint64_t>(total_delta_ms, 60 * kNanosecondsPerSecond);
213 }
200 214
201 if (end_at.tv_nsec >= E9) { 215 end_at.tv_sec = created_at_.tv_sec + total_delta_ms / 1000;
216 end_at.tv_nsec = created_at_.tv_nsec +
217 (total_delta_ms % 1000) * kNanosecondsPerMillisecond;
218
219 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
202 end_at.tv_sec++; 220 end_at.tv_sec++;
203 end_at.tv_nsec -= E9; 221 end_at.tv_nsec -= kNanosecondsPerSecond;
204 } 222 }
205 223
206 pthread_mutex_unlock(&mutex_); 224 pthread_mutex_unlock(&mutex_);
207 if (timer_event_->Wait(&end_at) == kEventSignaled) 225 // Reset event on first call so that we don't immediately return here if this
208 return true; 226 // thread was not blocked on timer_event_->Wait when the StartTimer() call
227 // was made.
228 if (timer_event_->Wait(&end_at, count_ == 1) == kEventSignaled)
229 return !is_stopping_;
209 230
210 pthread_mutex_lock(&mutex_); 231 pthread_mutex_lock(&mutex_);
211 if (periodic_ || count_ == 1) 232 if (periodic_ || count_ == 1)
212 Set(); 233 Set();
213 pthread_mutex_unlock(&mutex_); 234 pthread_mutex_unlock(&mutex_);
214 235
215 return true; 236 return true;
216 } 237 }
217 238
218 bool EventTimerPosix::StopTimer() { 239 bool EventTimerPosix::StopTimer() {
219 if (timer_event_) { 240 pthread_mutex_lock(&mutex_);
241 is_stopping_ = true;
242 pthread_mutex_unlock(&mutex_);
243
244 if (timer_event_)
220 timer_event_->Set(); 245 timer_event_->Set();
221 } 246
222 if (timer_thread_) { 247 if (timer_thread_) {
223 timer_thread_->Stop(); 248 timer_thread_->Stop();
224 timer_thread_.reset(); 249 timer_thread_.reset();
225 } 250 }
226 timer_event_.reset(); 251 timer_event_.reset();
227 252
228 // Set time to zero to force new reference time for the timer. 253 // Set time to zero to force new reference time for the timer.
229 memset(&created_at_, 0, sizeof(created_at_)); 254 memset(&created_at_, 0, sizeof(created_at_));
230 count_ = 0; 255 count_ = 0;
231 return true; 256 return true;
232 } 257 }
233 258
234 } // namespace webrtc 259 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698