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
21namespace webrtc {
22const long int E6 = 1000000;
23const long int E9 = 1000 * E6;
24
25EventWrapper* 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
43EventPosix::EventPosix()
44    : _timerThread(0),
45      _timerEvent(0),
46      _periodic(false),
47      _time(0),
48      _count(0),
49      _state(kDown)
50{
51}
52
53int 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
95EventPosix::~EventPosix()
96{
97    StopTimer();
98    pthread_cond_destroy(&cond);
99    pthread_mutex_destroy(&mutex);
100}
101
102bool 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
113bool 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
126EventTypeWrapper 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
181EventTypeWrapper 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
208bool 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
240bool EventPosix::Run(ThreadObj obj)
241{
242    return static_cast<EventPosix*>(obj)->Process();
243}
244
245bool 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
293bool 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