blob: eeef8095d78fed3c360934443061d490ca911209 [file] [log] [blame]
/*
* 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_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