[SDL] Semaphores in SDL 1.1

Stephane Peter megastep at lokigames.com
Tue Apr 18 17:59:18 PDT 2000


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 ;-)

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 (C) 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 (C) 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 (C) 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);
+}


More information about the SDL mailing list