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