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