Skip to content

Instantly share code, notes, and snippets.

@iscgar
Created April 11, 2015 21:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iscgar/c1af1be0ba8d6e1999fc to your computer and use it in GitHub Desktop.
Save iscgar/c1af1be0ba8d6e1999fc to your computer and use it in GitHub Desktop.
A simple absolute timer wrapper for Linux intended to be consumed by a single thread with timed wait.
/**
* @file AbsoluteTimer.cpp
*
* A simple absolute timer wrapper implementation for Linux (can be adjusted for other OSs easily).
*
* @author Isaac Garzon
* @since 11/04/2015
*/
#if !defined(__linux__)
# error AbsoluteTimer is implemented for linux only
#else // defined(__linux__)
# include <linux/version.h>
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
# error AbsoluteTimer requires kernel version >= 2.6.28
# endif // LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
#endif // defined(__linux__)
#include <time.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include "AbsoluteTimer.h"
#define INVALID_FD -1
namespace utils
{
AbsoluteTimer::AbsoluteTimer() :
m_start(0),
m_timer(INVALID_FD),
m_poll(INVALID_FD),
m_event((::eventfd)(0, 0)),
m_running(false)
{
int flags;
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = this->m_timer;
if ((this->m_event != INVALID_FD) &&
// Make sure we can poll
(((this->m_poll = (::epoll_create)(1)) == INVALID_FD) ||
((this->m_timer = (::timerfd_create)(CLOCK_MONOTONIC_RAW, 0)) == INVALID_FD) ||
// And that read will never block
((flags = (::fcntl)(this->m_timer, F_GETFL, 0)) < 0) ||
((::fcntl)(this->m_timer, F_SETFL, flags | O_NONBLOCK) != 0) ||
((flags = (::fcntl)(this->m_event, F_GETFL, 0)) < 0) ||
((::fcntl)(this->m_event, F_SETFL, flags | O_NONBLOCK) != 0) ||
// And that poll listeners are in place
((::epoll_ctl)(this->m_poll, EPOLL_CTL_ADD, this->m_timer, &ev) != 0) ||
((ev.data.fd = this->m_event),
((::epoll_ctl)(this->m_poll, EPOLL_CTL_ADD, this->m_event, &ev) != 0))))
{
this->Destroy();
}
}
AbsoluteTimer::~AbsoluteTimer()
{
// Destroy only if there's something to destroy
if (this->m_event != INVALID_FD)
{
this->Destroy();
}
}
void AbsoluteTimer::Destroy()
{
this->Stop();
(::close)(this->m_event);
this->m_event = INVALID_FD;
(::close)(this->m_timer);
this->m_timer = INVALID_FD;
}
bool AbsoluteTimer::Start()
{
if ((this->m_event == INVALID_FD) ||
(this->IsRunning()))
{
return false;
}
uint64_t val;
// Discard old event and timer data
(::read)(this->m_event, &val, sizeof(val));
(::read)(this->m_timer, &val, sizeof(val));
struct timespec start_time;
if ((::clock_gettime)(CLOCK_MONOTONIC_RAW, &start_time) != 0)
{
return false;
}
this->m_start = (start_time.tv_sec * 1000) + (start_time.tv_nsec * 1000000);
return (this->m_running = true);
}
void AbsoluteTimer::Stop()
{
// Stop only if currently running
if (this->IsRunning())
{
this->m_running = false;
// Clear poll
uint64_t val = 1;
(::write)(this->m_event, &val, sizeof(val));
}
}
bool AbsoluteTimer::Wait(uint64_t Millis)
{
if (!this->IsRunning())
{
return false;
}
if (Millis > 0)
{
uint64_t abs_time = this->m_start + Millis;
struct timespec wait_time;
wait_time.tv_sec = abs_time / 1000;
wait_time.tv_nsec = (abs_time % 1000) * 1000000;
struct epoll_event ev;
if (((::timerfd_settime)(this->m_timer, TFD_TIMER_ABSTIME, &wait_time, NULL) != 0) ||
((::epoll_wait)(this->m_poll, &ev, 1, Millis) <= 0))
{
return false;
}
uint64_t val;
// Check if aborted
if ((!this->IsRunning()) ||
((::read)(this->m_timer, &val, sizeof(val)) != sizeof(val)) ||
(!val))
{
return false;
}
this->m_start = abs_time;
}
return true;
}
} // namespace utils
/**
* @file AbsoluteTimer.h
*
* Declarations and definitions for a simple absolute timer wrapper class.
*
* @author Isaac Garzon
* @since 11/04/2015
*/
#ifndef _UTILS_ABSOLUTE_TIMER_H
#define _UTILS_ABSOLUTE_TIMER_H
#include <stdint.h>
namespace utils
{
/**
* A simple timer wrapper class.
*/
class AbsoluteTimer
{
public:
/**
* AbsoluteTimer constructor.
*/
AbsoluteTimer();
/**
* Default destructor.
*/
~AbsoluteTimer();
/**
* Provides an indication if the timer is currently operating.
*
* @return True if the timer has been started. False otherwise.
*/
inline bool IsRunning() const
{
return this->m_running;
}
/**
* Starts the timer.
* Requires IsRunning() == false.
*
* @return True if the timer is initialized and started successfully. False otherwise.
*/
bool Start();
/**
* Stops the timer.
*
* @note Has no effect if IsRunning() == false
*/
void Stop();
/**
* Waits for the timer to expire.
*
* @note This function is not meant to be called by more than one thread at a time.
*
* @param Millis The amount of milliseconds to wait for the timer to expire.
*
* @return True if the timer expired. False otherwise.
*/
bool Wait(uint64_t Millis);
private:
/**
* Destroys the timer.
*/
void Destroy();
uint64_t m_start;
int m_timer;
int m_poll;
int m_event;
bool m_running;
}; // class AbsoluteTimer
} // namespace utils
#endif // !_UTILS_ABSOLUTE_TIMER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment