libwinpr-synch: don't use timed waits on OS X until they are properly ported
[platform/upstream/freerdp.git] / winpr / libwinpr / synch / wait.c
index a8d9aef..47ca366 100644 (file)
 #endif
 
 #include <assert.h>
+#include <errno.h>
 
 #include <winpr/crt.h>
 #include <winpr/synch.h>
+#include <winpr/platform.h>
 
 #include "synch.h"
 #include "../thread/thread.h"
 
 #ifndef _WIN32
 
+#include <time.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
 #include "../handle/handle.h"
 
+#include "../pipe/pipe.h"
+
+#ifdef __MACH__
+
+#include <mach/mach_time.h>
+
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 0
+
+int clock_gettime(int clk_id, struct timespec *t)
+{
+       UINT64 time;
+       double seconds;
+       double nseconds;
+       mach_timebase_info_data_t timebase;
+
+       mach_timebase_info(&timebase);
+       time = mach_absolute_time();
+
+       nseconds = ((double) time * (double) timebase.numer) / ((double) timebase.denom);
+       seconds = ((double) time * (double) timebase.numer) / ((double) timebase.denom * 1e9);
+
+       t->tv_sec = seconds;
+       t->tv_nsec = nseconds;
+
+       return 0;
+}
+
+#endif
+
+/* Drop in replacement for the linux pthread_timedjoin_np and
+ * pthread_mutex_timedlock functions.
+ */
+#if !defined(HAVE_PTHREAD_GNU_EXT)
+#include <pthread.h>
+static int pthread_timedjoin_np(pthread_t td, void **res,
+               struct timespec *timeout)
+{
+       struct timeval timenow;
+       struct timespec sleepytime;
+       /* This is just to avoid a completely busy wait */
+       sleepytime.tv_sec = 0;
+       sleepytime.tv_nsec = 10000000; /* 10ms */
+
+       do
+       {
+               if (pthread_kill(td, 0))
+                       return pthread_join(td, res);
+
+               nanosleep(&sleepytime, NULL);
+  
+               gettimeofday(&timenow, NULL);
+
+               if (timenow.tv_sec >= timeout->tv_sec &&
+                               (timenow.tv_usec * 1000) >= timeout->tv_nsec)
+               {
+                       return ETIMEDOUT;
+               }
+       }
+       while (TRUE);
+
+       return ETIMEDOUT;
+}
+
+static int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout)
+{
+       struct timeval timenow;
+       struct timespec sleepytime;
+       int retcode;
+
+       /* This is just to avoid a completely busy wait */
+       sleepytime.tv_sec = 0;
+       sleepytime.tv_nsec = 10000000; /* 10ms */
+
+       while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY)
+       {
+               gettimeofday (&timenow, NULL);
+
+               if (timenow.tv_sec >= timeout->tv_sec &&
+                               (timenow.tv_usec * 1000) >= timeout->tv_nsec)
+               {
+                       return ETIMEDOUT;
+               }
+
+               nanosleep (&sleepytime, NULL);
+       }
+
+       return retcode;
+}
+#endif
+
 static void ts_add_ms(struct timespec *ts, DWORD dwMilliseconds)
 {
        ts->tv_sec += dwMilliseconds / 1000L;
@@ -75,46 +172,73 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
 
                if (thread->started)
                {
+#ifdef __linux__
                        if (dwMilliseconds != INFINITE)
                        {
-#if _GNU_SOURCE
                                struct timespec timeout;
 
+                               /* pthread_timedjoin_np returns ETIMEDOUT in case the timeout is 0,
+                                * so set it to the smallest value to get a proper return value. */
+                               if (dwMilliseconds == 0)
+                                       dwMilliseconds ++;
+
                                clock_gettime(CLOCK_REALTIME, &timeout);
                                ts_add_ms(&timeout, dwMilliseconds);
 
                                status = pthread_timedjoin_np(thread->thread, &thread_status, &timeout);
-#else
-                               fprintf(stderr, "[ERROR] %s: Thread timeouts not implemented.\n", __func__);
-                               assert(0);
-#endif
+
+                               if (ETIMEDOUT == status)
+                                       return WAIT_TIMEOUT;
                        }
                        else
+#endif
                                status = pthread_join(thread->thread, &thread_status);
 
                        if (status != 0)
-                               fprintf(stderr, "WaitForSingleObject: pthread_join failure: %d\n", status);
+                       {
+                               fprintf(stderr, "WaitForSingleObject: pthread_join failure: [%d] %s\n",
+                                               status, strerror(status));
+                       }
 
                        if (thread_status)
                                thread->dwExitCode = ((DWORD) (size_t) thread_status);
                }
        }
+       else if (Type == HANDLE_TYPE_PROCESS)
+       {
+               WINPR_PROCESS* process;
+
+               process = (WINPR_PROCESS*) Object;
+
+               if (waitpid(process->pid, &(process->status), 0) != -1)
+               {
+                       return WAIT_FAILED;
+               }
+
+               process->dwExitCode = (DWORD) process->status;
+       }
        else if (Type == HANDLE_TYPE_MUTEX)
        {
                WINPR_MUTEX* mutex;
 
                mutex = (WINPR_MUTEX*) Object;
 
+#ifdef __linux__
                if (dwMilliseconds != INFINITE)
                {
+                       int status;
                        struct timespec timeout;
 
                        clock_gettime(CLOCK_REALTIME, &timeout);
                        ts_add_ms(&timeout, dwMilliseconds);    
 
-                       pthread_mutex_timedlock(&mutex->mutex, &timeout);
+                       status = pthread_mutex_timedlock(&mutex->mutex, &timeout);
+
+                       if (ETIMEDOUT == status)
+                               return WAIT_TIMEOUT;
                }
                else
+#endif
                        pthread_mutex_lock(&mutex->mutex);
        }
        else if (Type == HANDLE_TYPE_EVENT)
@@ -236,6 +360,37 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
                return WAIT_FAILED;
 #endif
        }
+       else if (Type == HANDLE_TYPE_NAMED_PIPE)
+       {
+               int fd;
+               int status;
+               fd_set rfds;
+               struct timeval timeout;
+               WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object;
+
+               fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd;
+
+               if (fd == -1)
+                       return WAIT_FAILED;
+
+               FD_ZERO(&rfds);
+               FD_SET(fd, &rfds);
+               ZeroMemory(&timeout, sizeof(timeout));
+
+               if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0))
+               {
+                       timeout.tv_usec = dwMilliseconds * 1000;
+               }
+
+               status = select(fd + 1, &rfds, NULL, NULL,
+                               (dwMilliseconds == INFINITE) ? NULL : &timeout);
+
+               if (status < 0)
+                       return WAIT_FAILED;
+
+               if (status != 1)
+                       return WAIT_TIMEOUT;
+       }
        else
        {
                fprintf(stderr, "WaitForSingleObject: unknown handle type %lu\n", Type);
@@ -297,6 +452,14 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
                        WINPR_TIMER* timer = (WINPR_TIMER*) Object;
                        fd = timer->fd;
                }
+               else if (Type == HANDLE_TYPE_NAMED_PIPE)
+               {
+                       WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object;
+                       fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd;
+
+                       if (fd == -1)
+                               return WAIT_FAILED;
+               }
                else
                {
                        return WAIT_FAILED;
@@ -342,6 +505,11 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl
                        WINPR_TIMER* timer = (WINPR_TIMER*) Object;
                        fd = timer->fd;
                }
+               else if (Type == HANDLE_TYPE_NAMED_PIPE)
+               {
+                       WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*) Object;
+                       fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd;
+               }
 
                if (FD_ISSET(fd, &fds))
                {