libwinpr-synch: improve timer queue implementation
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Sun, 26 Jan 2014 22:21:12 +0000 (17:21 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Sun, 26 Jan 2014 22:21:12 +0000 (17:21 -0500)
winpr/libwinpr/synch/synch.h
winpr/libwinpr/synch/test/TestSynchTimerQueue.c
winpr/libwinpr/synch/timer.c

index 282d385..557aa4a 100644 (file)
@@ -119,6 +119,8 @@ struct winpr_timer_queue
        pthread_mutex_t mutex;
        struct sched_param param;
        
+       int resolution;
+
        WINPR_TIMER_QUEUE_TIMER* head;
 };
 typedef struct winpr_timer_queue WINPR_TIMER_QUEUE;
@@ -133,6 +135,10 @@ struct winpr_timer_queue_timer
        PVOID Parameter;
        WAITORTIMERCALLBACK Callback;
        
+       int FireCount;
+       UINT64 StartTime;
+       UINT64 ExpirationTime;
+
        WINPR_TIMER_QUEUE* timerQueue;
        
        WINPR_TIMER_QUEUE_TIMER* prev;
index 3400a86..96859c0 100644 (file)
@@ -1,30 +1,56 @@
 
 #include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+
 #include <winpr/synch.h>
 
-HANDLE gDoneEvent;
+#define FIRE_COUNT     5
+#define TIMER_COUNT    4
+
+static int g_Count = 0;
+static HANDLE g_Event = NULL;
+
+struct apc_data
+{
+       int TimerId;
+       UINT32 StartTime;
+};
+typedef struct apc_data APC_DATA;
 
 VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
 {
-       int param;
+       UINT32 TimerTime;
+       APC_DATA* apcData;
+       UINT32 CurrentTime = GetTickCount();
 
        if (!lpParam)
                return;
 
-       param = *((int*) lpParam);
+       apcData = (APC_DATA*) lpParam;
+
+       TimerTime = CurrentTime - apcData->StartTime;
+
+       printf("TimerRoutine: TimerId: %d Time: %d Discrepancy: %d\n",
+                       apcData->TimerId, TimerTime, TimerTime % 100);
 
-       printf("TimerRoutine: Param: %d TimerOrWaitFired: %d\n", param, TimerOrWaitFired);
+       g_Count++;
 
-       SetEvent(gDoneEvent);
+       if (g_Count >= (TIMER_COUNT * FIRE_COUNT))
+       {
+               SetEvent(g_Event);
+       }
 }
 
 int TestSynchTimerQueue(int argc, char* argv[])
 {
-       int arg = 123;
+       int index;
+       DWORD DueTime;
+       DWORD Period;
        HANDLE hTimer = NULL;
        HANDLE hTimerQueue = NULL;
+       APC_DATA apcData[TIMER_COUNT];
 
-       gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       g_Event = CreateEvent(NULL, TRUE, FALSE, NULL);
 
        hTimerQueue = CreateTimerQueue();
 
@@ -34,20 +60,28 @@ int TestSynchTimerQueue(int argc, char* argv[])
                return -1;
        }
 
-       if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, &arg , 1000, 0, 0))
+       for (index = 0; index < TIMER_COUNT; index++)
        {
-               printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
-               return -1;
+               apcData[index].TimerId = index;
+               apcData[index].StartTime = GetTickCount();
+
+               DueTime = (index * 100) + 500;
+               Period = 1000;
+
+               if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine,
+                               &apcData[index], DueTime, Period, 0))
+               {
+                       printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
+                       return -1;
+               }
        }
 
-       if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0)
+       if (WaitForSingleObject(g_Event, INFINITE) != WAIT_OBJECT_0)
        {
                printf("WaitForSingleObject failed (%d)\n", GetLastError());
                return -1;
        }
 
-       CloseHandle(gDoneEvent);
-
        if (!DeleteTimerQueueTimer(hTimerQueue, hTimer, NULL))
        {
                printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError());
@@ -60,5 +94,7 @@ int TestSynchTimerQueue(int argc, char* argv[])
                return -1;
        }
 
+       CloseHandle(g_Event);
+
        return 0;
 }
index e1afedc..85b5fc2 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include <winpr/crt.h>
+#include <winpr/sysinfo.h>
 
 #include <winpr/synch.h>
 
@@ -325,27 +326,6 @@ BOOL CancelWaitableTimer(HANDLE hTimer)
  * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html
  */
 
-static void* TimerQueueThread(void* arg)
-{
-       //WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg;
-       
-       return NULL;
-}
-
-int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue)
-{
-       pthread_cond_init(&(timerQueue->cond), NULL);
-       pthread_mutex_init(&(timerQueue->mutex), NULL);
-       
-       pthread_attr_init(&(timerQueue->attr));
-       timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO);
-       pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param));
-       pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO);
-       pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue);
-       
-       return 0;
-}
-
 int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER* timer)
 {
        WINPR_TIMER_QUEUE_TIMER* node;
@@ -360,11 +340,10 @@ int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER
        
        node = timerQueue->head;
        
-       do
+       while (node->next)
        {
                node = node->next;
        }
-       while (node->next);
        
        node->next = timer;
        timer->prev = node;
@@ -373,6 +352,64 @@ int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER
        return 0;
 }
 
+int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue)
+{
+       UINT64 currentTime;
+       WINPR_TIMER_QUEUE_TIMER* node;
+
+       if (!timerQueue->head)
+               return 0;
+
+       currentTime = GetTickCount64();
+
+       node = timerQueue->head;
+
+       while (node)
+       {
+               if (currentTime >= node->ExpirationTime)
+               {
+                       node->Callback(node->Parameter, TRUE);
+                       node->FireCount++;
+
+                       if (node->Period)
+                       {
+                               node->ExpirationTime = node->StartTime + (node->FireCount * node->Period);
+                       }
+               }
+
+               node = node->next;
+       }
+
+       return 0;
+}
+
+static void* TimerQueueThread(void* arg)
+{
+       WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg;
+
+       while (1)
+       {
+               FireExpiredTimerQueueTimers(timerQueue);
+               Sleep(timerQueue->resolution);
+       }
+
+       return NULL;
+}
+
+int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue)
+{
+       pthread_cond_init(&(timerQueue->cond), NULL);
+       pthread_mutex_init(&(timerQueue->mutex), NULL);
+
+       pthread_attr_init(&(timerQueue->attr));
+       timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO);
+       pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param));
+       pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO);
+       pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue);
+
+       return 0;
+}
+
 HANDLE CreateTimerQueue(void)
 {
        HANDLE handle = NULL;
@@ -385,13 +422,15 @@ HANDLE CreateTimerQueue(void)
                WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE);
                handle = (HANDLE) timerQueue;
                
+               timerQueue->resolution = 5;
+
                StartTimerQueueThread(timerQueue);
        }
 
        return handle;
 }
 
-BOOL DeleteTimerQueue(HANDLE TimerQueue)
+BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
 {
        WINPR_TIMER_QUEUE* timerQueue;
 
@@ -400,35 +439,31 @@ BOOL DeleteTimerQueue(HANDLE TimerQueue)
 
        timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue;
 
+       pthread_cond_destroy(&(timerQueue->cond));
+       pthread_mutex_destroy(&(timerQueue->mutex));
+
        free(timerQueue);
 
        return TRUE;
 }
 
-BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent)
+BOOL DeleteTimerQueue(HANDLE TimerQueue)
 {
-       WINPR_TIMER_QUEUE* timerQueue;
-
-       if (!TimerQueue)
-               return FALSE;
-
-       timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue;
-
-       free(timerQueue);
-
-       return TRUE;
+       return DeleteTimerQueueEx(TimerQueue, NULL);
 }
 
 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));
 
-       if (timer || !TimerQueue)
+       if (!timer || !TimerQueue)
                return FALSE;
 
        WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER);
@@ -442,6 +477,12 @@ 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);
+
        return TRUE;
 }