/* * 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 "event_linux.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* EventLinux::Create() { EventLinux* ptr = new EventLinux; if (!ptr) { return NULL; } const int error = ptr->Construct(); if (error) { delete ptr; return NULL; } return ptr; } EventLinux::EventLinux() : _timerThread(0), _timerEvent(0), _periodic(false), _time(0), _count(0), _state(kDown) { } int EventLinux::Construct() { // Set start time to zero memset(&_tCreate, 0, sizeof(_tCreate)); int result = pthread_mutex_init(&mutex, 0); 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 condAttr; result = pthread_condattr_init(&condAttr); if (result != 0) { return -1; } result = pthread_condattr_setclock(&condAttr, CLOCK_MONOTONIC); if (result != 0) { return -1; } result = pthread_cond_init(&cond, &condAttr); if (result != 0) { return -1; } result = pthread_condattr_destroy(&condAttr); if (result != 0) { return -1; } #endif return 0; } EventLinux::~EventLinux() { StopTimer(); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); } bool EventLinux::Reset() { if (0 != pthread_mutex_lock(&mutex)) { return false; } _state = kDown; pthread_mutex_unlock(&mutex); return true; } bool EventLinux::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 EventLinux::Wait(unsigned long timeout) { int retVal = 0; if (0 != pthread_mutex_lock(&mutex)) { return kEventError; } if (kDown == _state) { if (WEBRTC_EVENT_INFINITE != timeout) { timespec tEnd; #ifndef WEBRTC_MAC #ifdef WEBRTC_CLOCK_TYPE_REALTIME clock_gettime(CLOCK_REALTIME, &tEnd); #else clock_gettime(CLOCK_MONOTONIC, &tEnd); #endif #else timeval tVal; struct timezone tZone; tZone.tz_minuteswest = 0; tZone.tz_dsttime = 0; gettimeofday(&tVal,&tZone); TIMEVAL_TO_TIMESPEC(&tVal,&tEnd); #endif tEnd.tv_sec += timeout / 1000; tEnd.tv_nsec += (timeout - (timeout / 1000) * 1000) * E6; if (tEnd.tv_nsec >= E9) { tEnd.tv_sec++; tEnd.tv_nsec -= E9; } retVal = pthread_cond_timedwait(&cond, &mutex, &tEnd); } else { retVal = pthread_cond_wait(&cond, &mutex); } } _state = kDown; pthread_mutex_unlock(&mutex); switch(retVal) { case 0: return kEventSignaled; case ETIMEDOUT: return kEventTimeout; default: return kEventError; } } EventTypeWrapper EventLinux::Wait(timespec& tPulse) { int retVal = 0; if (0 != pthread_mutex_lock(&mutex)) { return kEventError; } if (kUp != _state) { retVal = pthread_cond_timedwait(&cond, &mutex, &tPulse); } _state = kDown; pthread_mutex_unlock(&mutex); switch(retVal) { case 0: return kEventSignaled; case ETIMEDOUT: return kEventTimeout; default: return kEventError; } } bool EventLinux::StartTimer(bool periodic, unsigned long time) { if (_timerThread) { if(_periodic) { // Timer already started. return false; } else { // New one shot timer _time = time; _tCreate.tv_sec = 0; _timerEvent->Set(); return true; } } // Start the timer thread _timerEvent = static_cast<EventLinux*>(EventWrapper::Create()); const char* threadName = "WebRtc_event_timer_thread"; _timerThread = ThreadWrapper::CreateThread(Run, this, kRealtimePriority, threadName); _periodic = periodic; _time = time; unsigned int id = 0; if (_timerThread->Start(id)) { return true; } return false; } bool EventLinux::Run(ThreadObj obj) { return static_cast<EventLinux*>(obj)->Process(); } bool EventLinux::Process() { if (_tCreate.tv_sec == 0) { #ifndef WEBRTC_MAC #ifdef WEBRTC_CLOCK_TYPE_REALTIME clock_gettime(CLOCK_REALTIME, &_tCreate); #else clock_gettime(CLOCK_MONOTONIC, &_tCreate); #endif #else timeval tVal; struct timezone tZone; tZone.tz_minuteswest = 0; tZone.tz_dsttime = 0; gettimeofday(&tVal,&tZone); TIMEVAL_TO_TIMESPEC(&tVal,&_tCreate); #endif _count=0; } timespec tEnd; unsigned long long time = _time * ++_count; tEnd.tv_sec = _tCreate.tv_sec + time/1000; tEnd.tv_nsec = _tCreate.tv_nsec + (time - (time/1000)*1000)*E6; if ( tEnd.tv_nsec >= E9 ) { tEnd.tv_sec++; tEnd.tv_nsec -= E9; } switch(_timerEvent->Wait(tEnd)) { case kEventSignaled: return true; case kEventError: return false; case kEventTimeout: break; } if(_periodic || _count==1) { Set(); } return true; } bool EventLinux::StopTimer() { if(_timerThread) { _timerThread->SetNotAlive(); } if (_timerEvent) { _timerEvent->Set(); } if (_timerThread) { if(!_timerThread->Stop()) { return false; } delete _timerThread; _timerThread = 0; } if (_timerEvent) { delete _timerEvent; _timerEvent = 0; } // Set time to zero to force new reference time for the timer. memset(&_tCreate, 0, sizeof(_tCreate)); _count=0; return true; } } // namespace webrtc