Skip to content

Commit

Permalink
Implement real periodic timer. (indilib#1387)
Browse files Browse the repository at this point in the history
  • Loading branch information
pawel-soja authored Mar 18, 2021
1 parent 6550c8b commit f6d8ecd
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 50 deletions.
135 changes: 109 additions & 26 deletions eventloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,13 @@ static int lastcb; /* cback index of last cb called */
typedef struct TF
{
double tgo; /* trigger time, ms from epoch */
int interval; /* repeat timer if interval > 0, ms */
void *ud; /* user's data handle */
TCF *fp; /* timer function */
int tid; /* unique id for this timer */
struct TF *next; /* pointer to next item */
} TF;
static TF timefunc_null = {0, NULL, NULL, 0, NULL};
static TF timefunc_null = {0, 0, NULL, NULL, 0, NULL};
static TF *timefunc = &timefunc_null; /* list of timer functions */
static int tid = 0; /* source of unique timer ids */
#define EPOCHDT(tp) /* ms from epoch to timeval *tp */ (((tp)->tv_usec) / 1000.0 + ((tp)->tv_sec) * 1000.0)
Expand Down Expand Up @@ -201,14 +202,30 @@ void rmCallback(int cid)
ncbinuse--;
}

/* insert maintaining sort */
static void insertTimer(TF *node)
{
TF *it = timefunc;

for(; ; it = it->next)
{
if (it->next == NULL || node->tgo < it->next->tgo)
{
node->next = it->next;
it->next = node;
break;
}
}
}

/* register a new timer function, fp, to be called with ud as arg after ms
* milliseconds. add to list in order of increasing time from epoch, ie,
* first entry runs soonest. return id for use with rmTimer().
*/
int addTimer(int ms, TCF *fp, void *ud)
static int addTimerImpl(int delay, int interval, TCF *fp, void *ud)
{
struct timeval t;
TF *node, *it = timefunc;
TF *node;

/* get time now */
gettimeofday(&t, NULL);
Expand All @@ -219,22 +236,48 @@ int addTimer(int ms, TCF *fp, void *ud)
/* init new entry */
node->ud = ud;
node->fp = fp;
node->tgo = EPOCHDT(&t) + ms;
node->tid = ++tid; /* store new unique id */
node->tgo = EPOCHDT(&t) + delay;
node->interval = interval;

/* insert maintaining sort */
for(; ; it = it->next)
insertTimer(node);

return node->tid;
}

int addTimer(int ms, TCF *fp, void *ud)
{
return addTimerImpl(ms, 0, fp, ud);
}

int addPeriodicTimer(int ms, TCF *fp, void *ud)
{
return addTimerImpl(ms, ms, fp, ud);
}

/* find the timer and remove from list */
static TF *dettachTimer(TF *node)
{
TF *it = timefunc;
for(; it->next != NULL; it = it->next)
{
if (it->next == NULL || node->tgo < it->next->tgo)
if (it->next == node)
{
node->next = it->next;
it->next = node;
break;
it->next = node->next;
return node;
}
}
return NULL;
}


/* store and return new unique id */
return (node->tid = ++tid);
/* find the timer by id */
static TF *findTimer(int timer_id)
{
TF *it = timefunc->next;
for(; it != NULL; it = it->next)
if (it->tid == timer_id)
return it;
return NULL;
}

/* remove the timer with the given id, as returned from addTimer().
Expand All @@ -254,6 +297,32 @@ void rmTimer(int timer_id)
}
}

/* Returns the timer's remaining value in milliseconds left until the timeout. */
static double remainingTimerNode(TF *node)
{
struct timeval now;
gettimeofday(&now, NULL);
return (node->tgo - EPOCHDT(&now));
}

/* Returns the timer's remaining value in milliseconds left until the timeout.
* If the timer not exists, the returned value will be -1.
*/
int remainingTimer(int timer_id)
{
TF *it = findTimer(timer_id);
return it == NULL ? -1 : remainingTimerNode(it);
}

/* Returns the timer's remaining value in nanoseconds left until the timeout.
* If the timer not exists, the returned value will be -1.
*/
int64_t nsecsRemainingTimer(int timer_id)
{
TF *it = findTimer(timer_id);
return it == NULL ? -1 : remainingTimerNode(it) * 1000000;
}

/* add a new work procedure, fp, to be called with ud when nothing else to do.
* return unique id for use with rmWorkProc().
*/
Expand Down Expand Up @@ -346,21 +415,23 @@ static void callCallback(fd_set *rfdp)
*/
static void checkTimer()
{
struct timeval now;
double tgonow;
TF *node = timefunc->next;

/* skip if list is empty */
if (node == NULL)
if (node == NULL || remainingTimerNode(node) > 0)
return;

gettimeofday(&now, NULL);
tgonow = EPOCHDT(&now);
(*node->fp)(node->ud);

node = dettachTimer(node);

if (node->tgo <= tgonow)
if (node == NULL)
return;

if (node->interval > 0)
{
timefunc->next = node->next; // remove node from list
(*node->fp)(node->ud);
node->tgo += node->interval;
insertTimer(node);
} else {
free(node);
}
}
Expand Down Expand Up @@ -403,10 +474,7 @@ static void oneLoop()
}
else if (timefunc->next != NULL)
{
struct timeval now;
double late;
gettimeofday(&now, NULL);
late = timefunc->next->tgo - EPOCHDT(&now); /* ms late */
double late = remainingTimerNode(timefunc->next); /* ms late */
if (late < 0)
late = 0;
late /= 1000.0; /* secs late */
Expand Down Expand Up @@ -459,6 +527,21 @@ int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
return (addTimer(millisecs, (TCF *)fp, p));
}

int IEAddPeriodicTimer(int millisecs, IE_TCF *fp, void *p)
{
return (addPeriodicTimer(millisecs, (TCF *)fp, p));
}

int IERemainingTimer(int timerid)
{
return (remainingTimer(timerid));
}

int64_t IENSecsRemainingTimer(int timerid)
{
return (nsecsRemainingTimer(timerid));
}

void IERmTimer(int timerid)
{
rmTimer(timerid);
Expand Down
29 changes: 26 additions & 3 deletions eventloop.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ extern int addWorkProc(WPF *fp, void *ud);
*/
extern void rmWorkProc(int wid);

/** Register a new timer function, \e fp, to be called with \e ud as argument after \e ms. Add to list in order of decreasing time from epoch, ie, last entry runs soonest. The timer will only invoke the callback function \b once. You need to call addTimer again if you want to repeat the process.
/** Register a new single-shot timer function, \e fp, to be called with \e ud as argument after \e ms.
*
* \param ms timer period in milliseconds.
* \param fp a pointer to the callback function.
Expand All @@ -93,9 +93,32 @@ extern void rmWorkProc(int wid);
*/
extern int addTimer(int ms, TCF *fp, void *ud);

/** Remove the timer with the given \e id, as returned from addTimer().
/** Register a new periodic timer function, \e fp, to be called with \e ud as argument after \e ms.
*
* \param tid the timer callback ID returned from addTimer().
* \param ms timer period in milliseconds.
* \param fp a pointer to the callback function.
* \param ud a pointer to be passed to the callback function when called.
* \return a unique id for use with rmTimer().
*/
extern int addPeriodicTimer(int ms, TCF *fp, void *ud);

/** Returns the timer's remaining value in milliseconds left until the timeout.
*
* \param tid the timer callback ID returned from addTimer() or addPeriodicTimer()
* \return If the timer not exists, the returned value will be -1.
*/
extern int remainingTimer(int tid);

/** Returns the timer's remaining value in nanoseconds left until the timeout.
*
* \param tid the timer callback ID returned from addTimer() or addPeriodicTimer()
* \return If the timer not exists, the returned value will be -1.
*/
extern int nsecRemainingTimer(int tid);

/** Remove the timer with the given \e id, as returned from addTimer() or addPeriodicTimer().
*
* \param tid the timer callback ID returned from addTimer() or addPeriodicTimer().
*/
extern void rmTimer(int tid);

Expand Down
31 changes: 26 additions & 5 deletions indidevapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,7 @@ extern int IEAddCallback(int readfiledes, IE_CBF *fp, void *userpointer);
*/
extern void IERmCallback(int callbackid);

/** \brief Register a new timer function, \e fp, to be called with \e ud as argument after \e ms.
Add to list in order of decreasing time from epoch, ie, last entry runs soonest. The timer will only invoke the callback function \b once. You need to call addTimer again if you want to repeat the process.
/** \brief Register a new single-shot timer function, \e fp, to be called with \e ud as argument after \e ms.
*
* \param millisecs timer period in milliseconds.
* \param fp a pointer to the callback function.
Expand All @@ -336,9 +334,32 @@ extern void IERmCallback(int callbackid);
*/
extern int IEAddTimer(int millisecs, IE_TCF *fp, void *userpointer);

/** \brief Remove the timer with the given \e timerid, as returned from IEAddTimer.
/** \brief Register a new periodic timer function, \e fp, to be called with \e ud as argument after \e ms.
*
* \param millisecs timer period in milliseconds.
* \param fp a pointer to the callback function.
* \param userpointer a pointer to be passed to the callback function when called.
* \return a unique id for use with IERmTimer().
*/
extern int IEAddPeriodicTimer(int millisecs, IE_TCF *fp, void *userpointer);

/** \brief Returns the timer's remaining value in milliseconds left until the timeout.
*
* \param timerid the timer callback ID returned from IEAddTimer() or IEAddPeriodicTimer()
* \return If the timer not exists, the returned value will be -1.
*/
extern int IERemainingTimer(int timerid);

/** Returns the timer's remaining value in nanoseconds left until the timeout.
*
* \param tid the timer callback ID returned from addTimer() or addPeriodicTimer()
* \return If the timer not exists, the returned value will be -1.
*/
extern int IENSecRemainingTimer(int tid);

/** \brief Remove the timer with the given \e timerid, as returned from IEAddTimer() or IEAddPeriodicTimer().
*
* \param timerid the timer callback ID returned from IEAddTimer().
* \param timerid the timer callback ID returned from IEAddTimer() or IEAddPeriodicTimer().
*/
extern void IERmTimer(int timerid);

Expand Down
18 changes: 17 additions & 1 deletion libs/indibase/timer/indielapsedtimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ ElapsedTimer::ElapsedTimer()
: d_ptr(new ElapsedTimerPrivate)
{ start(); }

ElapsedTimer::ElapsedTimer(ElapsedTimerPrivate &dd)
: d_ptr(&dd)
{ start(); }

ElapsedTimer::~ElapsedTimer()
{ }

Expand All @@ -48,13 +52,25 @@ int64_t ElapsedTimer::elapsed() const
{
D_PTR(const ElapsedTimer);
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();

return std::chrono::duration_cast<std::chrono::milliseconds>(now - d->start).count();
}

int64_t ElapsedTimer::nsecsElapsed() const
{
D_PTR(const ElapsedTimer);
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - d->start).count();
}

bool ElapsedTimer::hasExpired(int64_t timeout) const
{
return elapsed() > timeout;
}

void ElapsedTimer::nsecsRewind(int64_t nsecs)
{
D_PTR(ElapsedTimer);
d->start += std::chrono::nanoseconds(nsecs);
}

}
11 changes: 8 additions & 3 deletions libs/indibase/timer/indielapsedtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,34 @@ class ElapsedTimerPrivate;
class ElapsedTimer
{
DECLARE_PRIVATE(ElapsedTimer)

public:
ElapsedTimer();
virtual ~ElapsedTimer();

public:

/** @brief Starts this timer. Once started, a timer value can be checked with elapsed(). */
void start();

/** @brief Restarts the timer and returns the number of milliseconds elapsed since the previous start. */
int64_t restart();

public:

/** @brief Returns the number of milliseconds since this ElapsedTimer was last started. */
int64_t elapsed() const;

/** @brief Returns the number of nanoseconds since this ElapsedTimer was last started. */
int64_t nsecsElapsed() const;

/** @brief Returns true if this ElapsedTimer has already expired by timeout milliseconds. */
bool hasExpired(int64_t timeout) const;

public:
/** @brief Rewind elapsed time of nsec nanoseconds */
void nsecsRewind(int64_t nsecs);

protected:
std::unique_ptr<ElapsedTimerPrivate> d_ptr;
ElapsedTimer(ElapsedTimerPrivate &dd);
};

}
Loading

0 comments on commit f6d8ecd

Please sign in to comment.