/*
* 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