Semaphores in SDL 1.1

Here is a patch for POSIX-like counting semaphores in SDL. There are two
implementations : a generic one that works on top of SDL mutexes and should be
portable across all platforms, and another one that relies on the POSIX
1003.1b sem_* functions. You can chose to use either one with the
–enable-native-sem configure flag. Of course it would be nice to have other
native implementations (Win32, MacOS, Be…).

The API is pretty much the same as the POSIX one :

/* Create a semaphore, initialized with value */
extern DECLSPEC SDL_sem * SDL_CreateSemaphore(Uint32 value);

/* Destroy a semaphore */
extern DECLSPEC void SDL_DestroySemaphore(SDL_sem *sem);

/* Suspend the calling thread until the semaphore count is non-zero,
then decreases the counter
*/
extern DECLSPEC SDL_bool SDL_SemWait(SDL_sem *sem);

/* Non-blocking variant of SDL_SemWait(), returns SDL_FALSE if counter is zero
*/
extern DECLSPEC SDL_bool SDL_SemTryWait(SDL_sem *sem);

/* Returns the current count of the semaphore */
extern DECLSPEC Uint32 SDL_SemGetValue(SDL_sem *sem);

/* Atomically increases the semaphore’s count (not blocking) */
extern DECLSPEC SDL_bool SDL_SemPost(SDL_sem *sem);

If that patch looks good, let me know so that I commit it. The next logical
step would be to add that kind of functionality for condition variables (that
would actually have made the generic semaphore implementation cleaner). Maybe
another day :wink:

Sim City 3000 is going to use SDL semaphores right now to avoid problems with
glibc breaking compatibility of POSIX semaphores between minor releases… :-/–
Stephane Peter
Programmer
Loki Entertainment Software

“Microsoft has done to computers what McDonald’s has done to gastronomy”
-------------- next part --------------
Index: configure.in

RCS file: /cvs/SDL/configure.in,v
retrieving revision 1.25.2.48
diff -u -r1.25.2.48 configure.in
configure.in 2000/04/15 01:40:49 1.25.2.48
+++ configure.in 2000/04/19 00:47:39
@@ -545,6 +545,15 @@
AM_CONDITIONAL(USE_CLONE, test x$use_clone = xyes)
}

+dnl Check if we want to include native semaphore code
+CheckSEM()
+{

  • AC_ARG_ENABLE(native-sem,
    +[ --enable-native-sem Use native semaphores [default=yes]],
  •   , enable_native_sem=yes)
    
  • AM_CONDITIONAL(USE_NATIVE_SEM, test x$enable_native_sem = xyes)
    +}

dnl Determine whether the compiler can produce Win32 executables
CheckWIN32()
{
@@ -635,6 +644,7 @@
CheckAAlib
CheckOpenGL
CheckPTHREAD

  •   CheckSEM
       # Set up files for the main() stub
       COPY_ARCH_SRC(src/main, linux, SDL_main.c)
       # Set up files for the audio library
    

@@ -655,6 +665,7 @@
fi
COPY_ARCH_SRC(src/thread, linux, SDL_mutex.c)
COPY_ARCH_SRC(src/thread, linux, SDL_systhread.c)

  •        COPY_ARCH_SRC(src/thread, linux, SDL_syssemaphore.c)
           COPY_ARCH_SRC(src/thread, linux, SDL_systhread_c.h)
       fi
       # Set up files for the timer library
    

@@ -670,6 +681,7 @@
CheckOpenGL
CheckPTHREAD
CheckKSTAT

  •   CheckSEM
       # Set up files for the main() stub
       COPY_ARCH_SRC(src/main, linux, SDL_main.c)
       # Set up files for the audio library
    

@@ -688,6 +700,7 @@
if test x$enable_threads = xyes; then
COPY_ARCH_SRC(src/thread, linux, SDL_mutex.c)
COPY_ARCH_SRC(src/thread, linux, SDL_systhread.c)

  •        COPY_ARCH_SRC(src/thread, linux, SDL_syssemaphore.c)
           COPY_ARCH_SRC(src/thread, linux, SDL_systhread_c.h)
       fi
       # Set up files for the timer library
    

@@ -732,6 +745,7 @@
CheckAAlib
CheckOpenGL
CheckPTHREAD

  •   CheckSEM
       # Set up files for the main() stub
       COPY_ARCH_SRC(src/main, linux, SDL_main.c)
       # Set up files for the audio library
    

@@ -750,6 +764,7 @@
if test x$enable_threads = xyes; then
COPY_ARCH_SRC(src/thread, linux, SDL_mutex.c)
COPY_ARCH_SRC(src/thread, linux, SDL_systhread.c)

  •        COPY_ARCH_SRC(src/thread, linux, SDL_syssemaphore.c)
           COPY_ARCH_SRC(src/thread, linux, SDL_systhread_c.h)
       fi
       # Set up files for the timer library
    

@@ -764,6 +779,7 @@
CheckAAlib
CheckOpenGL
CheckPTHREAD

  •   CheckSEM
       # Set up files for the main() stub
       COPY_ARCH_SRC(src/main, linux, SDL_main.c)
       # Set up files for the audio library
    

@@ -785,6 +801,7 @@
if test x$enable_threads = xyes; then
COPY_ARCH_SRC(src/thread, linux, SDL_mutex.c)
COPY_ARCH_SRC(src/thread, linux, SDL_systhread.c)

  •        COPY_ARCH_SRC(src/thread, linux, SDL_syssemaphore.c)
           COPY_ARCH_SRC(src/thread, linux, SDL_systhread_c.h)
       fi
       # Set up files for the timer library
    

@@ -802,6 +819,7 @@
CheckAAlib
CheckOpenGL
CheckPTHREAD

  •   CheckSEM
       # Set up files for the main() stub
       COPY_ARCH_SRC(src/main, linux, SDL_main.c)
       # Set up files for the audio library
    

@@ -820,6 +838,7 @@
if test x$enable_threads = xyes; then
COPY_ARCH_SRC(src/thread, linux, SDL_mutex.c)
COPY_ARCH_SRC(src/thread, linux, SDL_systhread.c)

  •        COPY_ARCH_SRC(src/thread, linux, SDL_syssemaphore.c)
           COPY_ARCH_SRC(src/thread, linux, SDL_systhread_c.h)
       fi
       # Set up files for the timer library
    

Index: include/Makefile.am

RCS file: /cvs/SDL/include/Makefile.am,v
retrieving revision 1.3.2.1
diff -u -r1.3.2.1 Makefile.am
— include/Makefile.am 2000/01/05 19:30:59 1.3.2.1
+++ include/Makefile.am 2000/04/19 00:47:39
@@ -20,6 +20,7 @@
SDL_main.h
SDL_mouse.h
SDL_mutex.h \

  • SDL_semaphore.h
    SDL_quit.h
    SDL_rwops.h
    SDL_syswm.h
    Index: include/SDL_semaphore.h
    ===================================================================
    RCS file: SDL_semaphore.h
    diff -N SDL_semaphore.h
    — /dev/null Tue May 5 13:32:27 1998
    +++ SDL_semaphore.h Tue Apr 18 17:47:39 2000
    @@ -0,0 +1,71 @@
    +/*
  • SDL - Simple DirectMedia Layer
  • Copyright © 1997, 1998, 1999, 2000 Sam Lantinga
  • This library is free software; you can redistribute it and/or
  • modify it under the terms of the GNU Library General Public
  • License as published by the Free Software Foundation; either
  • version 2 of the License, or (at your option) any later version.
  • This library is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  • Library General Public License for more details.
  • You should have received a copy of the GNU Library General Public
  • License along with this library; if not, write to the Free
  • Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  • Sam Lantinga
  • slouken at devolution.com
    +*/

+#ifdef SAVE_RCSID
+static char rcsid =

  • “@(#) $Id: SDL_mutex.h,v 1.5.2.1 2000/03/16 15:20:37 hercules Exp $”;
    +#endif

+#ifndef _SDL_semaphore_h
+#define _SDL_semaphore_h
+
+#include “SDL_types.h”
+
+/* Counting semaphores /
+
+#include “begin_code.h”
+/
Set up for C function definitions, even when using C++ /
+#ifdef __cplusplus
+extern “C” {
+#endif
+
+/
The SDL semaphore structure, defined in SDL_semaphore.c /
+struct SDL_semaphore;
+typedef struct SDL_semaphore SDL_sem;
+
+/
Create a semaphore, initialized with value /
+extern DECLSPEC SDL_sem * SDL_CreateSemaphore(Uint32 value);
+
+/
Destroy a semaphore */
+extern DECLSPEC void SDL_DestroySemaphore(SDL_sem sem);
+
+/
Suspend the calling thread until the semaphore count is non-zero,

  • then decreases the counter
  • */
    +extern DECLSPEC SDL_bool SDL_SemWait(SDL_sem *sem);

+/* Non-blocking variant of SDL_SemWait(), returns SDL_FALSE if counter is zero
*/
+extern DECLSPEC SDL_bool SDL_SemTryWait(SDL_sem sem);
+
+/
Returns the current count of the semaphore */
+extern DECLSPEC Uint32 SDL_SemGetValue(SDL_sem sem);
+
+/
Atomically increases the semaphore’s count (not blocking) */
+extern DECLSPEC SDL_bool SDL_SemPost(SDL_sem sem);
+
+/
Ends C function definitions when using C++ /
+#ifdef __cplusplus
+};
+#endif
+#include “close_code.h”
+
+#endif /
_SDL_semaphore_h */
Index: src/thread/Makefile.am

RCS file: /cvs/SDL/src/thread/Makefile.am,v
retrieving revision 1.1.1.1.2.1
diff -u -r1.1.1.1.2.1 Makefile.am
— src/thread/Makefile.am 2000/01/24 03:32:46 1.1.1.1.2.1
+++ src/thread/Makefile.am 2000/04/19 00:47:39
@@ -13,17 +13,34 @@
endif

Include the architecture-independent sources

+if USE_NATIVE_SEM
COMMON_SRCS =
SDL_systhread.h
SDL_thread.c
SDL_thread_c.h
+else
+COMMON_SRCS = \

  • SDL_systhread.h \
  • SDL_thread.c \
  • SDL_thread_c.h \
  • SDL_semaphore.c
    +endif

Include the architecture-specific sources

+if USE_NATIVE_SEM
+ARCH_SRCS = \

  • SDL_mutex.c \
  • SDL_systhread.c \
  • SDL_systhread_c.h \
  • SDL_syssemaphore.c \
  • $(THREAD_ASM_SRC)
    +else
    ARCH_SRCS = \
  • SDL_mutex.c \
  • SDL_mutex.c
    SDL_systhread.c
    SDL_systhread_c.h
    $(THREAD_ASM_SRC)
    +endif

libthread_la_SOURCES = $(COMMON_SRCS) $(ARCH_SRCS)

Index: src/thread/SDL_semaphore.c

RCS file: SDL_semaphore.c
diff -N SDL_semaphore.c
— /dev/null Tue May 5 13:32:27 1998
+++ SDL_semaphore.c Tue Apr 18 17:47:39 2000
@@ -0,0 +1,151 @@
+/*

  • SDL - Simple DirectMedia Layer
  • Copyright © 1997, 1998, 1999, 2000 Sam Lantinga
  • This library is free software; you can redistribute it and/or
  • modify it under the terms of the GNU Library General Public
  • License as published by the Free Software Foundation; either
  • version 2 of the License, or (at your option) any later version.
  • This library is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  • Library General Public License for more details.
  • You should have received a copy of the GNU Library General Public
  • License along with this library; if not, write to the Free
  • Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  • Sam Lantinga
  • slouken at devolution.com
    +*/

+#ifdef SAVE_RCSID
+static char rcsid =

  • “@(#) $Id: SDL_thread.c,v 1.2.2.3 2000/03/30 06:30:31 hercules Exp $”;
    +#endif

+#include “SDL_error.h”
+#include “SDL_mutex.h”
+#include “SDL_semaphore.h”
+
+/* Generic implementation of semaphores on top of SDL mutexes */
+
+struct SDL_semaphore {

  • SDL_mutex *mutex;
  • Uint32 count;
  • Uint32 refcount; /* Number of threads waiting on the semaphore */
    +};

+/* Create a semaphore, initialized with value */
+SDL_sem * SDL_CreateSemaphore(Uint32 value)
+{

  • SDL_sem *sem = (SDL_sem *) malloc(sizeof(SDL_sem));
  • if ( sem ) {
  •   sem->mutex = SDL_CreateMutex();
    
  •   sem->count = value;
    
  •   sem->refcount = 0;
    
  • }
  • return sem;
    +}

+/* Destroy a semaphore */
+void SDL_DestroySemaphore(SDL_sem *sem)
+{

  • if ( sem ) {
  •   SDL_mutexP(sem->mutex);
    
  •   if ( sem->refcount ) { /* Still threads waiting on the semaphore */
    
  •   	SDL_bool waiting = SDL_TRUE;
    
  •   	sem->count = sem->refcount; /* Should make them all stop waiting */
    
  •   	SDL_mutexV(sem->mutex);
    
  •   	/* Wait until they are not waiting anymore */
    
  •   	while ( waiting ) {
    
  •   		SDL_Delay(1);
    
  •   		SDL_mutexP(sem->mutex);
    
  •   		if ( sem->refcount == 0 ) {
    
  •   			waiting = SDL_FALSE;
    
  •   		}
    
  •   		SDL_mutexV(sem->mutex);
    
  •   	}
    
  •   } else {
    
  •   	SDL_mutexV(sem->mutex);
    
  •   }
    
  •   SDL_DestroyMutex(sem->mutex);
    
  •   free(sem);
    
  • }
    +}

+/* Suspend the calling thread until the semaphore count is non-zero,

  • then decreases the counter
  • */
    +SDL_bool SDL_SemWait(SDL_sem *sem)
    +{
  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   SDL_mutexP(sem->mutex);
    
  •   if( sem->count > 0 ) {
    
  •   	-- sem->count;
    
  •   	ret = SDL_TRUE;
    
  •   	SDL_mutexV(sem->mutex);
    
  •   } else {
    
  •   	SDL_bool waiting = SDL_TRUE;
    
  •   	/* Suspend the thread */
    
  •   	++ sem->refcount;
    
  •   	SDL_mutexV(sem->mutex);
    
  •   	/* We go into a loop... Would be much better with a real condition variable 
    

*/

  •   	while ( waiting ) {
    
  •   		SDL_Delay(1);
    
  •   		SDL_mutexP(sem->mutex);
    
  •   		if ( sem->count > 0 ) {
    
  •   			-- sem->count;
    
  •   			-- sem->refcount;
    
  •   			waiting = SDL_FALSE;
    
  •   			ret = SDL_TRUE;
    
  •   		}
    
  •   		SDL_mutexV(sem->mutex);
    
  •   	}
    
  •   }
    
  • }
  • return ret;
    +}

+/* Non-blocking variant of SDL_SemWait(), returns SDL_FALSE if counter is zero
*/
+SDL_bool SDL_SemTryWait(SDL_sem *sem)
+{

  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   SDL_mutexP(sem->mutex);
    
  •   if( sem->count > 0 ) {
    
  •   	-- sem->count;
    
  •   	ret = SDL_TRUE;
    
  •   }
    
  •   SDL_mutexV(sem->mutex);
    
  • }
  • return ret;
    +}

+/* Returns the current count of the semaphore */
+Uint32 SDL_SemGetValue(SDL_sem *sem)
+{

  • Uint32 ret = 0;
  • if ( sem ) {
  •   SDL_mutexP(sem->mutex);
    
  •   ret = sem->count;
    
  •   SDL_mutexV(sem->mutex);
    
  • }
  • return ret;
    +}

+/* Atomically increases the semaphore’s count (not blocking) */
+SDL_bool SDL_SemPost(SDL_sem *sem)
+{

  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   SDL_mutexP(sem->mutex);
    
  •   ++ sem->count;
    
  •   SDL_mutexV(sem->mutex);
    
  •   ret = SDL_TRUE;
    
  • }
  • return ret;
    +}
    Index: src/thread/linux/SDL_syssemaphore.c
    ===================================================================
    RCS file: SDL_syssemaphore.c
    diff -N SDL_syssemaphore.c
    — /dev/null Tue May 5 13:32:27 1998
    +++ SDL_syssemaphore.c Tue Apr 18 17:47:39 2000
    @@ -0,0 +1,97 @@
    +/*
  • SDL - Simple DirectMedia Layer
  • Copyright © 1997, 1998, 1999, 2000 Sam Lantinga
  • This library is free software; you can redistribute it and/or
  • modify it under the terms of the GNU Library General Public
  • License as published by the Free Software Foundation; either
  • version 2 of the License, or (at your option) any later version.
  • This library is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  • Library General Public License for more details.
  • You should have received a copy of the GNU Library General Public
  • License along with this library; if not, write to the Free
  • Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  • Sam Lantinga
  • slouken at devolution.com
    +*/

+#ifdef SAVE_RCSID
+static char rcsid =

  • “@(#) $Id: SDL_thread.c,v 1.2.2.3 2000/03/30 06:30:31 hercules Exp $”;
    +#endif

+#include “SDL_error.h”
+#include “SDL_semaphore.h”
+#include <semaphore.h>
+
+/* Wrapper around POSIX 1003.1b semaphores */
+
+struct SDL_semaphore {

  • sem_t sem;
    +};

+/* Create a semaphore, initialized with value */
+SDL_sem * SDL_CreateSemaphore(Uint32 value)
+{

  • SDL_sem *sem = (SDL_sem *) malloc(sizeof(SDL_sem));
  • if ( sem ) {
  •   sem_init(&sem->sem, 0, value);
    
  • }
  • return sem;
    +}

+/* Destroy a semaphore */
+void SDL_DestroySemaphore(SDL_sem *sem)
+{

  • if ( sem ) {
  •   sem_destroy(&sem->sem);
    
  •   free(sem);
    
  • }
    +}

+/* Suspend the calling thread until the semaphore count is non-zero,

  • then decreases the counter
  • */
    +SDL_bool SDL_SemWait(SDL_sem *sem)
    +{
  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   ret = (sem_wait(&sem->sem) == 0);
    
  • }
  • return ret;
    +}

+/* Non-blocking variant of SDL_SemWait(), returns SDL_FALSE if counter is zero
*/
+SDL_bool SDL_SemTryWait(SDL_sem *sem)
+{

  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   ret = (sem_trywait(&sem->sem) == 0);
    
  • }
  • return ret;
    +}

+/* Returns the current count of the semaphore */
+Uint32 SDL_SemGetValue(SDL_sem *sem)
+{

  • Uint32 ret = 0;
  • if ( sem ) {
  •   sem_getvalue(&sem->sem, &ret);
    
  • }
  • return ret;
    +}

+/* Atomically increases the semaphore’s count (not blocking) */
+SDL_bool SDL_SemPost(SDL_sem *sem)
+{

  • SDL_bool ret = SDL_FALSE;
  • if ( sem ) {
  •   ret = (sem_post(&sem->sem) == 0);
    
  • }
  • return ret;
    +}
    Index: test/Makefile.am
    ===================================================================
    RCS file: /cvs/SDL/test/Makefile.am,v
    retrieving revision 1.2.2.4
    diff -u -r1.2.2.4 Makefile.am
    — test/Makefile.am 2000/03/14 20:29:16 1.2.2.4
    +++ test/Makefile.am 2000/04/19 00:47:39
    @@ -4,7 +4,7 @@
    checkkeys graywin loopwave testalpha testbitmap testcdrom
    testerror testhread testkeys testlock testsprite testtimer
    testtypes testver testvidinfo testwin testwm threadwin testgl \
  • testjoystick
  • testjoystick testsem

checkkeys_SOURCES = checkkeys.c
graywin_SOURCES = graywin.c
@@ -28,3 +28,4 @@
testgl_SOURCES = testgl.c
testgl_LDADD = @GL_LIBS@
testjoystick_SOURCES = testjoystick.c
+testsem_SOURCES = testsem.c
Index: test/testsem.c

RCS file: testsem.c
diff -N testsem.c
— /dev/null Tue May 5 13:32:27 1998
+++ testsem.c Tue Apr 18 17:47:39 2000
@@ -0,0 +1,75 @@
+/* Simple test of the SDL semaphore code */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include “SDL.h”
+#include “SDL_thread.h”
+#include “SDL_semaphore.h”
+
+#define NUM_THREADS 10
+
+static SDL_sem *sem;
+int alive = 1;
+
+int ThreadFunc(void *data)
+{

  • while ( alive ) {
  •   SDL_SemWait(sem);
    
  •   printf("Thread number %d has got the semaphore (value = %d)!\n", (int)data, S
    

DL_SemGetValue(sem));

  •   fflush(stdin);
    
  •   SDL_Delay(200);
    
  •   SDL_SemPost(sem);
    
  •   printf("Thread number %d has released the semaphore (value = %d)!\n", (int)da
    

ta, SDL_SemGetValue(sem));

  •   fflush(stdin);
    
  •   SDL_Delay(1); /* For the scheduler */
    
  • }
  • printf(“Threads number %d exiting.\n”, (int)data);
  • return 0;
    +}

+static void killed(int sig)
+{

  • alive = 0;
    +}

+int main(int argc, char **argv)
+{

  • SDL_Thread *threads[NUM_THREADS];
  • int i, init_sem;
  • if(argc < 2) {
  •   fprintf(stderr,"Usage: %s init_value\n", argv[0]);
    
  •   exit(1);
    
  • }
  • /* Load the SDL library */
  • if ( SDL_Init(0) < 0 ) {
  •   fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
    
  •   exit(1);
    
  • }
  • atexit(SDL_Quit);
  • signal(SIGTERM, killed);
  • signal(SIGINT, killed);
  • init_sem = atoi(argv[1]);
  • sem = SDL_CreateSemaphore(init_sem);
  • printf(“Running %d threads, semaphore value = %d\n”, NUM_THREADS, init_sem);
  • /* Create all the threads */
  • for( i = 0; i < NUM_THREADS; ++i ) {
  •   threads[i] = SDL_CreateThread(ThreadFunc, (void*)i);
    
  • }
  • /* Wait 10 seconds */
  • SDL_Delay(10 * 1000);
  • alive = 0;
  • /* Wait for all threads to finish */
  • for( i = 0; i < NUM_THREADS; ++i ) {
  •   SDL_WaitThread(threads[i], NULL);
    
  • }
  • SDL_DestroySemaphore(sem);
  • return(0);
    +}