blob: c873e3a9da9a7f5b93f0e5b9400664376a4b3b28 [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
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000011#include "webrtc/system_wrappers/source/event_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
niklase@google.com470e71d2011-07-07 08:21:25 +000025const long int E6 = 1000000;
26const long int E9 = 1000 * E6;
27
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000028EventWrapper* EventPosix::Create() {
29 EventPosix* ptr = new EventPosix;
30 if (!ptr) {
31 return NULL;
32 }
niklase@google.com470e71d2011-07-07 08:21:25 +000033
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000034 const int error = ptr->Construct();
35 if (error) {
36 delete ptr;
37 return NULL;
38 }
39 return ptr;
niklase@google.com470e71d2011-07-07 08:21:25 +000040}
41
ajm@google.comb5c49ff2011-08-01 17:04:04 +000042EventPosix::EventPosix()
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000043 : timer_thread_(0),
44 timer_event_(0),
45 periodic_(false),
46 time_(0),
47 count_(0),
48 state_(kDown) {
niklase@google.com470e71d2011-07-07 08:21:25 +000049}
50
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000051int EventPosix::Construct() {
52 // Set start time to zero
53 memset(&created_at_, 0, sizeof(created_at_));
niklase@google.com470e71d2011-07-07 08:21:25 +000054
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +000055 pthread_mutexattr_t attr;
56 pthread_mutexattr_init(&attr);
57 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
58 int result = pthread_mutex_init(&mutex_, &attr);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000059 if (result != 0) {
60 return -1;
61 }
niklase@google.com470e71d2011-07-07 08:21:25 +000062#ifdef WEBRTC_CLOCK_TYPE_REALTIME
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000063 result = pthread_cond_init(&cond_, 0);
64 if (result != 0) {
65 return -1;
66 }
niklase@google.com470e71d2011-07-07 08:21:25 +000067#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000068 pthread_condattr_t cond_attr;
69 result = pthread_condattr_init(&cond_attr);
70 if (result != 0) {
71 return -1;
72 }
73 result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC);
74 if (result != 0) {
75 return -1;
76 }
77 result = pthread_cond_init(&cond_, &cond_attr);
78 if (result != 0) {
79 return -1;
80 }
81 result = pthread_condattr_destroy(&cond_attr);
82 if (result != 0) {
83 return -1;
84 }
niklase@google.com470e71d2011-07-07 08:21:25 +000085#endif
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000086 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000087}
88
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000089EventPosix::~EventPosix() {
90 StopTimer();
91 pthread_cond_destroy(&cond_);
92 pthread_mutex_destroy(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +000093}
94
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +000095bool EventPosix::Set() {
96 if (0 != pthread_mutex_lock(&mutex_)) {
97 return false;
98 }
99 state_ = kUp;
100 // Release all waiting threads
101 pthread_cond_broadcast(&cond_);
102 pthread_mutex_unlock(&mutex_);
103 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000104}
105
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000106EventTypeWrapper EventPosix::Wait(unsigned long timeout) {
107 int ret_val = 0;
108 if (0 != pthread_mutex_lock(&mutex_)) {
109 return kEventError;
110 }
111
112 if (kDown == state_) {
113 if (WEBRTC_EVENT_INFINITE != timeout) {
114 timespec end_at;
niklase@google.com470e71d2011-07-07 08:21:25 +0000115#ifndef WEBRTC_MAC
116#ifdef WEBRTC_CLOCK_TYPE_REALTIME
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000117 clock_gettime(CLOCK_REALTIME, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +0000118#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000119 clock_gettime(CLOCK_MONOTONIC, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +0000120#endif
121#else
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000122 timeval value;
123 struct timezone time_zone;
124 time_zone.tz_minuteswest = 0;
125 time_zone.tz_dsttime = 0;
126 gettimeofday(&value, &time_zone);
127 TIMEVAL_TO_TIMESPEC(&value, &end_at);
niklase@google.com470e71d2011-07-07 08:21:25 +0000128#endif
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000129 end_at.tv_sec += timeout / 1000;
130 end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6;
131
132 if (end_at.tv_nsec >= E9) {
133 end_at.tv_sec++;
134 end_at.tv_nsec -= E9;
135 }
136 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
137 } else {
138 ret_val = pthread_cond_wait(&cond_, &mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000139 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000140 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000141
tommi@webrtc.org30015e32015-02-10 09:33:28 +0000142 // Be careful to only change the state if we're about to report that the
143 // event was signaled.
144 if (ret_val == 0) {
sprang@webrtc.orga6e8ceb2015-02-11 15:19:08 +0000145 // state_ might already be kDown, in case of multiple waiters. That's OK.
tommi@webrtc.org30015e32015-02-10 09:33:28 +0000146 state_ = kDown;
147 }
tommi@webrtc.orgf7e6cfd2015-02-09 18:25:38 +0000148
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000149 pthread_mutex_unlock(&mutex_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000150
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000151 switch (ret_val) {
152 case 0:
153 return kEventSignaled;
154 case ETIMEDOUT:
155 return kEventTimeout;
156 default:
157 return kEventError;
158 }
159}
160
161EventTypeWrapper EventPosix::Wait(timespec& wake_at) {
162 int ret_val = 0;
163 if (0 != pthread_mutex_lock(&mutex_)) {
164 return kEventError;
165 }
166
167 if (kUp != state_) {
168 ret_val = pthread_cond_timedwait(&cond_, &mutex_, &wake_at);
169 }
170 state_ = kDown;
171
172 pthread_mutex_unlock(&mutex_);
173
174 switch (ret_val) {
175 case 0:
176 return kEventSignaled;
177 case ETIMEDOUT:
178 return kEventTimeout;
179 default:
180 return kEventError;
181 }
182}
183
184bool EventPosix::StartTimer(bool periodic, unsigned long time) {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000185 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000186 if (timer_thread_) {
187 if (periodic_) {
188 // Timer already started.
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000189 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000190 return false;
191 } else {
192 // New one shot timer
193 time_ = time;
194 created_at_.tv_sec = 0;
195 timer_event_->Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000196 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000197 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000198 }
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000199 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000200
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000201 // Start the timer thread
202 timer_event_ = static_cast<EventPosix*>(EventWrapper::Create());
203 const char* thread_name = "WebRtc_event_timer_thread";
204 timer_thread_ = ThreadWrapper::CreateThread(Run, this, kRealtimePriority,
205 thread_name);
206 periodic_ = periodic;
207 time_ = time;
pbos@webrtc.org86639732015-03-13 00:06:21 +0000208 bool started = timer_thread_->Start();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000209 pthread_mutex_unlock(&mutex_);
210
211 return started;
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000212}
213
214bool EventPosix::Run(ThreadObj obj) {
215 return static_cast<EventPosix*>(obj)->Process();
216}
217
218bool EventPosix::Process() {
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000219 pthread_mutex_lock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000220 if (created_at_.tv_sec == 0) {
221#ifndef WEBRTC_MAC
222#ifdef WEBRTC_CLOCK_TYPE_REALTIME
223 clock_gettime(CLOCK_REALTIME, &created_at_);
224#else
225 clock_gettime(CLOCK_MONOTONIC, &created_at_);
226#endif
227#else
228 timeval value;
229 struct timezone time_zone;
230 time_zone.tz_minuteswest = 0;
231 time_zone.tz_dsttime = 0;
232 gettimeofday(&value, &time_zone);
233 TIMEVAL_TO_TIMESPEC(&value, &created_at_);
234#endif
235 count_ = 0;
236 }
237
238 timespec end_at;
239 unsigned long long time = time_ * ++count_;
240 end_at.tv_sec = created_at_.tv_sec + time / 1000;
241 end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6;
242
243 if (end_at.tv_nsec >= E9) {
244 end_at.tv_sec++;
245 end_at.tv_nsec -= E9;
246 }
247
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000248 pthread_mutex_unlock(&mutex_);
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000249 switch (timer_event_->Wait(end_at)) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000250 case kEventSignaled:
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000251 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000252 case kEventError:
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000253 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000254 case kEventTimeout:
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000255 break;
256 }
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000257
258 pthread_mutex_lock(&mutex_);
259 if (periodic_ || count_ == 1)
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000260 Set();
pbos@webrtc.orge6dc38e2013-08-20 09:49:19 +0000261 pthread_mutex_unlock(&mutex_);
262
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000263 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000264}
265
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000266bool EventPosix::StopTimer() {
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000267 if (timer_event_) {
268 timer_event_->Set();
269 }
270 if (timer_thread_) {
271 if (!timer_thread_->Stop()) {
272 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000273 }
274
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000275 delete timer_thread_;
276 timer_thread_ = 0;
277 }
278 if (timer_event_) {
279 delete timer_event_;
280 timer_event_ = 0;
281 }
282
283 // Set time to zero to force new reference time for the timer.
284 memset(&created_at_, 0, sizeof(created_at_));
285 count_ = 0;
286 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000287}
phoglund@webrtc.org5bbe0692012-12-10 10:44:37 +0000288
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000289} // namespace webrtc