[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