Pthread support from Gwl.
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3041 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
Changes towards version 0.7:
|
Changes towards version 0.7:
|
||||||
|
- Pthread support - Gwl
|
||||||
- Content Dirs completely reorganized; 3DO and PC segregation - Coredev
|
- Content Dirs completely reorganized; 3DO and PC segregation - Coredev
|
||||||
- Voiceovers controlled by a synthetic '3dovoice' addon - Michael
|
- Voiceovers controlled by a synthetic '3dovoice' addon - Michael
|
||||||
- CONVERSATION explicitly names text/voice/timestamps - Michael
|
- CONVERSATION explicitly names text/voice/timestamps - Michael
|
||||||
|
|||||||
+4
-1
@@ -44,6 +44,7 @@ uqm_HAVE_REGEX='@HAVE_REGEX_H_FLAG@'
|
|||||||
uqm_HAVE_OPENGL='@HAVE_OPENGL@'
|
uqm_HAVE_OPENGL='@HAVE_OPENGL@'
|
||||||
uqm_USE_ZIP_IO='@USE_ZIP_IO@'
|
uqm_USE_ZIP_IO='@USE_ZIP_IO@'
|
||||||
uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
|
uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
|
||||||
|
uqm_THREADLIB='@THREADLIB@'
|
||||||
uqm_NETPLAY='@NETPLAY@'
|
uqm_NETPLAY='@NETPLAY@'
|
||||||
uqm_USE_WINSOCK='@USE_WINSOCK@'
|
uqm_USE_WINSOCK='@USE_WINSOCK@'
|
||||||
DEBUG='@DEBUG@'
|
DEBUG='@DEBUG@'
|
||||||
@@ -52,4 +53,6 @@ MACRO___MINGW32__='@MACRO___MINGW32__@'
|
|||||||
export BUILD_SYSTEM HOST_SYSTEM DEBUG
|
export BUILD_SYSTEM HOST_SYSTEM DEBUG
|
||||||
export MACRO_WIN32 MACRO___MINGW32__
|
export MACRO_WIN32 MACRO___MINGW32__
|
||||||
export uqm_SOUNDMODULE uqm_USE_INTERNAL_MIKMOD uqm_HAVE_REGEX uqm_USE_WINSOCK
|
export uqm_SOUNDMODULE uqm_USE_INTERNAL_MIKMOD uqm_HAVE_REGEX uqm_USE_WINSOCK
|
||||||
export uqm_HAVE_OPENGL uqm_USE_ZIP_IO uqm_USE_PLATFORM_ACCEL uqm_NETPLAY
|
export uqm_HAVE_OPENGL uqm_USE_ZIP_IO uqm_USE_PLATFORM_ACCEL uqm_THREADLIB
|
||||||
|
export uqm_NETPLAY
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ uqm_prepare_config()
|
|||||||
{
|
{
|
||||||
# Describe the menu:
|
# Describe the menu:
|
||||||
MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
|
MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
|
||||||
ioformat accel"
|
ioformat accel threadlib"
|
||||||
case "$HOST_SYSTEM" in
|
case "$HOST_SYSTEM" in
|
||||||
Darwin)
|
Darwin)
|
||||||
# Installation directory not modifiable
|
# Installation directory not modifiable
|
||||||
@@ -92,6 +92,7 @@ uqm_prepare_config()
|
|||||||
MENU_main_ITEM_joystick_TYPE=CHOICE
|
MENU_main_ITEM_joystick_TYPE=CHOICE
|
||||||
MENU_main_ITEM_ioformat_TYPE=CHOICE
|
MENU_main_ITEM_ioformat_TYPE=CHOICE
|
||||||
MENU_main_ITEM_accel_TYPE=CHOICE
|
MENU_main_ITEM_accel_TYPE=CHOICE
|
||||||
|
MENU_main_ITEM_threadlib_TYPE=CHOICE
|
||||||
MENU_main_ITEM_install_path_TYPE=MENU
|
MENU_main_ITEM_install_path_TYPE=MENU
|
||||||
|
|
||||||
CHOICE_debug_OPTIONS="nodebug debug strictdebug"
|
CHOICE_debug_OPTIONS="nodebug debug strictdebug"
|
||||||
@@ -290,6 +291,31 @@ uqm_prepare_config()
|
|||||||
}
|
}
|
||||||
CHOICE_accel_DEFAULT=asm
|
CHOICE_accel_DEFAULT=asm
|
||||||
|
|
||||||
|
CHOICE_threadlib_OPTIONS="sdl pthread"
|
||||||
|
CHOICE_threadlib_TITLE="Thread library"
|
||||||
|
CHOICE_threadlib_OPTION_sdl_TITLE="SDL-controlled thread library"
|
||||||
|
CHOICE_threadlib_OPTION_sdl_ACTION="threadlib_sdl_action"
|
||||||
|
threadlib_sdl_action() {
|
||||||
|
CFLAGS="$CFLAGS -DTHREADLIB_SDL"
|
||||||
|
THREADLIB="SDL"
|
||||||
|
}
|
||||||
|
CHOICE_threadlib_OPTION_pthread_TITLE="Pthread thread library"
|
||||||
|
CHOICE_threadlib_OPTION_pthread_PRECOND="have_library pthread"
|
||||||
|
CHOICE_threadlib_OPTION_pthread_ACTION="threadlib_pthread_action"
|
||||||
|
threadlib_pthread_action() {
|
||||||
|
CFLAGS="$CFLAGS -DTHREADLIB_PTHREAD"
|
||||||
|
THREADLIB="PTHREAD"
|
||||||
|
use_library pthread
|
||||||
|
}
|
||||||
|
case "$HOST_SYSTEM" in
|
||||||
|
ARMV5|WINSCW)
|
||||||
|
CHOICE_threadlib_DEFAULT=pthread
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
CHOICE_threadlib_DEFAULT=sdl
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
MENU_install_path_ITEMS="install_prefix install_bindir install_libdir \
|
MENU_install_path_ITEMS="install_prefix install_bindir install_libdir \
|
||||||
install_sharedir"
|
install_sharedir"
|
||||||
MENU_install_path_TITLE="Installation paths"
|
MENU_install_path_TITLE="Installation paths"
|
||||||
@@ -359,7 +385,7 @@ uqm_process_config() {
|
|||||||
MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
|
MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
|
||||||
OGGVORBIS SOUNDMODULE USE_INTERNAL_MIKMOD \
|
OGGVORBIS SOUNDMODULE USE_INTERNAL_MIKMOD \
|
||||||
HAVE_OPENGL HAVE_REGEX_H_FLAG USE_ZIP_IO \
|
HAVE_OPENGL HAVE_REGEX_H_FLAG USE_ZIP_IO \
|
||||||
USE_PLATFORM_ACCEL USE_WINSOCK \
|
USE_PLATFORM_ACCEL THREADLIB USE_WINSOCK \
|
||||||
INSTALL_LIBDIR INSTALL_BINDIR INSTALL_SHAREDIR \
|
INSTALL_LIBDIR INSTALL_BINDIR INSTALL_SHAREDIR \
|
||||||
REZ WINDRES $HAVE_SYMBOLS"
|
REZ WINDRES $HAVE_SYMBOLS"
|
||||||
SUBSTITUTE_FILES="build.vars"
|
SUBSTITUTE_FILES="build.vars"
|
||||||
|
|||||||
@@ -1007,6 +1007,10 @@ set_host_system() {
|
|||||||
HOST_SYSTEM="QNX" ;;
|
HOST_SYSTEM="QNX" ;;
|
||||||
[Cc][Ee][Gg][Cc][Cc])
|
[Cc][Ee][Gg][Cc][Cc])
|
||||||
HOST_SYSTEM="cegcc" ;;
|
HOST_SYSTEM="cegcc" ;;
|
||||||
|
[Ww][Ii][Nn][Ss][Cc][Ww])
|
||||||
|
HOST_SYSTEM="WINSCW" ;;
|
||||||
|
[Aa][Rr][Mm][Vv]5)
|
||||||
|
HOST_SYSTEM="ARMV5" ;;
|
||||||
*)
|
*)
|
||||||
build_message "Warning: host type '$BUILD_HOST' unknown. Using defaults."
|
build_message "Warning: host type '$BUILD_HOST' unknown. Using defaults."
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -222,6 +222,27 @@ LIB_zlib_DETECT="try_pkgconfig_lib zlib zlib"
|
|||||||
LIB_zlib_DEPEND_DETECT_BIN="pkgconfig"
|
LIB_zlib_DEPEND_DETECT_BIN="pkgconfig"
|
||||||
|
|
||||||
|
|
||||||
|
### pthread ###
|
||||||
|
LIB_pthread_NAME="pthread"
|
||||||
|
case "$HOST_SYSTEM" in
|
||||||
|
Linux)
|
||||||
|
LIB_pthread_CFLAGS=""
|
||||||
|
LIB_pthread_LDFLAGS=""
|
||||||
|
LIB_pthread_VERSION=""
|
||||||
|
;;
|
||||||
|
FreeBSD|OpenBSD)
|
||||||
|
LIB_pthread_CFLAGS="$(pthread-config --cflags)"
|
||||||
|
LIB_pthread_LDFLAGS="$(pthread-config --ldflags)"
|
||||||
|
LIB_pthread_VERSION="$(pthread-config --version)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
LIB_pthread_CFLAGS=""
|
||||||
|
LIB_pthread_LDFLAGS="-lpthread"
|
||||||
|
LIB_pthread_VERSION=""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
# Additional platform-specific libraries for networking.
|
# Additional platform-specific libraries for networking.
|
||||||
LIB_netlibs_NAME="Platform-specific network libraries"
|
LIB_netlibs_NAME="Platform-specific network libraries"
|
||||||
case "$HOST_SYSTEM" in
|
case "$HOST_SYSTEM" in
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
#ifndef _THREADLIB_H
|
#ifndef _THREADLIB_H
|
||||||
#define _THREADLIB_H
|
#define _THREADLIB_H
|
||||||
|
|
||||||
#define THREADLIB SDL
|
|
||||||
|
|
||||||
#define NAMED_SYNCHRO /* Should synchronizable objects have names? */
|
#define NAMED_SYNCHRO /* Should synchronizable objects have names? */
|
||||||
#define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */
|
#define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,9 @@
|
|||||||
uqm_SUBDIRS="sdl"
|
case "$uqm_THREADLIB" in
|
||||||
|
SDL)
|
||||||
|
uqm_SUBDIRS="sdl"
|
||||||
|
;;
|
||||||
|
PTHREAD)
|
||||||
|
uqm_SUBDIRS="pthread"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
uqm_CFILES="thrcommon.c"
|
uqm_CFILES="thrcommon.c"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
uqm_CFILES="posixthreads.c"
|
||||||
@@ -0,0 +1,671 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "misc.h"
|
||||||
|
#include "posixthreads.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
#include "libs/log/uqmlog.h"
|
||||||
|
|
||||||
|
typedef struct _thread {
|
||||||
|
pthread_t native;
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
const char *name;
|
||||||
|
#endif
|
||||||
|
ThreadLocal *localData;
|
||||||
|
struct _thread *next;
|
||||||
|
} *TrueThread;
|
||||||
|
|
||||||
|
static volatile TrueThread threadQueue = NULL;
|
||||||
|
static pthread_mutex_t threadQueueMutex;
|
||||||
|
|
||||||
|
struct ThreadStartInfo
|
||||||
|
{
|
||||||
|
ThreadFunction func;
|
||||||
|
void *data;
|
||||||
|
sem_t sem;
|
||||||
|
TrueThread thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
InitThreadSystem_PT (void)
|
||||||
|
{
|
||||||
|
pthread_mutex_init (&threadQueueMutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UnInitThreadSystem_PT (void)
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy (&threadQueueMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QueueThread (TrueThread thread)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock (&threadQueueMutex);
|
||||||
|
thread->next = threadQueue;
|
||||||
|
threadQueue = thread;
|
||||||
|
pthread_mutex_unlock (&threadQueueMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
UnQueueThread (TrueThread thread)
|
||||||
|
{
|
||||||
|
volatile TrueThread *ptr;
|
||||||
|
|
||||||
|
pthread_mutex_lock (&threadQueueMutex);
|
||||||
|
ptr = &threadQueue;
|
||||||
|
while (*ptr != thread)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
if (*ptr == NULL)
|
||||||
|
{
|
||||||
|
// Should not happen.
|
||||||
|
log_add (log_Debug, "Error: Trying to remove non-present thread "
|
||||||
|
"from thread queue.");
|
||||||
|
fflush (stderr);
|
||||||
|
explode ();
|
||||||
|
}
|
||||||
|
#endif /* DEBUG_THREADS */
|
||||||
|
ptr = &(*ptr)->next;
|
||||||
|
}
|
||||||
|
*ptr = (*ptr)->next;
|
||||||
|
pthread_mutex_unlock (&threadQueueMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TrueThread
|
||||||
|
FindThreadInfo (pthread_t threadID)
|
||||||
|
{
|
||||||
|
TrueThread ptr;
|
||||||
|
|
||||||
|
pthread_mutex_lock (&threadQueueMutex);
|
||||||
|
ptr = threadQueue;
|
||||||
|
while (ptr)
|
||||||
|
{
|
||||||
|
if (ptr->native == threadID)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock (&threadQueueMutex);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock (&threadQueueMutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
static const char *
|
||||||
|
MyThreadName (void)
|
||||||
|
{
|
||||||
|
TrueThread t = FindThreadInfo (pthread_self());
|
||||||
|
return t ? t->name : "Unknown (probably renderer)";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void *
|
||||||
|
ThreadHelper (void *startInfo) {
|
||||||
|
ThreadFunction func;
|
||||||
|
void *data;
|
||||||
|
sem_t *sem;
|
||||||
|
TrueThread thread;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
//log_add (log_Debug, "ThreadHelper()");
|
||||||
|
|
||||||
|
func = ((struct ThreadStartInfo *) startInfo)->func;
|
||||||
|
data = ((struct ThreadStartInfo *) startInfo)->data;
|
||||||
|
sem = &((struct ThreadStartInfo *) startInfo)->sem;
|
||||||
|
|
||||||
|
// Wait until the Thread structure is available.
|
||||||
|
if (sem_wait (sem))
|
||||||
|
{
|
||||||
|
log_add(log_Fatal, "ThreadHelper sem_wait fail");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (sem_destroy (sem))
|
||||||
|
{
|
||||||
|
log_add(log_Fatal, "ThreadHelper sem_destroy fail");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = ((struct ThreadStartInfo *) startInfo)->thread;
|
||||||
|
HFree (startInfo);
|
||||||
|
|
||||||
|
result = (*func) (data);
|
||||||
|
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
log_add (log_Debug, "Thread '%s' done (returned %d).",
|
||||||
|
thread->name, result);
|
||||||
|
fflush (stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UnQueueThread (thread);
|
||||||
|
DestroyThreadLocal (thread->localData);
|
||||||
|
FinishThread (thread);
|
||||||
|
/* Destroying the thread is the responsibility of ProcessThreadLifecycles() */
|
||||||
|
return (void*)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DestroyThread_PT (Thread t)
|
||||||
|
{
|
||||||
|
HFree (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread
|
||||||
|
CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
, const char *name
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TrueThread thread;
|
||||||
|
struct ThreadStartInfo *startInfo;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
|
||||||
|
|
||||||
|
//log_add (log_Debug, "CreateThread_PT '%s'", name);
|
||||||
|
|
||||||
|
thread = (struct _thread *) HMalloc (sizeof *thread);
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
thread->name = name;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
thread->localData = CreateThreadLocal ();
|
||||||
|
|
||||||
|
startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
|
||||||
|
startInfo->func = func;
|
||||||
|
startInfo->data = data;
|
||||||
|
if (sem_init(&startInfo->sem, 0, 0) < 0)
|
||||||
|
{
|
||||||
|
log_add (log_Fatal, "createthread seminit fail");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
startInfo->thread = thread;
|
||||||
|
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
if (pthread_attr_setstacksize(&attr, 75000))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "pthread stacksize fail");
|
||||||
|
}
|
||||||
|
if (pthread_create(&thread->native, &attr, ThreadHelper, (void *)startInfo))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "pthread create fail");
|
||||||
|
DestroyThreadLocal (thread->localData);
|
||||||
|
HFree (startInfo);
|
||||||
|
HFree (thread);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// The responsibility to free 'startInfo' and 'thread' is now by the new
|
||||||
|
// thread.
|
||||||
|
|
||||||
|
QueueThread (thread);
|
||||||
|
|
||||||
|
#ifdef DEBUG_THREADS
|
||||||
|
//#if 0
|
||||||
|
log_add (log_Debug, "Thread '%s' created.", thread->name);
|
||||||
|
fflush (stderr);
|
||||||
|
//#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Signal to the new thread that the thread structure is ready
|
||||||
|
// and it can begin to use it.
|
||||||
|
if (sem_post (&startInfo->sem))
|
||||||
|
{
|
||||||
|
log_add(log_Fatal, "CreateThread sem_post fail");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) stackSize; /* Satisfying compiler (unused parameter) */
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SleepThread_PT (TimeCount sleepTime)
|
||||||
|
{
|
||||||
|
usleep (sleepTime * 1000000 / ONE_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SleepThreadUntil_PT (TimeCount wakeTime) {
|
||||||
|
TimeCount now;
|
||||||
|
|
||||||
|
now = GetTimeCounter ();
|
||||||
|
if (wakeTime <= now)
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
else
|
||||||
|
usleep ((wakeTime - now) * 1000000 / ONE_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TaskSwitch_PT (void) {
|
||||||
|
usleep (1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WaitThread_PT (Thread thread, int *status) {
|
||||||
|
//log_add(log_Debug, "WaitThread_PT '%s', status %x", ((TrueThread)thread)->name, status);
|
||||||
|
//pthread_join(((TrueThread)thread)->native, status);
|
||||||
|
pthread_join(((TrueThread)thread)->native, NULL);
|
||||||
|
//log_add(log_Debug, "WaitThread_PT '%s' complete", ((TrueThread)thread)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadLocal *
|
||||||
|
GetMyThreadLocal_PT (void)
|
||||||
|
{
|
||||||
|
TrueThread t = FindThreadInfo (pthread_self());
|
||||||
|
return t ? t->localData : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These are the pthread implementations of the UQM synchronization objects. */
|
||||||
|
|
||||||
|
/* Mutexes. */
|
||||||
|
/* TODO. The w_memlib uses Mutexes right now, so we can't use HMalloc
|
||||||
|
* or HFree. Once that goes, this needs to change. */
|
||||||
|
|
||||||
|
typedef struct _mutex {
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
pthread_t owner;
|
||||||
|
#endif
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
const char *name;
|
||||||
|
DWORD syncClass;
|
||||||
|
#endif
|
||||||
|
} Mut;
|
||||||
|
|
||||||
|
|
||||||
|
Mutex
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
CreateMutex_PT (const char *name, DWORD syncClass)
|
||||||
|
#else
|
||||||
|
CreateMutex_PT (void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Mut *mutex = malloc (sizeof (Mut));
|
||||||
|
|
||||||
|
if (mutex != NULL)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
if (pthread_mutex_init(&mutex->mutex, &attr))
|
||||||
|
{
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
/* logging depends on Mutexes, so we have to use the
|
||||||
|
* non-threaded version instead */
|
||||||
|
log_add_nothread (log_Fatal, "Could not initialize mutex '%s':"
|
||||||
|
"aborting.", name);
|
||||||
|
#else
|
||||||
|
log_add_nothread (log_Fatal, "Could not initialize mutex:"
|
||||||
|
"aborting.");
|
||||||
|
#endif
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
mutex->owner = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
mutex->name = name;
|
||||||
|
mutex->syncClass = syncClass;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DestroyMutex_PT (Mutex m)
|
||||||
|
{
|
||||||
|
Mut *mutex = (Mut *)m;
|
||||||
|
//log_add_nothread(log_Debug, "Destroying mutex '%s'", mutex->name);
|
||||||
|
pthread_mutex_destroy (&mutex->mutex);
|
||||||
|
free (mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LockMutex_PT (Mutex m)
|
||||||
|
{
|
||||||
|
Mut *mutex = (Mut *)m;
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
/* This code isn't really quite right; race conditions between
|
||||||
|
* check and lock remain and can produce reports of contention
|
||||||
|
* where the thread never sleeps, or fail to report in
|
||||||
|
* situations where it does. If tracking with perfect
|
||||||
|
* accuracy becomes important, the TRACK_CONTENTION mutex will
|
||||||
|
* need to handle its own wake/sleep cycles with condition
|
||||||
|
* variables (check the history of this file for the
|
||||||
|
* CrossThreadMutex code). This almost-measure is being added
|
||||||
|
* because for the most part it should suffice. */
|
||||||
|
if (mutex->owner && (mutex->syncClass & TRACK_CONTENTION_CLASSES))
|
||||||
|
{ /* logging depends on Mutexes, so we have to use the
|
||||||
|
* non-threaded version instead */
|
||||||
|
log_add_nothread (log_Debug, "Thread '%s' blocking on mutex '%s'",
|
||||||
|
MyThreadName (), mutex->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (pthread_mutex_lock (&mutex->mutex) != 0)
|
||||||
|
{
|
||||||
|
//log_add_nothread (log_Debug, "Attempt to acquire mutex '%s' failretry", mutex->name);
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
}
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
mutex->owner = pthread_self();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UnlockMutex_PT (Mutex m)
|
||||||
|
{
|
||||||
|
Mut *mutex = (Mut *)m;
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
mutex->owner = 0;
|
||||||
|
#endif
|
||||||
|
while (pthread_mutex_unlock (&mutex->mutex) != 0)
|
||||||
|
{
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Semaphores. */
|
||||||
|
|
||||||
|
typedef struct _sem {
|
||||||
|
sem_t sem;
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
const char *name;
|
||||||
|
DWORD syncClass;
|
||||||
|
#endif
|
||||||
|
} Sem;
|
||||||
|
|
||||||
|
Semaphore
|
||||||
|
CreateSemaphore_PT (DWORD initial
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
, const char *name, DWORD syncClass
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Sem *sem = (Sem *) HMalloc (sizeof (struct _sem));
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
sem->name = name;
|
||||||
|
sem->syncClass = syncClass;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//log_add (log_Debug, "Creating semaphore '%s'", sem->name);
|
||||||
|
|
||||||
|
if (sem_init(&sem->sem, 0, initial) < 0)
|
||||||
|
{
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
log_add (log_Fatal, "Could not initialize semaphore '%s':"
|
||||||
|
" aborting.", name);
|
||||||
|
#else
|
||||||
|
log_add (log_Fatal, "Could not initialize semaphore:"
|
||||||
|
" aborting.");
|
||||||
|
#endif
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
//log_add (log_Debug, "Creating semaphore '%s' success", sem->name);
|
||||||
|
return sem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DestroySemaphore_PT (Semaphore s)
|
||||||
|
{
|
||||||
|
Sem *sem = (Sem *)s;
|
||||||
|
//log_add (log_Debug, "Destroying semaphore '%s'", sem->name);
|
||||||
|
if (sem_destroy (&sem->sem))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Destroying semaphore '%s' failed", sem->name);
|
||||||
|
}
|
||||||
|
HFree (sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetSemaphore_PT (Semaphore s)
|
||||||
|
{
|
||||||
|
Sem *sem = (Sem *)s;
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
int contention = 0;
|
||||||
|
sem_getvalue(&sem->sem, &contention);
|
||||||
|
contention = !contention;
|
||||||
|
if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Thread '%s' blocking on semaphore '%s'",
|
||||||
|
MyThreadName (), sem->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//log_add (log_Debug, "Attempt to set semaphore '%s'", sem->name);
|
||||||
|
while (sem_wait (&sem->sem) == -1)
|
||||||
|
{
|
||||||
|
//log_add (log_Debug, "Attempt to set semaphore '%s' failretry", sem->name);
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
}
|
||||||
|
//log_add (log_Debug, "Attempt to set semaphore '%s' success", sem->name);
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Thread '%s' awakens,"
|
||||||
|
" released from semaphore '%s'", MyThreadName (), sem->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearSemaphore_PT (Semaphore s)
|
||||||
|
{
|
||||||
|
Sem *sem = (Sem *)s;
|
||||||
|
//log_add (log_Debug, "Attempt to clear semaphore '%s' %x", sem->name, sem);
|
||||||
|
while (sem_post (&sem->sem) == -1)
|
||||||
|
{
|
||||||
|
//log_add (log_Debug, "Attempt to clear semaphore %x failretry", sem);
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
}
|
||||||
|
//log_add (log_Debug, "Attempt to clear semaphore %x success", sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recursive mutexes. Adapted from mixSDL code, which was adapted from
|
||||||
|
the original DCQ code. */
|
||||||
|
|
||||||
|
typedef struct _recm {
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_t thread_id;
|
||||||
|
unsigned int locks;
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
const char *name;
|
||||||
|
DWORD syncClass;
|
||||||
|
#endif
|
||||||
|
} RecM;
|
||||||
|
|
||||||
|
RecursiveMutex
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
CreateRecursiveMutex_PT (const char *name, DWORD syncClass)
|
||||||
|
#else
|
||||||
|
CreateRecursiveMutex_PT (void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
|
||||||
|
|
||||||
|
mtx->thread_id = 0;
|
||||||
|
if (pthread_mutex_init(&mtx->mutex, NULL))
|
||||||
|
{
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
log_add (log_Fatal, "Could not initialize recursive "
|
||||||
|
"mutex '%s': aborting.", name);
|
||||||
|
#else
|
||||||
|
log_add (log_Fatal, "Could not initialize recursive "
|
||||||
|
"mutex: aborting.");
|
||||||
|
#endif
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
mtx->name = name;
|
||||||
|
mtx->syncClass = syncClass;
|
||||||
|
#endif
|
||||||
|
mtx->locks = 0;
|
||||||
|
return (RecursiveMutex) mtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DestroyRecursiveMutex_PT (RecursiveMutex val)
|
||||||
|
{
|
||||||
|
RecM *mtx = (RecM *)val;
|
||||||
|
pthread_mutex_destroy(&mtx->mutex);
|
||||||
|
HFree (mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LockRecursiveMutex_PT (RecursiveMutex val)
|
||||||
|
{
|
||||||
|
RecM *mtx = (RecM *)val;
|
||||||
|
pthread_t thread_id = pthread_self();
|
||||||
|
if (!mtx->locks || mtx->thread_id != thread_id)
|
||||||
|
{
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
if (mtx->thread_id && (mtx->syncClass & TRACK_CONTENTION_CLASSES))
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Thread '%s' blocking on '%s'",
|
||||||
|
MyThreadName (), mtx->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (pthread_mutex_lock (&mtx->mutex))
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
mtx->thread_id = thread_id;
|
||||||
|
}
|
||||||
|
mtx->locks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UnlockRecursiveMutex_PT (RecursiveMutex val)
|
||||||
|
{
|
||||||
|
RecM *mtx = (RecM *)val;
|
||||||
|
pthread_t thread_id = pthread_self();
|
||||||
|
if (!mtx->locks || mtx->thread_id != thread_id)
|
||||||
|
{
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
log_add (log_Debug, "'%s' attempted to unlock %s when it "
|
||||||
|
"didn't hold it", MyThreadName (), mtx->name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtx->locks--;
|
||||||
|
if (!mtx->locks)
|
||||||
|
{
|
||||||
|
mtx->thread_id = 0;
|
||||||
|
pthread_mutex_unlock (&mtx->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
GetRecursiveMutexDepth_PT (RecursiveMutex val)
|
||||||
|
{
|
||||||
|
RecM *mtx = (RecM *)val;
|
||||||
|
return mtx->locks;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _cond {
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
const char *name;
|
||||||
|
DWORD syncClass;
|
||||||
|
#endif
|
||||||
|
} cvar;
|
||||||
|
|
||||||
|
CondVar
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
CreateCondVar_PT (const char *name, DWORD syncClass)
|
||||||
|
#else
|
||||||
|
CreateCondVar_PT (void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int err1, err2;
|
||||||
|
cvar *cv = (cvar *) HMalloc (sizeof (cvar));
|
||||||
|
err1 = pthread_cond_init(&cv->cond, NULL);
|
||||||
|
err2 = pthread_mutex_init(&cv->mutex, NULL);
|
||||||
|
if (err1 || err2)
|
||||||
|
{
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
log_add (log_Fatal, "Could not initialize condition variable '%s':"
|
||||||
|
" aborting.", name);
|
||||||
|
#else
|
||||||
|
log_add (log_Fatal, "Could not initialize condition variable:"
|
||||||
|
" aborting.");
|
||||||
|
#endif
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
cv->name = name;
|
||||||
|
cv->syncClass = syncClass;
|
||||||
|
#endif
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DestroyCondVar_PT (CondVar c)
|
||||||
|
{
|
||||||
|
cvar *cv = (cvar *) c;
|
||||||
|
pthread_cond_destroy(&cv->cond);
|
||||||
|
pthread_mutex_destroy(&cv->mutex);
|
||||||
|
HFree (cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WaitCondVar_PT (CondVar c)
|
||||||
|
{
|
||||||
|
cvar *cv = (cvar *) c;
|
||||||
|
pthread_mutex_lock (&cv->mutex);
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
if (cv->syncClass & TRACK_CONTENTION_CLASSES)
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Thread '%s' waiting for signal from '%s'",
|
||||||
|
MyThreadName (), cv->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (pthread_cond_wait (&cv->cond, &cv->mutex) != 0)
|
||||||
|
{
|
||||||
|
TaskSwitch_PT ();
|
||||||
|
}
|
||||||
|
#ifdef TRACK_CONTENTION
|
||||||
|
if (cv->syncClass & TRACK_CONTENTION_CLASSES)
|
||||||
|
{
|
||||||
|
log_add (log_Debug, "Thread '%s' received signal from '%s',"
|
||||||
|
" awakening.", MyThreadName (), cv->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
pthread_mutex_unlock (&cv->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SignalCondVar_PT (CondVar c)
|
||||||
|
{
|
||||||
|
cvar *cv = (cvar *) c;
|
||||||
|
pthread_cond_signal(&cv->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BroadcastCondVar_PT (CondVar c)
|
||||||
|
{
|
||||||
|
cvar *cv = (cvar *) c;
|
||||||
|
pthread_cond_broadcast(&cv->cond);
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UQMPTHREADS_H
|
||||||
|
#define _UQMPTHREADS_H
|
||||||
|
|
||||||
|
#include "port.h"
|
||||||
|
#include "libs/threadlib.h"
|
||||||
|
|
||||||
|
void InitThreadSystem_PT (void);
|
||||||
|
void UnInitThreadSystem_PT (void);
|
||||||
|
|
||||||
|
#ifdef NAMED_SYNCHRO
|
||||||
|
/* Prototypes with the "name" field */
|
||||||
|
Thread CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
|
||||||
|
Mutex CreateMutex_PT (const char *name, DWORD syncClass);
|
||||||
|
Semaphore CreateSemaphore_PT (DWORD initial, const char *name, DWORD syncClass);
|
||||||
|
RecursiveMutex CreateRecursiveMutex_PT (const char *name, DWORD syncClass);
|
||||||
|
CondVar CreateCondVar_PT (const char *name, DWORD syncClass);
|
||||||
|
#else
|
||||||
|
/* Prototypes without the "name" field. */
|
||||||
|
Thread CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize);
|
||||||
|
Mutex CreateMutex_PT (void);
|
||||||
|
Semaphore CreateSemaphore_PT (DWORD initial);
|
||||||
|
RecursiveMutex CreateRecursiveMutex_PT (void);
|
||||||
|
CondVar CreateCondVar_PT (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ThreadLocal *GetMyThreadLocal_PT (void);
|
||||||
|
|
||||||
|
void SleepThread_PT (TimeCount sleepTime);
|
||||||
|
void SleepThreadUntil_PT (TimeCount wakeTime);
|
||||||
|
void TaskSwitch_PT (void);
|
||||||
|
void WaitThread_PT (Thread thread, int *status);
|
||||||
|
void DestroyThread_PT (Thread thread);
|
||||||
|
|
||||||
|
void DestroyMutex_PT (Mutex m);
|
||||||
|
void LockMutex_PT (Mutex m);
|
||||||
|
void UnlockMutex_PT (Mutex m);
|
||||||
|
|
||||||
|
void DestroySemaphore_PT (Semaphore sem);
|
||||||
|
void SetSemaphore_PT (Semaphore sem);
|
||||||
|
void ClearSemaphore_PT (Semaphore sem);
|
||||||
|
|
||||||
|
void DestroyCondVar_PT (CondVar c);
|
||||||
|
void WaitCondVar_PT (CondVar c);
|
||||||
|
void SignalCondVar_PT (CondVar c);
|
||||||
|
void BroadcastCondVar_PT (CondVar c);
|
||||||
|
|
||||||
|
void DestroyRecursiveMutex_PT (RecursiveMutex m);
|
||||||
|
void LockRecursiveMutex_PT (RecursiveMutex m);
|
||||||
|
void UnlockRecursiveMutex_PT (RecursiveMutex m);
|
||||||
|
int GetRecursiveMutexDepth_PT (RecursiveMutex m);
|
||||||
|
|
||||||
|
#define NativeInitThreadSystem InitThreadSystem_PT
|
||||||
|
#define NativeUnInitThreadSystem UnInitThreadSystem_PT
|
||||||
|
|
||||||
|
#define NativeGetMyThreadLocal GetMyThreadLocal_PT
|
||||||
|
|
||||||
|
#define NativeCreateThread CreateThread_PT
|
||||||
|
#define NativeSleepThread SleepThread_PT
|
||||||
|
#define NativeSleepThreadUntil SleepThreadUntil_PT
|
||||||
|
#define NativeTaskSwitch TaskSwitch_PT
|
||||||
|
#define NativeWaitThread WaitThread_PT
|
||||||
|
#define NativeDestroyThread DestroyThread_PT
|
||||||
|
|
||||||
|
#define NativeCreateMutex CreateMutex_PT
|
||||||
|
#define NativeDestroyMutex DestroyMutex_PT
|
||||||
|
#define NativeLockMutex LockMutex_PT
|
||||||
|
#define NativeUnlockMutex UnlockMutex_PT
|
||||||
|
|
||||||
|
#define NativeCreateSemaphore CreateSemaphore_PT
|
||||||
|
#define NativeDestroySemaphore DestroySemaphore_PT
|
||||||
|
#define NativeSetSemaphore SetSemaphore_PT
|
||||||
|
#define NativeClearSemaphore ClearSemaphore_PT
|
||||||
|
|
||||||
|
#define NativeCreateCondVar CreateCondVar_PT
|
||||||
|
#define NativeDestroyCondVar DestroyCondVar_PT
|
||||||
|
#define NativeWaitCondVar WaitCondVar_PT
|
||||||
|
#define NativeSignalCondVar SignalCondVar_PT
|
||||||
|
#define NativeBroadcastCondVar BroadcastCondVar_PT
|
||||||
|
|
||||||
|
#define NativeCreateRecursiveMutex CreateRecursiveMutex_PT
|
||||||
|
#define NativeDestroyRecursiveMutex DestroyRecursiveMutex_PT
|
||||||
|
#define NativeLockRecursiveMutex LockRecursiveMutex_PT
|
||||||
|
#define NativeUnlockRecursiveMutex UnlockRecursiveMutex_PT
|
||||||
|
#define NativeGetRecursiveMutexDepth GetRecursiveMutexDepth_PT
|
||||||
|
|
||||||
|
#endif /* _PTTHREAD_H */
|
||||||
|
|
||||||
@@ -18,9 +18,11 @@
|
|||||||
#define _THRCOMMON_H
|
#define _THRCOMMON_H
|
||||||
|
|
||||||
|
|
||||||
#if THREADLIB == SDL
|
#if defined(THREADLIB_SDL)
|
||||||
#include "sdl/sdlthreads.h"
|
# include "sdl/sdlthreads.h"
|
||||||
#endif /* THREADLIB == SDL */
|
#elif defined(THREADLIB_PTHREAD)
|
||||||
|
# include "pthread/posixthreads.h"
|
||||||
|
#endif /* defined(THREADLIB_PTHREAD) */
|
||||||
|
|
||||||
|
|
||||||
#endif /* _THR_COMMON_H */
|
#endif /* _THR_COMMON_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user