libwinpr-synch: make use of timespec for timer queue
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Mon, 27 Jan 2014 01:39:13 +0000 (20:39 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Mon, 27 Jan 2014 01:39:13 +0000 (20:39 -0500)
winpr/libwinpr/synch/synch.h
winpr/libwinpr/synch/test/TestSynchTimerQueue.c
winpr/libwinpr/synch/timer.c

index 557aa4a..ceea862 100644 (file)
@@ -115,11 +115,10 @@ struct winpr_timer_queue
        
        pthread_t thread;
        pthread_attr_t attr;
-       pthread_cond_t cond;
        pthread_mutex_t mutex;
+       pthread_cond_t cond;
+       pthread_mutex_t cond_mutex;
        struct sched_param param;
-       
-       int resolution;
 
        WINPR_TIMER_QUEUE_TIMER* head;
 };
@@ -136,12 +135,11 @@ struct winpr_timer_queue_timer
        WAITORTIMERCALLBACK Callback;
        
        int FireCount;
-       UINT64 StartTime;
-       UINT64 ExpirationTime;
+
+       struct timespec StartTime;
+       struct timespec ExpirationTime;
 
        WINPR_TIMER_QUEUE* timerQueue;
-       
-       WINPR_TIMER_QUEUE_TIMER* prev;
        WINPR_TIMER_QUEUE_TIMER* next;
 };
 
index 96859c0..1c9ad7c 100644 (file)
@@ -5,7 +5,7 @@
 #include <winpr/synch.h>
 
 #define FIRE_COUNT     5
-#define TIMER_COUNT    4
+#define TIMER_COUNT    5
 
 static int g_Count = 0;
 static HANDLE g_Event = NULL;
@@ -13,6 +13,9 @@ static HANDLE g_Event = NULL;
 struct apc_data
 {
        int TimerId;
+       int FireCount;
+       DWORD DueTime;
+       DWORD Period;
        UINT32 StartTime;
 };
 typedef struct apc_data APC_DATA;
@@ -21,6 +24,7 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
 {
        UINT32 TimerTime;
        APC_DATA* apcData;
+       UINT32 expectedTime;
        UINT32 CurrentTime = GetTickCount();
 
        if (!lpParam)
@@ -29,10 +33,12 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
        apcData = (APC_DATA*) lpParam;
 
        TimerTime = CurrentTime - apcData->StartTime;
+       expectedTime = apcData->DueTime + (apcData->Period * apcData->FireCount);
 
-       printf("TimerRoutine: TimerId: %d Time: %d Discrepancy: %d\n",
-                       apcData->TimerId, TimerTime, TimerTime % 100);
+       printf("TimerRoutine: TimerId: %d ActualTime: %d ExpectedTime: %d Discrepancy: %d\n",
+                       apcData->TimerId, TimerTime, expectedTime, TimerTime - expectedTime);
 
+       apcData->FireCount++;
        g_Count++;
 
        if (g_Count >= (TIMER_COUNT * FIRE_COUNT))
@@ -44,8 +50,6 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
 int TestSynchTimerQueue(int argc, char* argv[])
 {
        int index;
-       DWORD DueTime;
-       DWORD Period;
        HANDLE hTimer = NULL;
        HANDLE hTimerQueue = NULL;
        APC_DATA apcData[TIMER_COUNT];
@@ -64,12 +68,12 @@ int TestSynchTimerQueue(int argc, char* argv[])
        {
                apcData[index].TimerId = index;
                apcData[index].StartTime = GetTickCount();
-
-               DueTime = (index * 100) + 500;
-               Period = 1000;
+               apcData[index].DueTime = (index * 100) + 500;
+               apcData[index].Period = 1000;
+               apcData[index].FireCount = 0;
 
                if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine,
-                               &apcData[index], DueTime, Period, 0))
+                               &apcData[index], apcData[index].DueTime, apcData[index].Period, 0))
                {
                        printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
                        return -1;
index 85b5fc2..60e904e 100644 (file)
@@ -326,54 +326,86 @@ BOOL CancelWaitableTimer(HANDLE hTimer)
  * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html
  */
 
-int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER* timer)
+static void timespec_add_ms(struct timespec* tspec, UINT32 ms)
+{
+       UINT64 ns = tspec->tv_nsec + (ms * 1000000);
+       tspec->tv_sec += (ns / 1000000000);
+       tspec->tv_nsec = (ns % 1000000000);
+}
+
+static void timespec_gettimeofday(struct timespec* tspec)
+{
+       struct timeval tval;
+       gettimeofday(&tval, NULL);
+       tspec->tv_sec = tval.tv_sec;
+       tspec->tv_nsec = tval.tv_usec * 1000;
+}
+
+static int timespec_compare(const struct timespec* tspec1, const struct timespec* tspec2)
+{
+       if (tspec1->tv_sec < tspec2->tv_sec)
+               return -1;
+
+       if (tspec1->tv_sec > tspec2->tv_sec)
+               return 1;
+
+       return tspec1->tv_nsec - tspec2->tv_nsec;
+}
+
+static void timespec_copy(struct timespec* dst, struct timespec* src)
+{
+       dst->tv_sec = src->tv_sec;
+       dst->tv_nsec = src->tv_nsec;
+}
+
+void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer)
 {
        WINPR_TIMER_QUEUE_TIMER* node;
-       
-       if (!timerQueue->head)
+
+       if (!(*pHead))
        {
-               timerQueue->head = timer;
-               timer->prev = NULL;
-               timer->next = NULL;
-               return 0;
+               *pHead = timer;
+               return;
        }
-       
-       node = timerQueue->head;
-       
+
+       node = *pHead;
+
        while (node->next)
        {
+               if (timespec_compare(&(timer->ExpirationTime), &(node->ExpirationTime)) < 0)
+                       break;
+
                node = node->next;
        }
-       
+
+       if (node->next)
+               timer->next = node->next->next;
+
        node->next = timer;
-       timer->prev = node;
-       timer->next = NULL;
-       
-       return 0;
 }
 
 int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue)
 {
-       UINT64 currentTime;
+       struct timespec CurrentTime;
        WINPR_TIMER_QUEUE_TIMER* node;
 
        if (!timerQueue->head)
                return 0;
 
-       currentTime = GetTickCount64();
+       timespec_gettimeofday(&CurrentTime);
 
        node = timerQueue->head;
 
        while (node)
        {
-               if (currentTime >= node->ExpirationTime)
+               if (timespec_compare(&CurrentTime, &(node->ExpirationTime)) >= 0)
                {
                        node->Callback(node->Parameter, TRUE);
                        node->FireCount++;
 
                        if (node->Period)
                        {
-                               node->ExpirationTime = node->StartTime + (node->FireCount * node->Period);
+                               timespec_add_ms(&(node->ExpirationTime), node->Period);
                        }
                }
 
@@ -385,12 +417,22 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue)
 
 static void* TimerQueueThread(void* arg)
 {
+       int status;
+       struct timespec tspec;
        WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg;
 
        while (1)
        {
+               pthread_mutex_lock(&(timerQueue->cond_mutex));
+
+               timespec_gettimeofday(&tspec);
+               timespec_add_ms(&tspec, 23);
+
+               status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &tspec);
+
                FireExpiredTimerQueueTimers(timerQueue);
-               Sleep(timerQueue->resolution);
+
+               pthread_mutex_unlock(&(timerQueue->cond_mutex));
        }
 
        return NULL;
@@ -399,6 +441,8 @@ static void* TimerQueueThread(void* arg)
 int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue)
 {
        pthread_cond_init(&(timerQueue->cond), NULL);
+       pthread_mutex_init(&(timerQueue->cond_mutex), NULL);
+
        pthread_mutex_init(&(timerQueue->mutex), NULL);
 
        pthread_attr_init(&(timerQueue->attr));
@@ -421,8 +465,6 @@ HANDLE CreateTimerQueue(void)
        {
                WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE);
                handle = (HANDLE) timerQueue;
-               
-               timerQueue->resolution = 5;
 
                StartTimerQueueThread(timerQueue);
        }
@@ -440,8 +482,12 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
        timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue;
 
        pthread_cond_destroy(&(timerQueue->cond));
+       pthread_mutex_destroy(&(timerQueue->cond_mutex));
+
        pthread_mutex_destroy(&(timerQueue->mutex));
 
+       pthread_attr_destroy(&(timerQueue->attr));
+
        free(timerQueue);
 
        return TRUE;
@@ -455,11 +501,9 @@ BOOL DeleteTimerQueue(HANDLE TimerQueue)
 BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue,
                WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags)
 {
-       UINT64 currentTime;
        WINPR_TIMER_QUEUE* timerQueue;
        WINPR_TIMER_QUEUE_TIMER* timer;
 
-       currentTime = GetTickCount64();
        timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue;
        timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER));
 
@@ -469,6 +513,10 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue,
        WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER);
        *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer;
 
+       timespec_gettimeofday(&(timer->StartTime));
+       timespec_add_ms(&(timer->StartTime), DueTime);
+       timespec_copy(&(timer->ExpirationTime), &(timer->StartTime));
+
        timer->Flags = Flags;
        timer->DueTime = DueTime;
        timer->Period = Period;
@@ -478,10 +526,8 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue,
        timer->timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue;
 
        timer->FireCount = 0;
-       timer->StartTime = currentTime + DueTime;
-       timer->ExpirationTime = timer->StartTime;
 
-       InsertTimerQueueTimer(timerQueue, timer);
+       InsertTimerQueueTimer(&(timerQueue->head), timer);
 
        return TRUE;
 }