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 "webrtc/system_wrappers/source/event_timer_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 #include "webrtc/base/checks.h"
22 
23 namespace webrtc {
24 
25 // static
Create()26 EventTimerWrapper* EventTimerWrapper::Create() {
27   return new EventTimerPosix();
28 }
29 
30 const long int E6 = 1000000;
31 const long int E9 = 1000 * E6;
32 
EventTimerPosix()33 EventTimerPosix::EventTimerPosix()
34     : event_set_(false),
35       timer_thread_(nullptr),
36       created_at_(),
37       periodic_(false),
38       time_(0),
39       count_(0) {
40   pthread_mutexattr_t attr;
41   pthread_mutexattr_init(&attr);
42   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
43   pthread_mutex_init(&mutex_, &attr);
44 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
45   pthread_cond_init(&cond_, 0);
46 #else
47   pthread_condattr_t cond_attr;
48   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);
52 #endif
53 }
54 
~EventTimerPosix()55 EventTimerPosix::~EventTimerPosix() {
56   StopTimer();
57   pthread_cond_destroy(&cond_);
58   pthread_mutex_destroy(&mutex_);
59 }
60 
61 // TODO(pbos): Make this void.
Set()62 bool EventTimerPosix::Set() {
63   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
64   event_set_ = true;
65   pthread_cond_signal(&cond_);
66   pthread_mutex_unlock(&mutex_);
67   return true;
68 }
69 
Wait(unsigned long timeout)70 EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout) {
71   int ret_val = 0;
72   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
73 
74   if (!event_set_) {
75     if (WEBRTC_EVENT_INFINITE != timeout) {
76       timespec end_at;
77 #ifndef WEBRTC_MAC
78 #ifdef WEBRTC_CLOCK_TYPE_REALTIME
79       clock_gettime(CLOCK_REALTIME, &end_at);
80 #else
81       clock_gettime(CLOCK_MONOTONIC, &end_at);
82 #endif
83 #else
84       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);
90 #endif
91       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       }
98       while (ret_val == 0 && !event_set_)
99         ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at);
100     } else {
101       while (ret_val == 0 && !event_set_)
102         ret_val = pthread_cond_wait(&cond_, &mutex_);
103     }
104   }
105 
106   RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
107 
108   // 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   }
113   pthread_mutex_unlock(&mutex_);
114 
115   return ret_val == 0 ? kEventSignaled : kEventTimeout;
116 }
117 
Wait(timespec * end_at)118 EventTypeWrapper EventTimerPosix::Wait(timespec* end_at) {
119   int ret_val = 0;
120   RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_));
121 
122   while (ret_val == 0 && !event_set_)
123     ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at);
124 
125   RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT);
126 
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   }
132   pthread_mutex_unlock(&mutex_);
133 
134   return ret_val == 0 ? kEventSignaled : kEventTimeout;
135 }
136 
StartTimer(bool periodic,unsigned long time)137 bool EventTimerPosix::StartTimer(bool periodic, unsigned long time) {
138   pthread_mutex_lock(&mutex_);
139   if (timer_thread_) {
140     if (periodic_) {
141       // Timer already started.
142       pthread_mutex_unlock(&mutex_);
143       return false;
144     } else  {
145       // New one shot timer
146       time_ = time;
147       created_at_.tv_sec = 0;
148       timer_event_->Set();
149       pthread_mutex_unlock(&mutex_);
150       return true;
151     }
152   }
153 
154   // Start the timer thread
155   timer_event_.reset(new EventTimerPosix());
156   const char* thread_name = "WebRtc_event_timer_thread";
157   timer_thread_.reset(new rtc::PlatformThread(Run, this, thread_name));
158   periodic_ = periodic;
159   time_ = time;
160   timer_thread_->Start();
161   timer_thread_->SetPriority(rtc::kRealtimePriority);
162   pthread_mutex_unlock(&mutex_);
163 
164   return true;
165 }
166 
Run(void * obj)167 bool EventTimerPosix::Run(void* obj) {
168   return static_cast<EventTimerPosix*>(obj)->Process();
169 }
170 
Process()171 bool EventTimerPosix::Process() {
172   pthread_mutex_lock(&mutex_);
173   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 
201   pthread_mutex_unlock(&mutex_);
202   if (timer_event_->Wait(&end_at) == kEventSignaled)
203     return true;
204 
205   pthread_mutex_lock(&mutex_);
206   if (periodic_ || count_ == 1)
207     Set();
208   pthread_mutex_unlock(&mutex_);
209 
210   return true;
211 }
212 
StopTimer()213 bool EventTimerPosix::StopTimer() {
214   if (timer_event_) {
215     timer_event_->Set();
216   }
217   if (timer_thread_) {
218     timer_thread_->Stop();
219     timer_thread_.reset();
220   }
221   timer_event_.reset();
222 
223   // Set time to zero to force new reference time for the timer.
224   memset(&created_at_, 0, sizeof(created_at_));
225   count_ = 0;
226   return true;
227 }
228 
229 }  // namespace webrtc
230