|  | /* | 
|  | *  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 <errno.h> | 
|  | #include <pthread.h> | 
|  | #include <signal.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <sys/time.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | 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<EventPosix*>(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<EventPosix*>(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 |