📄 rvtimer.c
字号:
event->id = tqueue->idcounter;
event->starttime = starttime;
event->triggerlength = delay;
event->triggertime = starttime + delay;
event->canceled = RV_FALSE;
event->userdata = userdata;
event->callback = callback;
/* Add the new event to the Priority Queue. */
if(RvPQueuePut(&tqueue->pqueue, event) == NULL) {
RvObjPoolReleaseItem(&tqueue->pool, event);
RvLockRelease(&tqueue->lock);
return RvTimerErrorCode(RV_TIMER_ERROR_QUEUEFULL);
}
/* Fill in the timer identification information. */
if(timer != NULL) {
/* All 4 items guarantee being able to confirm sanity of the timer. */
timer->event = event;
timer->tqueue = tqueue;
timer->starttime = starttime;
timer->id = event->id;
}
RvLockRelease(&tqueue->lock);
return(RV_OK);
}
/* Cancels the requested timer. If blocking is set to RV_TIMER_CANCEL_BLOCKING, cancel */
/* will block until any callback that is currently in progress has completed. When this */
/* function returns RV_OK the timer is guaranteed to have been canceled. If blocking is */
/* RV_TIMER_CANCEL_NONBLOCKING and a callback is in progress, RV_TIMER_WARNING_BUSY is */
/* returned but future triggers of PERIODIC timers are canceled. If RV_TIMER_WARNING_BUSY */
/* is returned, the cancel may be called again on that timer (with or without blocking). */
/* Do not call this on any timer which has been put on a timer queue that has been deleted. */
/* Also, do not call it with blocking set to RV_TIMER_CANCEL_BLOCKING from that timer's callback. */
RVCOREAPI RvStatus RVCALLCONV RvTimerCancel(RvTimer *timer, RvBool blocking)
{
RvStatus result;
RvTimerEvent *event;
RvTimerQueue *tqueue;
#if defined(RV_NULLCHECK)
if(timer == NULL)
return RvTimerErrorCode(RV_ERROR_NULLPTR);
#endif
#if defined(RV_RANGECHECK)
if((blocking != RV_TRUE) && (blocking != RV_FALSE))
return RvTimerErrorCode(RV_ERROR_OUTOFRANGE);
#endif
result = RvLockGet(&timer->tqueue->lock);
if(result != RV_OK)
return result;
event = timer->event;
tqueue = timer->tqueue;
/* Check to insure timer event is still valid. */
if((event->tqueue != tqueue) || (event->starttime != timer->starttime) ||
(event->id != timer->id) || (event->state == RV_TIMER_EVENTSTATE_NOTINUSE)) {
/* Timer isn't valid so just say that cancel worked. */
RvLockRelease(&tqueue->lock);
return RV_OK;
}
if(event->state == RV_TIMER_EVENTSTATE_QUEUED) {
/* Event is in the Priority Queue, just remove it and return it to the pool. */
RvPQueueRemove(&tqueue->pqueue, event->index);
event->state = RV_TIMER_EVENTSTATE_NOTINUSE;
RvObjPoolReleaseItem(&tqueue->pool, event);
RvLockRelease(&tqueue->lock);
return RV_OK;
}
/* The event is in the middle of its callback (TRIGGERED state). */
event->canceled = RV_TRUE; /* Tell periodic timers not to repeat. */
if(blocking == RV_FALSE) {
/* Caller does not want to wait for callback completeion. */
RvLockRelease(&tqueue->lock);
return RvTimerErrorCode(RV_TIMER_WARNING_BUSY);
}
/* Caller wants to wait for timer callback to complete. */
event->waitcount += 1;
RvLockRelease(&tqueue->lock);
result = RvSemaphoreWait(&event->wait);
RvLockGet(&tqueue->lock);
event->waitcount -= 1;
if(event->waitcount == 0) {
/* We're the last one that was waiting so return the event to the pool. */
event->state = RV_TIMER_EVENTSTATE_NOTINUSE;
RvObjPoolReleaseItem(&tqueue->pool, event);
/* If the timer queue is stopped and we were waiting for the */
/* last callback, wake up the task that has been waiting for us. */
if((tqueue->stopped == RV_TRUE) && (tqueue->callcount == 0))
RvSemaphorePost(&tqueue->wait);
}
RvLockRelease(&tqueue->lock);
return result;
}
/* Returns the resoluion of the clock used by timers. */
RvInt64 RvTimerResolution(void)
{
return RvTimestampResolution();
}
/* Check the timer queue and execute the next maxevents callbacks if they */
/* should be triggered. Setting maxevents to 0 indicates to execute as many */
/* events as are available. Will return the actual number of events executed */
/* in the numevents parameter. */
RVCOREAPI RvStatus RVCALLCONV RvTimerQueueService(RvTimerQueue *tqueue, RvSize_t maxevents, RvSize_t *numevents)
{
RvStatus result;
RvTimerEvent *event;
RvBool callbackresult;
RvSize_t i, *eventcount, defaultcount;
RvInt64 curtime;
#if defined(RV_NULLCHECK)
if(tqueue == NULL)
return RvTimerErrorCode(RV_ERROR_NULLPTR);
#endif
/* If numevents is NULL, we still need a counter. */
if(numevents != NULL) {
eventcount = numevents;
} else eventcount = &defaultcount;
*eventcount = 0;
result = RvLockGet(&tqueue->lock);
if(result != RV_OK)
return result;
if(tqueue->stopped == RV_TRUE) {
/* Queue has been stopped. */
RvLockRelease(&tqueue->lock);
return RvTimerErrorCode(RV_TIMER_ERROR_QUEUESTOPPED);
}
curtime = RvTimestampGet();
/* Event processing loop. */
do {
/* Check to see if the event at top of the queue should be triggered. */
event = (RvTimerEvent *)RvPQueuePeek(&tqueue->pqueue);
if((event == NULL) || (event->triggertime > curtime))
break; /* No events to trigger. */
/* Remove that first event and mark it as triggered. */
RvPQueueRemove(&tqueue->pqueue, event->index); /* Faster than PQueueueGet */
event->state = RV_TIMER_EVENTSTATE_TRIGGERED;
*eventcount += 1;
/* Call the users callback. */
tqueue->callcount += 1;
RvLockRelease(&tqueue->lock);
callbackresult = event->callback(event->userdata);
result = RvLockGet(&tqueue->lock);
tqueue->callcount -= 1;
/* Check to see if event is to be rescheduled. */
if((event->timertype == RV_TIMER_TYPE_PERIODIC) && (event->canceled == RV_FALSE) &&
(callbackresult == RV_TRUE) && (tqueue->stopped == RV_FALSE)) {
/* Reschedule the event */
event->state = RV_TIMER_EVENTSTATE_QUEUED;
event->triggertime += event->triggerlength;
if(RvPQueuePut(&tqueue->pqueue, event) == NULL) {
/* We can't reschedule, so just clean up the event. */
event->state = RV_TIMER_EVENTSTATE_NOTINUSE;
RvObjPoolReleaseItem(&tqueue->pool, event);
result = RvTimerErrorCode(RV_TIMER_ERROR_QUEUEFULL);
}
} else {
/* Deal with events that are not to be rescheduled. */
if(event->waitcount > 0) {
/* Tasks are waiting to cancel this event, notify them. */
/* They'll have to clean up the event. */
for(i = 0; i < event->waitcount; i++)
RvSemaphorePost(&event->wait);
} else {
/* No one waiting so we have to clean up the event. */
event->state = RV_TIMER_EVENTSTATE_NOTINUSE;
RvObjPoolReleaseItem(&tqueue->pool, event);
/* If the timer queue is stopped and we are the last callback, */
/* wake up the task that might be waiting for us. */
if((tqueue->stopped == RV_TRUE) && (tqueue->callcount == 0))
RvSemaphorePost(&tqueue->wait);
}
}
} while((result == RV_OK) && (tqueue->stopped == RV_FALSE) &&
((maxevents == 0) || (*eventcount < maxevents))); /* Do more events? */
RvLockRelease(&tqueue->lock);
return result;
}
#if defined(RV_TEST_CODE)
#include "rv64ascii.h"
#include "rvthread.h"
#include "rvstdio.h"
#include <stdlib.h>
#define RvTimerPrintError(_r) RvPrintf("Error %d/%d/%d\n", RvErrorGetLib((_r)), RvErrorGetModule((_r)), RvErrorGetCode((_r)))
static volatile RvBool exitthread;
static RvBool RvTimerTestCallback(void *userdata)
{
RvInt64 curtime;
RvChar timestr[RV_64TOASCII_BUFSIZE];
curtime = RvTimestampGet();
Rv64toA(timestr, curtime);
RvPrintf("Timer Callback at %s, userdata = %p\n", timestr, userdata);
return RV_TRUE;
}
static RvBool RvTimerTestCallback2(void *userdata)
{
RvInt64 curtime;
RvChar timestr[RV_64TOASCII_BUFSIZE];
curtime = RvTimestampGet();
Rv64toA(timestr, curtime);
RvPrintf("Timer Callback at %s, userdata = %p: Delay...\n", timestr, userdata);
RvThreadNanosleep( (RvInt64)Rv64Multiply(RV_TIME64_NSECPERSEC, RvInt64Const(10)) );
RvPrintf("Timer Callback complete.\n");
return RV_TRUE;
}
static void RvTimerTestServiceThread(RvThread *th, void *data)
{
RvTimerQueue *tqueue;
RvStatus result;
RvSize_t numevents, totalevents;
result = RV_OK;
totalevents = 0;
RvPrintf("Timer Queue Service Task (%p) starting.\n", th);
tqueue = (RvTimerQueue *)data;
while((exitthread != RV_TRUE) && (result == RV_OK)){
result = RvTimerQueueService(tqueue, 0, &numevents);
if(result != RV_OK) {
RvPrintf("Thread %p RvTimerQueueService: ", th);
RvTimerPrintError(result);
} else totalevents += numevents;
RvThreadNanosleep( (RvInt64)Rv64Multiply(RV_TIME64_NSECPERMSEC, RvInt64Const(100)) );
}
RvPrintf("Timer Queue Sevice Task (%p) Exiting. Total Events = %u\n", th, totalevents);
}
#define RV_TIMER_TEST_NUMTIMERS 25
void RvTimerTest(void)
{
RvStatus result;
RvTimerQueue tqueue;
RvTimer timer1, timer2, *timerarray;
RvInt64 delay, eventtime;
RvChar timestr[RV_64TOASCII_BUFSIZE];
RvSize_t numevents, i, resultsize;
RvThread th1, th2;
RvBool bresult;
RvCCoreInit();
RvPrintf("RvTimerInit: ");
result = RvTimerInit();
if(result != RV_OK) {
RvTimerPrintError(result);
} else RvPrintf("OK\n");
delay = RvTimerResolution();
Rv64toA(timestr, delay);
RvPrintf("RvTimerResolution = %s\n", timestr);
/* Basic tests */
RvPrintf("RvTimerQueueConstruct(FIXED, 100, 0, 0, 0, 40): ");
result = RvTimerQueueConstruct(&tqueue, RV_TIMER_QTYPE_FIXED, 100, 0, 0, 0, 40, NULL);
if(result != RV_OK) {
RvTimerPrintError(result);
} else RvPrintf("OK\n");
RvPrintf("RvTimerQueueNumEvents = %u.\n", RvTimerQueueNumEvents(&tqueue));
RvPrintf("RvTimerQueueNextEvent = ");
result = RvTimerQueueNextEvent(&tqueue, &eventtime);
if(RvErrorGetCode(result) != RV_TIMER_WARNING_QUEUEEMPTY) {
if(result == RV_OK) {
Rv64toA(timestr, eventtime);
RvPrintf("ERROR, returned %s\n", timestr);
} else RvTimerPrintError(result);
} else RvPrintf("EMPTY. Correct.\n");
RvPrintf("size=%u, max=%u, min=%u, freelevel=%u\n", RvTimerQueueGetSize(&tqueue), RvTimerQueueGetMaxtimers(&tqueue), RvTimerQueueGetMintimers(&tqueue), RvTimerQueueGetFreelevel(&tqueue));
delay = RV_TIME64_NSECPERSEC;
Rv64toA(timestr, delay);
RvPrintf("RvTimerStart(ONESHOT, %s): ", timestr);
result = RvTimerStart(&timer1, &tqueue, RV_TIMER_TYPE_ONESHOT, delay, RvTimerTestCallback, (void *)&timer1);
if(result == RV_OK) {
RvPrintf("OK\n");
Rv64toA(timestr, timer1.starttime);
RvPrintf(" Timer1: id = %u, starttime = %s\n", timer1.id, timestr);
} else RvTimerPrintError(result);
delay *= RvInt64Const(2);
Rv64toA(timestr, delay);
RvPrintf("RvTimerStart(ONESHOT, %s): ", timestr);
result = RvTimerStart(&timer2, &tqueue, RV_TIMER_TYPE_ONESHOT, delay, RvTimerTestCallback, (void *)&timer2);
if(result == RV_OK) {
RvPrintf("OK\n");
Rv64toA(timestr, timer2.starttime);
RvPrintf(" Timer2: id = %u, starttime = %s\n", timer2.id, timestr);
} else RvTimerPrintError(result);
RvPrintf("RvTimerQueueNumEvents = %u.\n", RvTimerQueueNumEvents(&tqueue));
RvPrintf("RvTimerQueueService(0): ");
result = RvTimerQueueService(&tqueue, 0, &numevents);
if(result == RV_OK) {
RvPrintf("OK, numevents = %u\n", numevents);
} else RvTimerPrintError(result);
RvPrintf("RvTimerCancel Timer1 (blocking): ");
result = RvTimerCancel(&timer1, RV_TIMER_CANCEL_BLOCKING);
if(result != RV_OK) {
RvTimerPrintError(result);
} else RvPrintf("OK\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -