blob: dead7c9b80d207a67e9b806f411ca1fd5a902ab4 [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
Peter Boström64c03662015-04-08 11:24:19 +020011#include "webrtc/system_wrappers/source/event_timer_posix.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
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
Edward Lemurc20978e2017-07-06 19:44:34 +020021#include "webrtc/rtc_base/checks.h"
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +000022
niklase@google.com470e71d2011-07-07 08:21:25 +000023namespace webrtc {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000024
Peter Boström64c03662015-04-08 11:24:19 +020025// static
26EventTimerWrapper* EventTimerWrapper::Create() {
27 return new EventTimerPosix();
28}
29
sprang53cf3462016-03-22 01:51:39 -070030const int64_t kNanosecondsPerMillisecond = 1000000;
31const int64_t kNanosecondsPerSecond = 1000000000;
niklase@google.com470e71d2011-07-07 08:21:25 +000032
Peter Boström64c03662015-04-08 11:24:19 +020033EventTimerPosix::EventTimerPosix()
pbos@webrtc.orga8463712015-03-17 13:11:15 +000034 : event_set_(false),
35 timer_thread_(nullptr),
pbos@webrtc.orga8463712015-03-17 13:11:15 +000036 created_at_(),
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000037 periodic_(false),
sprang53cf3462016-03-22 01:51:39 -070038 time_ms_(0),
39 count_(0),
40 is_stopping_(false) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +000041 pthread_mutexattr_t attr;
42 pthread_mutexattr_init(&attr);
43 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pbos@webrtc.orga8463712015-03-17 13:11:15 +000044 pthread_mutex_init(&mutex_, &attr);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000045 pthread_condattr_t cond_attr;
pbos@webrtc.orga8463712015-03-17 13:11:15 +000046 pthread_condattr_init(&cond_attr);
sprange791ffd2016-01-26 01:53:20 -080047// 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))
pbos@webrtc.orga8463712015-03-17 13:11:15 +000053 pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
sprange791ffd2016-01-26 01:53:20 -080054#endif
pbos@webrtc.orga8463712015-03-17 13:11:15 +000055 pthread_cond_init(&cond_, &cond_attr);
56 pthread_condattr_destroy(&cond_attr);
niklase@google.com470e71d2011-07-07 08:21:25 +000057}
58
Peter Boström64c03662015-04-08 11:24:19 +020059EventTimerPosix::~EventTimerPosix() {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000060 StopTimer();
61 pthread_cond_destroy(&cond_);
62 pthread_mutex_destroy(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +000063}
64
pbos@webrtc.orga8463712015-03-17 13:11:15 +000065// TODO(pbos): Make this void.
Peter Boström64c03662015-04-08 11:24:19 +020066bool EventTimerPosix::Set() {
henrikg91d6ede2015-09-17 00:24:34 -070067 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
pbos@webrtc.orga8463712015-03-17 13:11:15 +000068 event_set_ = true;
69 pthread_cond_signal(&cond_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000070 pthread_mutex_unlock(&mutex_);
71 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +000072}
73
sprang53cf3462016-03-22 01:51:39 -070074EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout_ms) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000075 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -070076 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000077
pbos@webrtc.orga8463712015-03-17 13:11:15 +000078 if (!event_set_) {
sprang53cf3462016-03-22 01:51:39 -070079 if (WEBRTC_EVENT_INFINITE != timeout_ms) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000080 timespec end_at;
niklase@google.com470e71d2011-07-07 08:21:25 +000081#ifndef WEBRTC_MAC
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000082 clock_gettime(CLOCK_MONOTONIC, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +000083#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000084 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);
niklase@google.com470e71d2011-07-07 08:21:25 +000090#endif
sprang53cf3462016-03-22 01:51:39 -070091 end_at.tv_sec += timeout_ms / 1000;
92 end_at.tv_nsec += (timeout_ms % 1000) * kNanosecondsPerMillisecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000093
sprang53cf3462016-03-22 01:51:39 -070094 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000095 end_at.tv_sec++;
sprang53cf3462016-03-22 01:51:39 -070096 end_at.tv_nsec -= kNanosecondsPerSecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000097 }
sprange791ffd2016-01-26 01:53:20 -080098 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
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000102 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
sprange791ffd2016-01-26 01:53:20 -0800103#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
104 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000105 } else {
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000106 while (ret_val == 0 && !event_set_)
107 ret_val = pthread_cond_wait(&cond_, &mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000108 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000109 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000110
henrikg91d6ede2015-09-17 00:24:34 -0700111 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +0000112
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000113 // Reset and signal if set, regardless of why the thread woke up.
114 if (event_set_) {
115 ret_val = 0;
116 event_set_ = false;
117 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000118 pthread_mutex_unlock(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000119
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000120 return ret_val == 0 ? kEventSignaled : kEventTimeout;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000121}
122
sprang53cf3462016-03-22 01:51:39 -0700123EventTypeWrapper EventTimerPosix::Wait(timespec* end_at, bool reset_event) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000124 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -0700125 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
sprang53cf3462016-03-22 01:51:39 -0700126 if (reset_event) {
127 // Only wake for new events or timeouts.
128 event_set_ = false;
129 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000130
sprange791ffd2016-01-26 01:53:20 -0800131 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
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000135 ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
sprange791ffd2016-01-26 01:53:20 -0800136#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
137 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000138
henrikg91d6ede2015-09-17 00:24:34 -0700139 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000140
141 // Reset and signal if set, regardless of why the thread woke up.
142 if (event_set_) {
143 ret_val = 0;
144 event_set_ = false;
145 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000146 pthread_mutex_unlock(&mutex_);
147
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000148 return ret_val == 0 ? kEventSignaled : kEventTimeout;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000149}
150
sprang53cf3462016-03-22 01:51:39 -0700151rtc::PlatformThread* EventTimerPosix::CreateThread() {
152 const char* kThreadName = "WebRtc_event_timer_thread";
153 return new rtc::PlatformThread(Run, this, kThreadName);
154}
155
156bool EventTimerPosix::StartTimer(bool periodic, unsigned long time_ms) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000157 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000158 if (timer_thread_) {
159 if (periodic_) {
160 // Timer already started.
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000161 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000162 return false;
163 } else {
sprang53cf3462016-03-22 01:51:39 -0700164 // New one shot timer.
165 time_ms_ = time_ms;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000166 created_at_.tv_sec = 0;
167 timer_event_->Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000168 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000169 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000170 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000171 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000172
sprang53cf3462016-03-22 01:51:39 -0700173 // Start the timer thread.
Peter Boström64c03662015-04-08 11:24:19 +0200174 timer_event_.reset(new EventTimerPosix());
sprang53cf3462016-03-22 01:51:39 -0700175 timer_thread_.reset(CreateThread());
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000176 periodic_ = periodic;
sprang53cf3462016-03-22 01:51:39 -0700177 time_ms_ = time_ms;
Peter Boström8c38e8b2015-11-26 17:45:47 +0100178 timer_thread_->Start();
179 timer_thread_->SetPriority(rtc::kRealtimePriority);
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000180 pthread_mutex_unlock(&mutex_);
181
Peter Boström8c38e8b2015-11-26 17:45:47 +0100182 return true;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000183}
184
Peter Boström64c03662015-04-08 11:24:19 +0200185bool EventTimerPosix::Run(void* obj) {
186 return static_cast<EventTimerPosix*>(obj)->Process();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000187}
188
Peter Boström64c03662015-04-08 11:24:19 +0200189bool EventTimerPosix::Process() {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000190 pthread_mutex_lock(&mutex_);
sprang53cf3462016-03-22 01:51:39 -0700191 if (is_stopping_) {
192 pthread_mutex_unlock(&mutex_);
193 return false;
194 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000195 if (created_at_.tv_sec == 0) {
196#ifndef WEBRTC_MAC
sprang53cf3462016-03-22 01:51:39 -0700197 RTC_CHECK_EQ(0, clock_gettime(CLOCK_MONOTONIC, &created_at_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000198#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 count_ = 0;
207 }
208
209 timespec end_at;
sprang53cf3462016-03-22 01:51:39 -0700210 unsigned long long total_delta_ms = time_ms_ * ++count_;
211 if (!periodic_ && count_ >= 1) {
212 // No need to wake up often if we're not going to signal waiting threads.
213 total_delta_ms =
214 std::min<uint64_t>(total_delta_ms, 60 * kNanosecondsPerSecond);
215 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000216
sprang53cf3462016-03-22 01:51:39 -0700217 end_at.tv_sec = created_at_.tv_sec + total_delta_ms / 1000;
218 end_at.tv_nsec = created_at_.tv_nsec +
219 (total_delta_ms % 1000) * kNanosecondsPerMillisecond;
220
221 if (end_at.tv_nsec >= kNanosecondsPerSecond) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000222 end_at.tv_sec++;
sprang53cf3462016-03-22 01:51:39 -0700223 end_at.tv_nsec -= kNanosecondsPerSecond;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000224 }
225
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000226 pthread_mutex_unlock(&mutex_);
sprang53cf3462016-03-22 01:51:39 -0700227 // 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 if (timer_event_->Wait(&end_at, count_ == 1) == kEventSignaled)
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000231 return true;
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000232
233 pthread_mutex_lock(&mutex_);
234 if (periodic_ || count_ == 1)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000235 Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000236 pthread_mutex_unlock(&mutex_);
237
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000238 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000239}
240
Peter Boström64c03662015-04-08 11:24:19 +0200241bool EventTimerPosix::StopTimer() {
sprang53cf3462016-03-22 01:51:39 -0700242 pthread_mutex_lock(&mutex_);
243 is_stopping_ = true;
244 pthread_mutex_unlock(&mutex_);
245
246 if (timer_event_)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000247 timer_event_->Set();
sprang53cf3462016-03-22 01:51:39 -0700248
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000249 if (timer_thread_) {
Peter Boström8c38e8b2015-11-26 17:45:47 +0100250 timer_thread_->Stop();
tommi@webrtc.org361981f2015-03-19 14:44:18 +0000251 timer_thread_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000252 }
Peter Boström64c03662015-04-08 11:24:19 +0200253 timer_event_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000254
255 // Set time to zero to force new reference time for the timer.
256 memset(&created_at_, 0, sizeof(created_at_));
257 count_ = 0;
258 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000259}
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000260
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000261} // namespace webrtc