Colin Leroy 2011-01-26 04:24:20 PST
authorSam Lantinga <slouken@libsdl.org>
Thu, 27 Jan 2011 00:34:12 -0800
changeset 5106 d547877e355e
parent 5104 5fe0330b0fd6
child 5107 30da7089dcb4
Colin Leroy 2011-01-26 04:24:20 PST the pthread implementation of SDL_SemWaitTimeout() uses busy waiting, while pthread's sem_timedwait() does work. Attached are patches that make use of it
src/thread/pthread/SDL_syssem.c
test/testsem.c
--- a/src/thread/pthread/SDL_syssem.c	Thu Jan 27 00:19:46 2011 -0800
+++ b/src/thread/pthread/SDL_syssem.c	Thu Jan 27 00:34:12 2011 -0800
@@ -21,6 +21,7 @@
 */
 #include "SDL_config.h"
 
+#include <errno.h>
 #include <pthread.h>
 #include <semaphore.h>
 
@@ -102,6 +103,8 @@
 SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
 {
     int retval;
+    struct timeval now;
+    struct timespec ts_timeout;
 
     if (!sem) {
         SDL_SetError("Passed a NULL semaphore");
@@ -116,16 +119,34 @@
         return SDL_SemWait(sem);
     }
 
-    /* Ack!  We have to busy wait... */
-    /* FIXME: Use sem_timedwait()? */
-    timeout += SDL_GetTicks();
+    /* Setup the timeout. sem_timedwait doesn't wait for
+    * a lapse of time, but until we reach a certain time.
+    * This time is now plus the timeout.
+    */
+    gettimeofday(&now, NULL);
+
+    /* Add our timeout to current time */
+    now.tv_usec += (timeout % 1000) * 1000;
+    now.tv_sec += timeout / 1000;
+
+    /* Wrap the second if needed */
+    if ( now.tv_usec >= 1000000 ) {
+        now.tv_usec -= 1000000;
+        now.tv_sec ++;
+    }
+
+    /* Convert to timespec */
+    ts_timeout.tv_sec = now.tv_sec;
+    ts_timeout.tv_nsec = now.tv_usec * 1000;
+
+    /* Wait. */
     do {
-        retval = SDL_SemTryWait(sem);
-        if (retval == 0) {
-            break;
-        }
-        SDL_Delay(1);
-    } while (SDL_GetTicks() < timeout);
+        retval = sem_timedwait(&sem->sem, &ts_timeout);
+    } while (retval < 0 && errno == EINTR);
+
+    if (retval < 0) {
+        SDL_SetError("sem_timedwait() failed");
+    }
 
     return retval;
 }
--- a/test/testsem.c	Thu Jan 27 00:19:46 2011 -0800
+++ b/test/testsem.c	Thu Jan 27 00:34:12 2011 -0800
@@ -39,6 +39,29 @@
     alive = 0;
 }
 
+static void
+TestWaitTimeout(void)
+{
+    Uint32 start_ticks;
+    Uint32 end_ticks;
+    Uint32 duration;
+
+    sem = SDL_CreateSemaphore(0);
+    printf("Waiting 2 seconds on semaphore\n");
+
+    start_ticks = SDL_GetTicks();
+    SDL_SemWaitTimeout(sem, 2000);
+    end_ticks = SDL_GetTicks();
+
+    duration = end_ticks - start_ticks;
+
+    /* Accept a little offset in the effective wait */
+    if (duration > 1900 && duration < 2050)
+        printf("Wait done.\n");
+    else
+        fprintf(stderr, "Wait took %d milliseconds\n", duration);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -81,6 +104,9 @@
     printf("Finished waiting for threads\n");
 
     SDL_DestroySemaphore(sem);
+
+    TestWaitTimeout();
+
     SDL_Quit();
     return (0);
 }