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 26 EventTimerWrapper* EventTimerWrapper::Create() { 27 return new EventTimerPosix(); 28 } 29 30 const long int E6 = 1000000; 31 const long int E9 = 1000 * E6; 32 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 55 EventTimerPosix::~EventTimerPosix() { 56 StopTimer(); 57 pthread_cond_destroy(&cond_); 58 pthread_mutex_destroy(&mutex_); 59 } 60 61 // TODO(pbos): Make this void. 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 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 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 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 167 bool EventTimerPosix::Run(void* obj) { 168 return static_cast<EventTimerPosix*>(obj)->Process(); 169 } 170 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 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