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 "event_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 namespace webrtc {
22 const long int E6 = 1000000;
23 const long int E9 = 1000 * E6;
24
Create()25 EventWrapper* EventPosix::Create()
26 {
27 EventPosix* ptr = new EventPosix;
28 if (!ptr)
29 {
30 return NULL;
31 }
32
33 const int error = ptr->Construct();
34 if (error)
35 {
36 delete ptr;
37 return NULL;
38 }
39 return ptr;
40 }
41
42
EventPosix()43 EventPosix::EventPosix()
44 : _timerThread(0),
45 _timerEvent(0),
46 _periodic(false),
47 _time(0),
48 _count(0),
49 _state(kDown)
50 {
51 }
52
Construct()53 int EventPosix::Construct()
54 {
55 // Set start time to zero
56 memset(&_tCreate, 0, sizeof(_tCreate));
57
58 int result = pthread_mutex_init(&mutex, 0);
59 if (result != 0)
60 {
61 return -1;
62 }
63 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
64 result = pthread_cond_init(&cond, 0);
65 if (result != 0)
66 {
67 return -1;
68 }
69 #else
70 pthread_condattr_t condAttr;
71 result = pthread_condattr_init(&condAttr);
72 if (result != 0)
73 {
74 return -1;
75 }
76 result = pthread_condattr_setclock(&condAttr, CLOCK_MONOTONIC);
77 if (result != 0)
78 {
79 return -1;
80 }
81 result = pthread_cond_init(&cond, &condAttr);
82 if (result != 0)
83 {
84 return -1;
85 }
86 result = pthread_condattr_destroy(&condAttr);
87 if (result != 0)
88 {
89 return -1;
90 }
91 #endif
92 return 0;
93 }
94
~EventPosix()95 EventPosix::~EventPosix()
96 {
97 StopTimer();
98 pthread_cond_destroy(&cond);
99 pthread_mutex_destroy(&mutex);
100 }
101
Reset()102 bool EventPosix::Reset()
103 {
104 if (0 != pthread_mutex_lock(&mutex))
105 {
106 return false;
107 }
108 _state = kDown;
109 pthread_mutex_unlock(&mutex);
110 return true;
111 }
112
Set()113 bool EventPosix::Set()
114 {
115 if (0 != pthread_mutex_lock(&mutex))
116 {
117 return false;
118 }
119 _state = kUp;
120 // Release all waiting threads
121 pthread_cond_broadcast(&cond);
122 pthread_mutex_unlock(&mutex);
123 return true;
124 }
125
Wait(unsigned long timeout)126 EventTypeWrapper EventPosix::Wait(unsigned long timeout)
127 {
128 int retVal = 0;
129 if (0 != pthread_mutex_lock(&mutex))
130 {
131 return kEventError;
132 }
133
134 if (kDown == _state)
135 {
136 if (WEBRTC_EVENT_INFINITE != timeout)
137 {
138 timespec tEnd;
139 #ifndef WEBRTC_MAC
140 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
141 clock_gettime(CLOCK_REALTIME, &tEnd);
142 #else
143 clock_gettime(CLOCK_MONOTONIC, &tEnd);
144 #endif
145 #else
146 timeval tVal;
147 struct timezone tZone;
148 tZone.tz_minuteswest = 0;
149 tZone.tz_dsttime = 0;
150 gettimeofday(&tVal,&tZone);
151 TIMEVAL_TO_TIMESPEC(&tVal,&tEnd);
152 #endif
153 tEnd.tv_sec += timeout / 1000;
154 tEnd.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6;
155
156 if (tEnd.tv_nsec >= E9)
157 {
158 tEnd.tv_sec++;
159 tEnd.tv_nsec -= E9;
160 }
161 retVal = pthread_cond_timedwait(&cond, &mutex, &tEnd);
162 } else {
163 retVal = pthread_cond_wait(&cond, &mutex);
164 }
165 }
166
167 _state = kDown;
168 pthread_mutex_unlock(&mutex);
169
170 switch(retVal)
171 {
172 case 0:
173 return kEventSignaled;
174 case ETIMEDOUT:
175 return kEventTimeout;
176 default:
177 return kEventError;
178 }
179 }
180
Wait(timespec & tPulse)181 EventTypeWrapper EventPosix::Wait(timespec& tPulse)
182 {
183 int retVal = 0;
184 if (0 != pthread_mutex_lock(&mutex))
185 {
186 return kEventError;
187 }
188
189 if (kUp != _state)
190 {
191 retVal = pthread_cond_timedwait(&cond, &mutex, &tPulse);
192 }
193 _state = kDown;
194
195 pthread_mutex_unlock(&mutex);
196
197 switch(retVal)
198 {
199 case 0:
200 return kEventSignaled;
201 case ETIMEDOUT:
202 return kEventTimeout;
203 default:
204 return kEventError;
205 }
206 }
207
StartTimer(bool periodic,unsigned long time)208 bool EventPosix::StartTimer(bool periodic, unsigned long time)
209 {
210 if (_timerThread)
211 {
212 if(_periodic)
213 {
214 // Timer already started.
215 return false;
216 } else {
217 // New one shot timer
218 _time = time;
219 _tCreate.tv_sec = 0;
220 _timerEvent->Set();
221 return true;
222 }
223 }
224
225 // Start the timer thread
226 _timerEvent = static_cast<EventPosix*>(EventWrapper::Create());
227 const char* threadName = "WebRtc_event_timer_thread";
228 _timerThread = ThreadWrapper::CreateThread(Run, this, kRealtimePriority,
229 threadName);
230 _periodic = periodic;
231 _time = time;
232 unsigned int id = 0;
233 if (_timerThread->Start(id))
234 {
235 return true;
236 }
237 return false;
238 }
239
Run(ThreadObj obj)240 bool EventPosix::Run(ThreadObj obj)
241 {
242 return static_cast<EventPosix*>(obj)->Process();
243 }
244
Process()245 bool EventPosix::Process()
246 {
247 if (_tCreate.tv_sec == 0)
248 {
249 #ifndef WEBRTC_MAC
250 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
251 clock_gettime(CLOCK_REALTIME, &_tCreate);
252 #else
253 clock_gettime(CLOCK_MONOTONIC, &_tCreate);
254 #endif
255 #else
256 timeval tVal;
257 struct timezone tZone;
258 tZone.tz_minuteswest = 0;
259 tZone.tz_dsttime = 0;
260 gettimeofday(&tVal,&tZone);
261 TIMEVAL_TO_TIMESPEC(&tVal,&_tCreate);
262 #endif
263 _count=0;
264 }
265
266 timespec tEnd;
267 unsigned long long time = _time * ++_count;
268 tEnd.tv_sec = _tCreate.tv_sec + time/1000;
269 tEnd.tv_nsec = _tCreate.tv_nsec + (time - (time/1000)*1000)*E6;
270
271 if ( tEnd.tv_nsec >= E9 )
272 {
273 tEnd.tv_sec++;
274 tEnd.tv_nsec -= E9;
275 }
276
277 switch(_timerEvent->Wait(tEnd))
278 {
279 case kEventSignaled:
280 return true;
281 case kEventError:
282 return false;
283 case kEventTimeout:
284 break;
285 }
286 if(_periodic || _count==1)
287 {
288 Set();
289 }
290 return true;
291 }
292
StopTimer()293 bool EventPosix::StopTimer()
294 {
295 if(_timerThread)
296 {
297 _timerThread->SetNotAlive();
298 }
299 if (_timerEvent)
300 {
301 _timerEvent->Set();
302 }
303 if (_timerThread)
304 {
305 if(!_timerThread->Stop())
306 {
307 return false;
308 }
309
310 delete _timerThread;
311 _timerThread = 0;
312 }
313 if (_timerEvent)
314 {
315 delete _timerEvent;
316 _timerEvent = 0;
317 }
318
319 // Set time to zero to force new reference time for the timer.
320 memset(&_tCreate, 0, sizeof(_tCreate));
321 _count=0;
322 return true;
323 }
324 } // namespace webrtc
325