I wrote this class today, but I am trying to figure out how to make it more accurate. I pass in seconds and multiply by 1000 to make it milliseconds, and the time does not line up. I need the ability to have multiple timers, so using timer_gettime()
, which limits one clock per process, is not an option. Also note I borrowed my idea from free glut.
Please feel free to leave general comments on style, but please focus more on how to improve the accuracy of the timer.
#include "TimerManager.h"
#include <iostream>
void func1(int id)
{
std::cout << "I was called with " << std::endl;
}
void func2(int id)
{
std::cout << "I was called too ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))" << std::endl;
}
int main(int argc, char *argv[])
{
TimerManager t;
t.addTimer(6, func1);
t.addTimer(2, func2);
while(true) {
t.start();
}
return 0;
}
#ifndef TIMERMANAGER_H_
#define TIMERMANAGER_H_
#include <stdlib.h>
#include <iostream>
#include <pthread.h>
#include <list>
extern "C" {
void *create_pthread(void *data);
}
class TimerManager {
public:
TimerManager();
~TimerManager();
void start();
void stop();
void addTimer(long usec, void (*callback)(int id));
private:
class Timer
{
public:
Timer(long usec, void (*callback)(int)) :
duration(usec),
callback(callback),
start(0)
{
}
bool operator ==(Timer other)
{
if ((this->callback == other.callback) && (this->duration == other.duration)) {
return true;
}
return false;
}
long duration;
void (*callback)(int);
long start;
};
std::list<Timer> m_timers;
pthread_t m_timer_thread;
Timer setUpTimer(long usec, void (*callback)(int id));
friend void *create_pthread(void *data);
void run();
bool go;
};
#endif
#include <algorithm>
#include <iterator>
#include <sys/time.h>
#include "TimerManager.h"
extern "C" void *create_pthread(void *data)
{
TimerManager *thread_timer_manager = static_cast<TimerManager *>(data);
thread_timer_manager->run();
return data;
}
TimerManager::TimerManager() :
go(false)
{
int thread_creation = pthread_create(&m_timer_thread, NULL, create_pthread, this);
if(thread_creation != 0) {
std::cerr << "Failed to create thread" << std::endl;
return;
} else {
std::cout << "The status flag is " << thread_creation << std::endl;
}
}
TimerManager::~TimerManager()
{
}
void TimerManager::run()
{
struct timeval l_tv;
while(true) {
if (go) {
typedef std::list<Timer>::iterator li;
li begin = m_timers.begin();
li end = m_timers.end();
int item = 0;
for(;begin != end; ++begin) {
gettimeofday(&l_tv, NULL);
long elapsed_time = (l_tv.tv_usec - begin->start);
sleep(1);
if (elapsed_time >= begin->duration) {
//std::cout << "Status of timer " << item << " is " << elapsed_time << " > " << begin->duration << std::endl;
begin->callback(item);
gettimeofday(&l_tv, NULL);
begin->start = l_tv.tv_usec;
} else {
//std::cout << "Status of timer " << item << " is " << elapsed_time << " < " << begin->duration << std::endl;
}
item++;
}
}
}
}
void TimerManager::start()
{
go = true;
}
oid TimerManager::stop()
{
go = false;
}
TimerManager::Timer TimerManager::setUpTimer(long usec, void (*callback)(int id))
{
struct timeval l_tv;
gettimeofday(&l_tv, NULL);
Timer l_t(usec * 100, callback);
l_t.start = l_tv.tv_usec;
std::cout << "Timer created with a values of START: " << l_t.start << " CALLBACK: " << &l_t.callback << " DURATION: " << l_t.duration << std::endl;
return l_t;
}
void TimerManager::addTimer(long usec, void (*callback)(int id))
{
Timer insert = setUpTimer(usec, callback);
typedef std::list<Timer>::iterator li;
li begin = m_timers.begin();
li end = m_timers.end();
for(;begin != end; ++begin) {
if (*begin == *end) {
return;
}
}
m_timers.push_back(insert);
}
Update: Changes after reading answers.
Here is the updated code,
#include "TimerManager.h"
#include <iostream>
#include <fstream>
#include <sys/time.h>
extern "C"
void func1(int id)
{
struct timeval l_tv;
gettimeofday(&l_tv, NULL);
std::cout << "I was called (1) @ " << l_tv.tv_usec << std::endl;
}
extern "C"
void func2(int id)
{
struct timeval l_tv;
gettimeofday(&l_tv, NULL);
std::cout << "I was called (2) @ " << l_tv.tv_usec << std::endl;
}
int main(int, char *[])
{
TimerManager t;
t.addTimer(1000000 / 2, func1);
t.addTimer(1000000 * 4, func2);
t.start();
while(true) {
sleep(1);
}
return 0;
}
#ifndef TIMERMANAGER_H_
#define TIMERMANAGER_H_
#include <stdlib.h>
#include <iostream>
#include <pthread.h>
#include <list>
extern "C" {
void *create_pthread(void *data);
}
class TimerManager {
public:
TimerManager();
~TimerManager();
void start();
void stop();
void addTimer(long usec, void (*callback)(int id));
private:
class Timer
{
public:
Timer(long usec, void (*callback)(int)) :
duration(usec),
callback(callback),
start(0)
{
}
bool operator ==(Timer other)
{
if ((this->callback == other.callback) && (this->duration == other.duration)) {
return true;
}
return false;
}
void operator =(Timer other)
{
duration = other.duration;
callback = other.callback;
start = other.start;
}
suseconds_t duration;
void (*callback)(int);
suseconds_t start;
};
Timer setUpTimer(long micro_duration, void (*callback)(int id));
friend void *create_pthread(void *data);
void run();
bool m_bRunning;
bool m_bGo;
long m_lMinSleep;
std::list<Timer> m_cTimers;
pthread_t m_tTimerThread;
pthread_cond_t m_tGoLockCondition;
pthread_mutex_t m_tGoLock;
};
#endif
#include <algorithm>
#include <iterator>
#include <sys/time.h>
#include "TimerManager.h"
extern "C" void *create_pthread(void *data)
{
TimerManager *thread_timer_manager = static_cast<TimerManager *>(data);
thread_timer_manager->run();
return data;
}
TimerManager::TimerManager() :
m_bRunning(false),
m_bGo(false),
m_lMinSleep(0)
{
int mutex_creation = pthread_mutex_init(&m_tGoLock, NULL);
if(mutex_creation != 0) {
std::cerr << "Failed to create mutex" << std::endl; // Use RAII if resource acquisition fails
return;
}
int mutex_cond_creation = pthread_cond_init(&m_tGoLockCondition, NULL);
if(mutex_cond_creation != 0) {
std::cerr << "Failed to create condition mutex" << std::endl; // Use RAII if resource acquisition fails
return;
}
int thread_creation = pthread_create(&m_tTimerThread, NULL, create_pthread, this); // On success call create_pthread to start tiemr loop
if(thread_creation != 0) {
std::cerr << "Failed to create thread" << std::endl; // Use RAII if resource acquisition fails
return;
}
m_bRunning = true;
}
TimerManager::~TimerManager()
{
pthread_mutex_lock(&m_tGoLock); // m_bRunning access on other thread
m_bRunning = false;
pthread_mutex_unlock(&m_tGoLock);
void *result;
pthread_join(m_tTimerThread, &result); // Do not let calling thread exit before deleting
pthread_mutex_destroy(&m_tGoLock); // Now destroy the mutex (release resources)
pthread_cond_destroy(&m_tGoLockCondition);
}
void TimerManager::run()
{
pthread_mutex_lock(&m_tGoLock); // Timers run on seperate thread
while(m_bRunning) { // While timer manager exists
while (!m_bGo) { // While timer manager told to run
pthread_cond_wait(&m_tGoLockCondition, &m_tGoLock); // Set in the start() member function
}
pthread_mutex_unlock(&m_tGoLock); // Once timer unlocked and mutex released
if (!m_bRunning) { // Make sure timer manager not out of scope
break;
}
struct timeval l_tv;
usleep(std::max(0l, m_lMinSleep));
gettimeofday(&l_tv, NULL); // System call to get time of day
m_lMinSleep = 0; // Used to sleep if no timer to go off soon
long l_lMin = 0; // Used if timer goes off to get actual Min sleep
for(std::list<Timer>::iterator it = m_cTimers.begin(); it != m_cTimers.end(); ++it) { // Iterate over timers to see which one is going off
TimerManager::Timer l_oTimer = *it; // Obtain a copy of the timer
long elapsed_time = ((l_tv.tv_sec * 1000000 + l_tv.tv_usec) - (l_oTimer.start)); // Calcuate the elapsed time from the start of timer X
l_lMin = elapsed_time - l_oTimer.duration; // Minimum time you can sleep in loop
if (elapsed_time >= l_oTimer.duration) { // If time passed is greater than or equal to duration: THEN
l_lMin = l_oTimer.duration; // The minimum you can wait is possibly the entire duration of that timer
l_oTimer.callback(0); // Call the call back
gettimeofday(&l_tv, NULL); // After callback called...
it->start = (l_tv.tv_sec * 1000000) + l_tv.tv_usec; // Start the timer over again
}
m_lMinSleep = std::min(m_lMinSleep, l_lMin); // Find the actual minumum time you can sleep to not lock
}
}
}
void TimerManager::start()
{
pthread_mutex_lock(&m_tGoLock); // Go flag accessed from another thread
m_bGo = true;
pthread_cond_signal(&m_tGoLockCondition);
pthread_mutex_unlock(&m_tGoLock);
}
void TimerManager::stop()
{
pthread_mutex_lock(&m_tGoLock); // Go flag accessed from another thread
m_bGo = false;
pthread_mutex_unlock(&m_tGoLock);
}
TimerManager::Timer TimerManager::setUpTimer(long micro_duration, void (*callback)(int id))
{
struct timeval l_tv;
gettimeofday(&l_tv, NULL); // System call to get the ms and sec since Epoch0
Timer l_oTimer(micro_duration, callback); // Create a timer
l_oTimer.start = (l_tv.tv_sec * 1000000) + l_tv.tv_usec; // Tell the timer when to start
return l_oTimer; // Return a copy to addTimer
}
void TimerManager::addTimer(long usec, void (*callback)(int id))
{
pthread_mutex_lock(&m_tGoLock); // Tell object to wait till timer inserted
Timer insert = setUpTimer(usec, callback);
for (std::list<Timer>::iterator it = m_cTimers.begin(); it != m_cTimers.end(); ++it) {
if (*it == insert) { // Return if timer callback and duration the same
return;
}
}
m_cTimers.push_back(insert); // If no duplicate timers found then insert into list
pthread_mutex_unlock(&m_tGoLock); // Tell object it is okay to proceed
}