blob: ff26e76ec0b3e036bb1cf2c7e5954277b79b5c85 [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
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +000021#include "webrtc/base/checks.h"
22
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
niklase@google.com470e71d2011-07-07 08:21:25 +000030const long int E6 = 1000000;
31const long int E9 = 1000 * E6;
32
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),
38 time_(0),
pbos@webrtc.orga8463712015-03-17 13:11:15 +000039 count_(0) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +000040 pthread_mutexattr_t attr;
41 pthread_mutexattr_init(&attr);
42 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pbos@webrtc.orga8463712015-03-17 13:11:15 +000043 pthread_mutex_init(&mutex_, &attr);
niklase@google.com470e71d2011-07-07 08:21:25 +000044#ifdef WEBRTC_CLOCK_TYPE_REALTIME
pbos@webrtc.orga8463712015-03-17 13:11:15 +000045 pthread_cond_init(&cond_, 0);
niklase@google.com470e71d2011-07-07 08:21:25 +000046#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000047 pthread_condattr_t cond_attr;
pbos@webrtc.orga8463712015-03-17 13:11:15 +000048 pthread_condattr_init(&cond_attr);
49 pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
50 pthread_cond_init(&cond_, &cond_attr);
51 pthread_condattr_destroy(&cond_attr);
niklase@google.com470e71d2011-07-07 08:21:25 +000052#endif
niklase@google.com470e71d2011-07-07 08:21:25 +000053}
54
Peter Boström64c03662015-04-08 11:24:19 +020055EventTimerPosix::~EventTimerPosix() {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000056 StopTimer();
57 pthread_cond_destroy(&cond_);
58 pthread_mutex_destroy(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +000059}
60
pbos@webrtc.orga8463712015-03-17 13:11:15 +000061// TODO(pbos): Make this void.
Peter Boström64c03662015-04-08 11:24:19 +020062bool EventTimerPosix::Set() {
henrikg91d6ede2015-09-17 00:24:34 -070063 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
pbos@webrtc.orga8463712015-03-17 13:11:15 +000064 event_set_ = true;
65 pthread_cond_signal(&cond_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000066 pthread_mutex_unlock(&mutex_);
67 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +000068}
69
Peter Boström64c03662015-04-08 11:24:19 +020070EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000071 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -070072 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000073
pbos@webrtc.orga8463712015-03-17 13:11:15 +000074 if (!event_set_) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000075 if (WEBRTC_EVENT_INFINITE != timeout) {
76 timespec end_at;
niklase@google.com470e71d2011-07-07 08:21:25 +000077#ifndef WEBRTC_MAC
78#ifdef WEBRTC_CLOCK_TYPE_REALTIME
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000079 clock_gettime(CLOCK_REALTIME, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +000080#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000081 clock_gettime(CLOCK_MONOTONIC, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +000082#endif
83#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
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000091 end_at.tv_sec += timeout / 1000;
92 end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6;
93
94 if (end_at.tv_nsec >= E9) {
95 end_at.tv_sec++;
96 end_at.tv_nsec -= E9;
97 }
pbos@webrtc.orga8463712015-03-17 13:11:15 +000098 while (ret_val == 0 && !event_set_)
99 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000100 } else {
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000101 while (ret_val == 0 && !event_set_)
102 ret_val = pthread_cond_wait(&cond_, &mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000103 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000104 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000105
henrikg91d6ede2015-09-17 00:24:34 -0700106 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +0000107
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000108 // Reset and signal if set, regardless of why the thread woke up.
109 if (event_set_) {
110 ret_val = 0;
111 event_set_ = false;
112 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000113 pthread_mutex_unlock(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000114
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000115 return ret_val == 0 ? kEventSignaled : kEventTimeout;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000116}
117
Peter Boström64c03662015-04-08 11:24:19 +0200118EventTypeWrapper EventTimerPosix::Wait(timespec* end_at) {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000119 int ret_val = 0;
henrikg91d6ede2015-09-17 00:24:34 -0700120 RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000121
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000122 while (ret_val == 0 && !event_set_)
123 ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000124
henrikg91d6ede2015-09-17 00:24:34 -0700125 RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000126
127 // 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_);
133
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
Peter Boström64c03662015-04-08 11:24:19 +0200137bool EventTimerPosix::StartTimer(bool periodic, unsigned long time) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000138 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000139 if (timer_thread_) {
140 if (periodic_) {
141 // Timer already started.
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000142 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000143 return false;
144 } else {
145 // New one shot timer
146 time_ = time;
147 created_at_.tv_sec = 0;
148 timer_event_->Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000149 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000150 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000151 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000152 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000153
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000154 // Start the timer thread
Peter Boström64c03662015-04-08 11:24:19 +0200155 timer_event_.reset(new EventTimerPosix());
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000156 const char* thread_name = "WebRtc_event_timer_thread";
pbos12411ef2015-11-23 14:47:56 -0800157 timer_thread_ = PlatformThread::CreateThread(Run, this, thread_name);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000158 periodic_ = periodic;
159 time_ = time;
pbos@webrtc.org86639732015-03-13 00:06:21 +0000160 bool started = timer_thread_->Start();
tommi@webrtc.org38492c52015-03-22 14:41:46 +0000161 timer_thread_->SetPriority(kRealtimePriority);
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000162 pthread_mutex_unlock(&mutex_);
163
164 return started;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000165}
166
Peter Boström64c03662015-04-08 11:24:19 +0200167bool EventTimerPosix::Run(void* obj) {
168 return static_cast<EventTimerPosix*>(obj)->Process();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000169}
170
Peter Boström64c03662015-04-08 11:24:19 +0200171bool EventTimerPosix::Process() {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000172 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000173 if (created_at_.tv_sec == 0) {
174#ifndef WEBRTC_MAC
175#ifdef WEBRTC_CLOCK_TYPE_REALTIME
176 clock_gettime(CLOCK_REALTIME, &created_at_);
177#else
178 clock_gettime(CLOCK_MONOTONIC, &created_at_);
179#endif
180#else
181 timeval value;
182 struct timezone time_zone;
183 time_zone.tz_minuteswest = 0;
184 time_zone.tz_dsttime = 0;
185 gettimeofday(&value, &time_zone);
186 TIMEVAL_TO_TIMESPEC(&value, &created_at_);
187#endif
188 count_ = 0;
189 }
190
191 timespec end_at;
192 unsigned long long time = time_ * ++count_;
193 end_at.tv_sec = created_at_.tv_sec + time / 1000;
194 end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6;
195
196 if (end_at.tv_nsec >= E9) {
197 end_at.tv_sec++;
198 end_at.tv_nsec -= E9;
199 }
200
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000201 pthread_mutex_unlock(&mutex_);
pbos@webrtc.orga8463712015-03-17 13:11:15 +0000202 if (timer_event_->Wait(&end_at) == kEventSignaled)
203 return true;
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000204
205 pthread_mutex_lock(&mutex_);
206 if (periodic_ || count_ == 1)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000207 Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000208 pthread_mutex_unlock(&mutex_);
209
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000210 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000211}
212
Peter Boström64c03662015-04-08 11:24:19 +0200213bool EventTimerPosix::StopTimer() {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000214 if (timer_event_) {
215 timer_event_->Set();
216 }
217 if (timer_thread_) {
218 if (!timer_thread_->Stop()) {
219 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000220 }
tommi@webrtc.org361981f2015-03-19 14:44:18 +0000221 timer_thread_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000222 }
Peter Boström64c03662015-04-08 11:24:19 +0200223 timer_event_.reset();
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000224
225 // Set time to zero to force new reference time for the timer.
226 memset(&created_at_, 0, sizeof(created_at_));
227 count_ = 0;
228 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000229}
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000230
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000231} // namespace webrtc