/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/system_wrappers/source/event_posix.h" #include #include #include #include #include #include #include namespace webrtc { const long int E6 = 1000000; const long int E9 = 1000 * E6; EventWrapper* EventPosix::Create() { EventPosix* ptr = new EventPosix; if (!ptr) { return NULL; } const int error = ptr->Construct(); if (error) { delete ptr; return NULL; } return ptr; } EventPosix::EventPosix() : timer_thread_(0), timer_event_(0), periodic_(false), time_(0), count_(0), state_(kDown) { } int EventPosix::Construct() { // Set start time to zero memset(&created_at_, 0, sizeof(created_at_)); pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); int result = pthread_mutex_init(&mutex_, &attr); if (result != 0) { return -1; } #ifdef WEBRTC_CLOCK_TYPE_REALTIME result = pthread_cond_init(&cond_, 0); if (result != 0) { return -1; } #else pthread_condattr_t cond_attr; result = pthread_condattr_init(&cond_attr); if (result != 0) { return -1; } result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); if (result != 0) { return -1; } result = pthread_cond_init(&cond_, &cond_attr); if (result != 0) { return -1; } result = pthread_condattr_destroy(&cond_attr); if (result != 0) { return -1; } #endif return 0; } EventPosix::~EventPosix() { StopTimer(); pthread_cond_destroy(&cond_); pthread_mutex_destroy(&mutex_); } bool EventPosix::Reset() { if (0 != pthread_mutex_lock(&mutex_)) { return false; } state_ = kDown; pthread_mutex_unlock(&mutex_); return true; } bool EventPosix::Set() { if (0 != pthread_mutex_lock(&mutex_)) { return false; } state_ = kUp; // Release all waiting threads pthread_cond_broadcast(&cond_); pthread_mutex_unlock(&mutex_); return true; } EventTypeWrapper EventPosix::Wait(unsigned long timeout) { int ret_val = 0; if (0 != pthread_mutex_lock(&mutex_)) { return kEventError; } if (kDown == state_) { if (WEBRTC_EVENT_INFINITE != timeout) { timespec end_at; #ifndef WEBRTC_MAC #ifdef WEBRTC_CLOCK_TYPE_REALTIME clock_gettime(CLOCK_REALTIME, &end_at); #else clock_gettime(CLOCK_MONOTONIC, &end_at); #endif #else timeval value; struct timezone time_zone; time_zone.tz_minuteswest = 0; time_zone.tz_dsttime = 0; gettimeofday(&value, &time_zone); TIMEVAL_TO_TIMESPEC(&value, &end_at); #endif end_at.tv_sec += timeout / 1000; end_at.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6; if (end_at.tv_nsec >= E9) { end_at.tv_sec++; end_at.tv_nsec -= E9; } ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at); } else { ret_val = pthread_cond_wait(&cond_, &mutex_); } } state_ = kDown; pthread_mutex_unlock(&mutex_); switch (ret_val) { case 0: return kEventSignaled; case ETIMEDOUT: return kEventTimeout; default: return kEventError; } } EventTypeWrapper EventPosix::Wait(timespec& wake_at) { int ret_val = 0; if (0 != pthread_mutex_lock(&mutex_)) { return kEventError; } if (kUp != state_) { ret_val = pthread_cond_timedwait(&cond_, &mutex_, &wake_at); } state_ = kDown; pthread_mutex_unlock(&mutex_); switch (ret_val) { case 0: return kEventSignaled; case ETIMEDOUT: return kEventTimeout; default: return kEventError; } } bool EventPosix::StartTimer(bool periodic, unsigned long time) { pthread_mutex_lock(&mutex_); if (timer_thread_) { if (periodic_) { // Timer already started. pthread_mutex_unlock(&mutex_); return false; } else { // New one shot timer time_ = time; created_at_.tv_sec = 0; timer_event_->Set(); pthread_mutex_unlock(&mutex_); return true; } } // Start the timer thread timer_event_ = static_cast(EventWrapper::Create()); const char* thread_name = "WebRtc_event_timer_thread"; timer_thread_ = ThreadWrapper::CreateThread(Run, this, kRealtimePriority, thread_name); periodic_ = periodic; time_ = time; unsigned int id = 0; bool started = timer_thread_->Start(id); pthread_mutex_unlock(&mutex_); return started; } bool EventPosix::Run(ThreadObj obj) { return static_cast(obj)->Process(); } bool EventPosix::Process() { pthread_mutex_lock(&mutex_); if (created_at_.tv_sec == 0) { #ifndef WEBRTC_MAC #ifdef WEBRTC_CLOCK_TYPE_REALTIME clock_gettime(CLOCK_REALTIME, &created_at_); #else clock_gettime(CLOCK_MONOTONIC, &created_at_); #endif #else timeval value; struct timezone time_zone; time_zone.tz_minuteswest = 0; time_zone.tz_dsttime = 0; gettimeofday(&value, &time_zone); TIMEVAL_TO_TIMESPEC(&value, &created_at_); #endif count_ = 0; } timespec end_at; unsigned long long time = time_ * ++count_; end_at.tv_sec = created_at_.tv_sec + time / 1000; end_at.tv_nsec = created_at_.tv_nsec + (time - (time / 1000) * 1000) * E6; if (end_at.tv_nsec >= E9) { end_at.tv_sec++; end_at.tv_nsec -= E9; } pthread_mutex_unlock(&mutex_); switch (timer_event_->Wait(end_at)) { case kEventSignaled: return true; case kEventError: return false; case kEventTimeout: break; } pthread_mutex_lock(&mutex_); if (periodic_ || count_ == 1) Set(); pthread_mutex_unlock(&mutex_); return true; } bool EventPosix::StopTimer() { if (timer_thread_) { timer_thread_->SetNotAlive(); } if (timer_event_) { timer_event_->Set(); } if (timer_thread_) { if (!timer_thread_->Stop()) { return false; } delete timer_thread_; timer_thread_ = 0; } if (timer_event_) { delete timer_event_; timer_event_ = 0; } // Set time to zero to force new reference time for the timer. memset(&created_at_, 0, sizeof(created_at_)); count_ = 0; return true; } } // namespace webrtc