Created
April 11, 2015 21:09
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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