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:
|
||||
- Pthread support - Gwl
|
||||
- Content Dirs completely reorganized; 3DO and PC segregation - Coredev
|
||||
- Voiceovers controlled by a synthetic '3dovoice' addon - 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_USE_ZIP_IO='@USE_ZIP_IO@'
|
||||
uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
|
||||
uqm_THREADLIB='@THREADLIB@'
|
||||
uqm_NETPLAY='@NETPLAY@'
|
||||
uqm_USE_WINSOCK='@USE_WINSOCK@'
|
||||
DEBUG='@DEBUG@'
|
||||
@@ -52,4 +53,6 @@ MACRO___MINGW32__='@MACRO___MINGW32__@'
|
||||
export BUILD_SYSTEM HOST_SYSTEM DEBUG
|
||||
export MACRO_WIN32 MACRO___MINGW32__
|
||||
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:
|
||||
MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
|
||||
ioformat accel"
|
||||
ioformat accel threadlib"
|
||||
case "$HOST_SYSTEM" in
|
||||
Darwin)
|
||||
# Installation directory not modifiable
|
||||
@@ -92,6 +92,7 @@ uqm_prepare_config()
|
||||
MENU_main_ITEM_joystick_TYPE=CHOICE
|
||||
MENU_main_ITEM_ioformat_TYPE=CHOICE
|
||||
MENU_main_ITEM_accel_TYPE=CHOICE
|
||||
MENU_main_ITEM_threadlib_TYPE=CHOICE
|
||||
MENU_main_ITEM_install_path_TYPE=MENU
|
||||
|
||||
CHOICE_debug_OPTIONS="nodebug debug strictdebug"
|
||||
@@ -290,6 +291,31 @@ uqm_prepare_config()
|
||||
}
|
||||
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 \
|
||||
install_sharedir"
|
||||
MENU_install_path_TITLE="Installation paths"
|
||||
@@ -359,7 +385,7 @@ uqm_process_config() {
|
||||
MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
|
||||
OGGVORBIS SOUNDMODULE USE_INTERNAL_MIKMOD \
|
||||
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 \
|
||||
REZ WINDRES $HAVE_SYMBOLS"
|
||||
SUBSTITUTE_FILES="build.vars"
|
||||
|
||||
@@ -1007,6 +1007,10 @@ set_host_system() {
|
||||
HOST_SYSTEM="QNX" ;;
|
||||
[Cc][Ee][Gg][Cc][Cc])
|
||||
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."
|
||||
;;
|
||||
|
||||
@@ -222,6 +222,27 @@ LIB_zlib_DETECT="try_pkgconfig_lib zlib zlib"
|
||||
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.
|
||||
LIB_netlibs_NAME="Platform-specific network libraries"
|
||||
case "$HOST_SYSTEM" in
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#ifndef _THREADLIB_H
|
||||
#define _THREADLIB_H
|
||||
|
||||
#define THREADLIB SDL
|
||||
|
||||
#define NAMED_SYNCHRO /* Should synchronizable objects have names? */
|
||||
#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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
#if THREADLIB == SDL
|
||||
#include "sdl/sdlthreads.h"
|
||||
#endif /* THREADLIB == SDL */
|
||||
#if defined(THREADLIB_SDL)
|
||||
# include "sdl/sdlthreads.h"
|
||||
#elif defined(THREADLIB_PTHREAD)
|
||||
# include "pthread/posixthreads.h"
|
||||
#endif /* defined(THREADLIB_PTHREAD) */
|
||||
|
||||
|
||||
#endif /* _THR_COMMON_H */
|
||||
|
||||
Reference in New Issue
Block a user