blob: e79aa9900af0004bd385ee879ed770e9ededc707 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "system_wrappers/source/event_timer_posix.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
Mirko Bonadei7fd74ff2018-02-26 17:06:51 +010013#if defined(WEBRTC_ANDROID)
14#include <android/api-level.h>
15#endif
16
niklase@google.com470e71d2011-07-07 08:21:25 +000017#include <errno.h>
18#include <pthread.h>
19#include <signal.h>
20#include <stdio.h>
21#include <string.h>
22#include <sys/time.h>
23#include <unistd.h>
24
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/checks.h"
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +000026
Mirko Bonadei7fd74ff2018-02-26 17:06:51 +010027#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
28// Chromium build is always defining this macro if __ANDROID_API__ < 20.
29#undef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
30#endif
31
32#if defined(WEBRTC_ANDROID) && defined(__ANDROID_API__)
33#define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC (__ANDROID_API__ < 21)
34#else
35#define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 0
36#endif
37
niklase@google.com470e71d2011-07-07 08:21:25 +000038namespace webrtc {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000039
Peter Boström64c03662015-04-08 11:24:19 +020040// static
41EventTimerWrapper* EventTimerWrapper::Create() {
42 return new EventTimerPosix();
43}
44
sprang53cf3462016-03-22 01:51:39 -070045const int64_t kNanosecondsPerMillisecond = 1000000;
46const int64_t kNanosecondsPerSecond = 1000000000;
niklase@google.com470e71d2011-07-07 08:21:25 +000047
Peter Boström64c03662015-04-08 11:24:19 +020048EventTimerPosix::EventTimerPosix()
pbos@webrtc.orga8463712015-03-17 13:11:15 +000049 : event_set_(false),
50 timer_thread_(nullptr),
pbos@webrtc.orga8463712015-03-17 13:11:15 +000051 created_at_(),
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000052 periodic_(false),
sprang53cf3462016-03-22 01:51:39 -070053 time_ms_(0),
54 count_(0),
55 is_stopping_(false) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +000056 pthread_mutexattr_t attr;
57 pthread_mutexattr_init(&attr);
58 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pbos@webrtc.orga8463712015-03-17 13:11:15 +000059 pthread_mutex_init(&mutex_, &attr);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000060 pthread_condattr_t cond_attr;
pbos@webrtc.orga8463712015-03-17 13:11:15 +000061 pthread_condattr_init(&cond_attr);
sprange791ffd2016-01-26 01:53:20 -080062// TODO(sprang): Remove HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC special case once
63// all supported Android platforms support pthread_condattr_setclock.
64// TODO(sprang): Add support for monotonic clock on Apple platforms.
65#if !(defined(WEBRTC_MAC) || defined(WEBRTC_IOS)) && \
Mirko Bonadei7fd74ff2018-02-26 17:06:51 +010066 !(defined(WEBRTC_ANDROID) && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
pbos@webrtc.orga8463712015-03-17 13:11:15 +000067 pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
sprange791ffd2016-01-26 01:53:20 -080068#endif
pbos@webrtc.orga8463712015-03-17 13:11:15 +000069 pthread_cond_init(&cond_, &cond_attr);
70 pthread_condattr_destroy(&cond_attr);
niklase@google.com470e71d2011-07-07 08:21:25 +000071}
72
Peter Boström64c03662015-04-08 11:24:19 +020073EventTimerPosix::~EventTimerPosix() {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000074 StopTimer();
75 pthread_cond_destroy(&cond_);
76 pthread_mutex_destroy(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +000077}
78
pbos@webrtc.orga8463712015-03-17 13:11:15 +000079// TODO(pbos): Make this void.
Peter Boström64c03662015-04-08 11:24:19 +020080bool EventTimerPosix::Set() {
henrikg91d6ede2015-09-17 00:24:34 -070081 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
pbos@webrtc.orga8463712015-03-17 13:11:15 +000082 event_set_ = true;
83 pthread_cond_signal(&cond_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000084 pthread_mutex_unlock(&mutex_);
85 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +000086}
87
sprang53cf3462016-03-22 01:51:39 -070088EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout_ms) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000089 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -070090 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000091
pbos@webrtc.orga8463712015-03-17 13:11:15 +000092 if (!event_set_) {
sprang53cf3462016-03-22 01:51:39 -070093 if (WEBRTC_EVENT_INFINITE != timeout_ms) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000094 timespec end_at;
niklase@google.com470e71d2011-07-07 08:21:25 +000095#ifndef WEBRTC_MAC
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000096 clock_gettime(CLOCK_MONOTONIC, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +000097#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000098 timeval value;
99 struct timezone time_zone;
100 time_zone.tz_minuteswest = 0;
101 time_zone.tz_dsttime = 0;
102 gettimeofday(&value, &time_zone);
103 TIMEVAL_TO_TIMESPEC(&value, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +0000104#endif
sprang53cf3462016-03-22 01:51:39 -0700105 end_at.tv_sec += timeout_ms / 1000;
106 end_at.tv_nsec += (timeout_ms % 1000) * kNanosecondsPerMillisecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000107
sprang53cf3462016-03-22 01:51:39 -0700108 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000109 end_at.tv_sec++;
sprang53cf3462016-03-22 01:51:39 -0700110 end_at.tv_nsec -= kNanosecondsPerSecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000111 }
sprange791ffd2016-01-26 01:53:20 -0800112 while (ret_val == 0 && !event_set_) {
Mirko Bonadei7fd74ff2018-02-26 17:06:51 +0100113#if defined(WEBRTC_ANDROID) && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
sprange791ffd2016-01-26 01:53:20 -0800114 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, &end_at);
115#else
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000116 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
sprange791ffd2016-01-26 01:53:20 -0800117#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
118 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000119 } else {
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000120 while (ret_val == 0 && !event_set_)
121 ret_val = pthread_cond_wait(&cond_, &mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000122 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000123 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000124
henrikg91d6ede2015-09-17 00:24:34 -0700125 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +0000126
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000127 // Reset and signal if set, regardless of why the thread woke up.
128 if (event_set_) {
129 ret_val = 0;
130 event_set_ = false;
131 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000132 pthread_mutex_unlock(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000133
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000134 return ret_val == 0 ? kEventSignaled : kEventTimeout;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000135}
136
sprang53cf3462016-03-22 01:51:39 -0700137EventTypeWrapper EventTimerPosix::Wait(timespec* end_at, bool reset_event) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000138 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -0700139 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
sprang53cf3462016-03-22 01:51:39 -0700140 if (reset_event) {
141 // Only wake for new events or timeouts.
142 event_set_ = false;
143 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000144
sprange791ffd2016-01-26 01:53:20 -0800145 while (ret_val == 0 && !event_set_) {
Mirko Bonadei7fd74ff2018-02-26 17:06:51 +0100146#if defined(WEBRTC_ANDROID) && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
sprange791ffd2016-01-26 01:53:20 -0800147 ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, end_at);
148#else
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000149 ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
sprange791ffd2016-01-26 01:53:20 -0800150#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
151 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000152
henrikg91d6ede2015-09-17 00:24:34 -0700153 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000154
155 // Reset and signal if set, regardless of why the thread woke up.
156 if (event_set_) {
157 ret_val = 0;
158 event_set_ = false;
159 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000160 pthread_mutex_unlock(&mutex_);
161
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000162 return ret_val == 0 ? kEventSignaled : kEventTimeout;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000163}
164
sprang53cf3462016-03-22 01:51:39 -0700165rtc::PlatformThread* EventTimerPosix::CreateThread() {
166 const char* kThreadName = "WebRtc_event_timer_thread";
167 return new rtc::PlatformThread(Run, this, kThreadName);
168}
169
170bool EventTimerPosix::StartTimer(bool periodic, unsigned long time_ms) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000171 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000172 if (timer_thread_) {
173 if (periodic_) {
174 // Timer already started.
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000175 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000176 return false;
Karl Wiberg79eb1d92017-11-08 12:26:07 +0100177 } else {
sprang53cf3462016-03-22 01:51:39 -0700178 // New one shot timer.
179 time_ms_ = time_ms;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000180 created_at_.tv_sec = 0;
181 timer_event_->Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000182 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000183 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000184 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000185 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000186
sprang53cf3462016-03-22 01:51:39 -0700187 // Start the timer thread.
Peter Boström64c03662015-04-08 11:24:19 +0200188 timer_event_.reset(new EventTimerPosix());
sprang53cf3462016-03-22 01:51:39 -0700189 timer_thread_.reset(CreateThread());
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000190 periodic_ = periodic;
sprang53cf3462016-03-22 01:51:39 -0700191 time_ms_ = time_ms;
Peter Boström8c38e8b2015-11-26 17:45:47 +0100192 timer_thread_->Start();
193 timer_thread_->SetPriority(rtc::kRealtimePriority);
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000194 pthread_mutex_unlock(&mutex_);
195
Peter Boström8c38e8b2015-11-26 17:45:47 +0100196 return true;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000197}
198
Peter Boström64c03662015-04-08 11:24:19 +0200199bool EventTimerPosix::Run(void* obj) {
200 return static_cast<EventTimerPosix*>(obj)->Process();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000201}
202
Peter Boström64c03662015-04-08 11:24:19 +0200203bool EventTimerPosix::Process() {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000204 pthread_mutex_lock(&mutex_);
sprang53cf3462016-03-22 01:51:39 -0700205 if (is_stopping_) {
206 pthread_mutex_unlock(&mutex_);
207 return false;
208 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000209 if (created_at_.tv_sec == 0) {
210#ifndef WEBRTC_MAC
sprang53cf3462016-03-22 01:51:39 -0700211 RTC_CHECK_EQ(0, clock_gettime(CLOCK_MONOTONIC, &created_at_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000212#else
213 timeval value;
214 struct timezone time_zone;
215 time_zone.tz_minuteswest = 0;
216 time_zone.tz_dsttime = 0;
217 gettimeofday(&value, &time_zone);
218 TIMEVAL_TO_TIMESPEC(&value, &created_at_);
219#endif
220 count_ = 0;
221 }
222
223 timespec end_at;
sprang53cf3462016-03-22 01:51:39 -0700224 unsigned long long total_delta_ms = time_ms_ * ++count_;
225 if (!periodic_ && count_ >= 1) {
226 // No need to wake up often if we're not going to signal waiting threads.
227 total_delta_ms =
228 std::min<uint64_t>(total_delta_ms, 60 * kNanosecondsPerSecond);
229 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000230
sprang53cf3462016-03-22 01:51:39 -0700231 end_at.tv_sec = created_at_.tv_sec + total_delta_ms / 1000;
232 end_at.tv_nsec = created_at_.tv_nsec +
233 (total_delta_ms % 1000) * kNanosecondsPerMillisecond;
234
235 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000236 end_at.tv_sec++;
sprang53cf3462016-03-22 01:51:39 -0700237 end_at.tv_nsec -= kNanosecondsPerSecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000238 }
239
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000240 pthread_mutex_unlock(&mutex_);
sprang53cf3462016-03-22 01:51:39 -0700241 // Reset event on first call so that we don't immediately return here if this
242 // thread was not blocked on timer_event_->Wait when the StartTimer() call
243 // was made.
244 if (timer_event_->Wait(&end_at, count_ == 1) == kEventSignaled)
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000245 return true;
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000246
247 pthread_mutex_lock(&mutex_);
248 if (periodic_ || count_ == 1)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000249 Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000250 pthread_mutex_unlock(&mutex_);
251
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000252 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000253}
254
Peter Boström64c03662015-04-08 11:24:19 +0200255bool EventTimerPosix::StopTimer() {
sprang53cf3462016-03-22 01:51:39 -0700256 pthread_mutex_lock(&mutex_);
257 is_stopping_ = true;
258 pthread_mutex_unlock(&mutex_);
259
260 if (timer_event_)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000261 timer_event_->Set();
sprang53cf3462016-03-22 01:51:39 -0700262
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000263 if (timer_thread_) {
Peter Boström8c38e8b2015-11-26 17:45:47 +0100264 timer_thread_->Stop();
tommi@webrtc.org361981f2015-03-19 14:44:18 +0000265 timer_thread_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000266 }
Peter Boström64c03662015-04-08 11:24:19 +0200267 timer_event_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000268
269 // Set time to zero to force new reference time for the timer.
270 memset(&created_at_, 0, sizeof(created_at_));
271 count_ = 0;
272 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000273}
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000274
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000275} // namespace webrtc