libwinpr-synch: initial waitable timer asynchronous procedure callback support
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Sat, 25 Jan 2014 03:44:23 +0000 (22:44 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Sat, 25 Jan 2014 03:44:23 +0000 (22:44 -0500)
winpr/libwinpr/synch/synch.h
winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c
winpr/libwinpr/synch/timer.c
winpr/libwinpr/synch/wait.c

index 6dbe50b..d41f828 100644 (file)
@@ -86,6 +86,8 @@ struct winpr_timer
        WINPR_HANDLE_DEF();
 
        int fd;
+       BOOL bInit;
+       timer_t tid;
        LONG lPeriod;
        BOOL bManualReset;
        PTIMERAPCROUTINE pfnCompletionRoutine;
@@ -106,6 +108,12 @@ typedef struct winpr_timer_queue WINPR_TIMER_QUEUE;
 struct winpr_timer_queue_timer
 {
        WINPR_HANDLE_DEF();
+
+       ULONG Flags;
+       DWORD DueTime;
+       DWORD Period;
+       PVOID Parameter;
+       WAITORTIMERCALLBACK Callback;
 };
 typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER;
 
index ed34b3e..238472e 100644 (file)
@@ -1,54 +1,68 @@
 
 #include <winpr/crt.h>
 #include <winpr/synch.h>
+#include <winpr/sysinfo.h>
 
-HANDLE gDoneEvent;
+static int g_Count = 0;
+static HANDLE g_Event = NULL;
+
+struct apc_data
+{
+       UINT32 StartTime;
+};
+typedef struct apc_data APC_DATA;
 
 VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
 {
-       int param;
+       APC_DATA* apcData;
+       UINT32 CurrentTime = GetTickCount();
 
        if (!lpArg)
                return;
 
-       param = *((int*) lpArg);
+       apcData = (APC_DATA*) lpArg;
+
+       printf("TimerAPCProc: time: %d\n", CurrentTime - apcData->StartTime);
 
-       printf("TimerAPCProc: %d\n", param);
+       g_Count++;
 
-       SetEvent(gDoneEvent);
+       if (g_Count >= 5)
+       {
+               SetEvent(g_Event);
+       }
 }
 
 int TestSynchWaitableTimerAPC(int argc, char* argv[])
 {
-       int arg = 123;
        HANDLE hTimer;
        BOOL bSuccess;
-       INT64 qwDueTime;
-       LARGE_INTEGER liDueTime;
+       LARGE_INTEGER due;
+       APC_DATA* apcData;
 
+       apcData = (APC_DATA*) malloc(sizeof(APC_DATA));
+       g_Event = CreateEvent(NULL, TRUE, FALSE, NULL);
        hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
 
        if (!hTimer)
                return -1;
 
-       qwDueTime = -5 * 10000000;
-       liDueTime.LowPart = (DWORD) (qwDueTime & 0xFFFFFFFF);
-       liDueTime.HighPart = (LONG) (qwDueTime >> 32);
+       due.QuadPart = -15000000LL; /* 1.5 seconds */
 
-       bSuccess = SetWaitableTimer(hTimer, &liDueTime, 2000, TimerAPCProc, &arg, FALSE);
+       apcData->StartTime = GetTickCount();
+       bSuccess = SetWaitableTimer(hTimer, &due, 2000, TimerAPCProc, apcData, FALSE);
 
        if (!bSuccess)
                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);
-
        CloseHandle(hTimer);
+       CloseHandle(g_Event);
+       free(apcData);
 
        return 0;
 }
index 8ff9f33..34aa418 100644 (file)
 
 #include <winpr/synch.h>
 
+#ifndef _WIN32
+#include <sys/time.h>
+#include <signal.h>
+#endif
+
 #include "synch.h"
 
 #ifndef _WIN32
 
 #include "../handle/handle.h"
 
-/**
- * Waitable Timer
- */
+static BOOL g_WaitableTimerSignalHandlerInstalled = FALSE;
 
-HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCSTR lpTimerName)
+void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg)
 {
-       HANDLE handle = NULL;
-       WINPR_TIMER* timer;
+       WINPR_TIMER* timer = siginfo->si_value.sival_ptr;
 
-       timer = (WINPR_TIMER*) malloc(sizeof(WINPR_TIMER));
+       if (!timer || (signum != SIGALRM))
+               return;
 
-       if (timer)
+       if (timer->pfnCompletionRoutine)
        {
-               int status = 0;
+               timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0);
 
-               WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER);
-               handle = (HANDLE) timer;
+               if (timer->lPeriod)
+               {
+                       timer->timeout.it_interval.tv_sec = (timer->lPeriod / 1000); /* seconds */
+                       timer->timeout.it_interval.tv_nsec = ((timer->lPeriod % 1000) * 1000000); /* nanoseconds */
 
-               timer->fd = -1;
-               timer->lPeriod = 0;
-               timer->bManualReset = bManualReset;
-               timer->pfnCompletionRoutine = NULL;
-               timer->lpArgToCompletionRoutine = NULL;
+                       if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0)
+                       {
+                               perror("timer_settime");
+                       }
+               }
+       }
+}
 
+int InstallWaitableTimerSignalHandler()
+{
+       if (!g_WaitableTimerSignalHandlerInstalled)
+       {
+               struct sigaction action;
+
+               sigemptyset(&action.sa_mask);
+               sigaddset(&action.sa_mask, SIGALRM);
+
+               action.sa_flags = SA_RESTART | SA_SIGINFO;
+               action.sa_sigaction = (void*) &WaitableTimerSignalHandler;
+
+               sigaction(SIGALRM, &action, NULL);
+
+               g_WaitableTimerSignalHandlerInstalled = TRUE;
+       }
+
+       return 0;
+}
+
+int InitializeWaitableTimer(WINPR_TIMER* timer)
+{
+       int status;
+
+       if (!timer->lpArgToCompletionRoutine)
+       {
 #ifdef HAVE_TIMERFD_H
                timer->fd = timerfd_create(CLOCK_MONOTONIC, 0);
+
                if (timer->fd <= 0)
                {
                        free(timer);
-                       return NULL;
+                       return -1;
                }
 
                status = fcntl(timer->fd, F_SETFL, O_NONBLOCK);
@@ -68,11 +102,57 @@ HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManua
                if (status)
                {
                        close(timer->fd);
-                       free(timer);
-                       return NULL;
+                       return -1;
                }
 #endif
        }
+       else
+       {
+               struct sigevent sigev;
+
+               InstallWaitableTimerSignalHandler();
+
+               ZeroMemory(&sigev, sizeof(struct sigevent));
+
+               sigev.sigev_notify = SIGEV_SIGNAL;
+               sigev.sigev_signo = SIGALRM;
+               sigev.sigev_value.sival_ptr = (void*) timer;
+
+               if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0)
+               {
+                       perror("timer_create");
+                       return -1;
+               }
+       }
+
+       timer->bInit = TRUE;
+
+       return 0;
+}
+
+/**
+ * Waitable Timer
+ */
+
+HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCSTR lpTimerName)
+{
+       HANDLE handle = NULL;
+       WINPR_TIMER* timer;
+
+       timer = (WINPR_TIMER*) malloc(sizeof(WINPR_TIMER));
+
+       if (timer)
+       {
+               WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER);
+               handle = (HANDLE) timer;
+
+               timer->fd = -1;
+               timer->lPeriod = 0;
+               timer->bManualReset = bManualReset;
+               timer->pfnCompletionRoutine = NULL;
+               timer->lpArgToCompletionRoutine = NULL;
+               timer->bInit = FALSE;
+       }
 
        return handle;
 }
@@ -124,7 +204,12 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
        timer->pfnCompletionRoutine = pfnCompletionRoutine;
        timer->lpArgToCompletionRoutine = lpArgToCompletionRoutine;
 
-#ifdef HAVE_TIMERFD_H
+       if (!timer->bInit)
+       {
+               if (InitializeWaitableTimer(timer) < 0)
+                       return FALSE;
+       }
+
        ZeroMemory(&(timer->timeout), sizeof(struct itimerspec));
 
        if (lpDueTime->QuadPart < 0)
@@ -163,14 +248,26 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio
                timer->timeout.it_value.tv_nsec = timer->timeout.it_interval.tv_nsec; /* nanoseconds */
        }
 
-       status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL);
+       if (!timer->pfnCompletionRoutine)
+       {
+#ifdef HAVE_TIMERFD_H
+               status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL);
 
-       if (status)
+               if (status)
+               {
+                       printf("SetWaitableTimer timerfd_settime failure: %d\n", status);
+                       return FALSE;
+               }
+#endif
+       }
+       else
        {
-               printf("SetWaitableTimer timerfd_settime failure: %d\n", status);
-               return FALSE;
+               if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0)
+               {
+                       perror("timer_settime");
+                       return FALSE;
+               }
        }
-#endif
 
        return TRUE;
 }
@@ -270,6 +367,12 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue,
        WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER);
        *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer;
 
+       timer->Flags = Flags;
+       timer->DueTime = DueTime;
+       timer->Period = Period;
+       timer->Callback = Callback;
+       timer->Parameter = Parameter;
+
        return TRUE;
 }
 
index 560fed5..eb2268c 100644 (file)
@@ -247,7 +247,9 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                                return WAIT_TIMEOUT;
                }
                else
+               {
                        pthread_mutex_lock(&mutex->mutex);
+               }
        }
        else if (Type == HANDLE_TYPE_EVENT)
        {
@@ -268,8 +270,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                        timeout.tv_usec = (dwMilliseconds % 1000) * 1000;
                }
 
-               status = select(event->pipe_fd[0] + 1, &rfds, NULL, NULL,
-                               (dwMilliseconds == INFINITE) ? NULL : &timeout);
+               do
+               {
+                       status = select(event->pipe_fd[0] + 1, &rfds, NULL, NULL,
+                                       (dwMilliseconds == INFINITE) ? NULL : &timeout);
+               }
+               while (status < 0 && (errno == EINTR));
 
                if (status < 0)
                {
@@ -304,8 +310,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                                timeout.tv_usec = (dwMilliseconds % 1000) * 1000;
                        }
 
-                       status = select(semaphore->pipe_fd[0] + 1, &rfds, 0, 0,
-                                       (dwMilliseconds == INFINITE) ? NULL : &timeout);
+                       do
+                       {
+                               status = select(semaphore->pipe_fd[0] + 1, &rfds, 0, 0,
+                                               (dwMilliseconds == INFINITE) ? NULL : &timeout);
+                       }
+                       while (status < 0 && (errno == EINTR));
 
                        if (status < 0)
                        {
@@ -358,8 +368,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                                timeout.tv_usec = (dwMilliseconds % 1000) * 1000;
                        }
 
-                       status = select(timer->fd + 1, &rfds, 0, 0,
-                                       (dwMilliseconds == INFINITE) ? NULL : &timeout);
+                       do
+                       {
+                               status = select(timer->fd + 1, &rfds, 0, 0,
+                                               (dwMilliseconds == INFINITE) ? NULL : &timeout);
+                       }
+                       while (status < 0 && (errno == EINTR));
 
                        if (status < 0)
                        {
@@ -431,7 +445,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                        status = select(fd + 1, &rfds, NULL, NULL,
                                        (dwMilliseconds == INFINITE) ? NULL : &timeout);
                }
-               while (status < 0 && errno == EINTR);
+               while (status < 0 && (errno == EINTR));
 
                if (status < 0)
                {
@@ -476,7 +490,6 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
                return WAIT_FAILED;
        }
 
-
        maxfd = 0;
        FD_ZERO(&fds);
        ZeroMemory(&timeout, sizeof(timeout));