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