summaryrefslogtreecommitdiff
path: root/src/libs/threads
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/threads')
-rw-r--r--src/libs/threads/Makeinfo11
-rw-r--r--src/libs/threads/pthread/Makeinfo2
-rw-r--r--src/libs/threads/pthread/posixthreads.c672
-rw-r--r--src/libs/threads/pthread/posixthreads.h103
-rw-r--r--src/libs/threads/sdl/Makeinfo2
-rw-r--r--src/libs/threads/sdl/sdlthreads.c706
-rw-r--r--src/libs/threads/sdl/sdlthreads.h106
-rw-r--r--src/libs/threads/thrcommon.c451
-rw-r--r--src/libs/threads/thrcommon.h28
9 files changed, 2081 insertions, 0 deletions
diff --git a/src/libs/threads/Makeinfo b/src/libs/threads/Makeinfo
new file mode 100644
index 0000000..57f56a8
--- /dev/null
+++ b/src/libs/threads/Makeinfo
@@ -0,0 +1,11 @@
+case "$uqm_THREADLIB" in
+ SDL)
+ uqm_SUBDIRS="sdl"
+ ;;
+ PTHREAD)
+ uqm_SUBDIRS="pthread"
+ ;;
+esac
+
+uqm_CFILES="thrcommon.c"
+uqm_HFILES="thrcommon.h"
diff --git a/src/libs/threads/pthread/Makeinfo b/src/libs/threads/pthread/Makeinfo
new file mode 100644
index 0000000..251db7b
--- /dev/null
+++ b/src/libs/threads/pthread/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="posixthreads.c"
+uqm_HFILES="posixthreads.h"
diff --git a/src/libs/threads/pthread/posixthreads.c b/src/libs/threads/pthread/posixthreads.c
new file mode 100644
index 0000000..4f0d2e8
--- /dev/null
+++ b/src/libs/threads/pthread/posixthreads.c
@@ -0,0 +1,672 @@
+/*
+ * 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 "libs/misc.h"
+#include "libs/memlib.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);
+}
diff --git a/src/libs/threads/pthread/posixthreads.h b/src/libs/threads/pthread/posixthreads.h
new file mode 100644
index 0000000..9945b53
--- /dev/null
+++ b/src/libs/threads/pthread/posixthreads.h
@@ -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 LIBS_THREADS_PTHREAD_POSIXTHREADS_H_
+#define LIBS_THREADS_PTHREAD_POSIXTHREADS_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 */
+
diff --git a/src/libs/threads/sdl/Makeinfo b/src/libs/threads/sdl/Makeinfo
new file mode 100644
index 0000000..7a7ad76
--- /dev/null
+++ b/src/libs/threads/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="sdlthreads.c"
+uqm_HFILES="sdlthreads.h"
diff --git a/src/libs/threads/sdl/sdlthreads.c b/src/libs/threads/sdl/sdlthreads.c
new file mode 100644
index 0000000..118951d
--- /dev/null
+++ b/src/libs/threads/sdl/sdlthreads.c
@@ -0,0 +1,706 @@
+/*
+ * 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 "libs/misc.h"
+#include "libs/memlib.h"
+#include "sdlthreads.h"
+#ifdef PROFILE_THREADS
+#include <signal.h>
+#include <unistd.h>
+#endif
+#include "libs/log.h"
+
+#if defined(PROFILE_THREADS) && !defined(WIN32)
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#if SDL_MAJOR_VERSION == 1
+typedef Uint32 SDL_threadID;
+#endif
+
+typedef struct _thread {
+ void *native;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+#endif
+#ifdef PROFILE_THREADS
+ int startTime;
+#endif /* PROFILE_THREADS */
+ ThreadLocal *localData;
+ struct _thread *next;
+} *TrueThread;
+
+static volatile TrueThread threadQueue = NULL;
+static SDL_mutex *threadQueueMutex;
+
+struct ThreadStartInfo
+{
+ ThreadFunction func;
+ void *data;
+ SDL_sem *sem;
+ TrueThread thread;
+};
+
+#ifdef PROFILE_THREADS
+static void
+SigUSR1Handler (int signr) {
+ if (getpgrp () != getpid ())
+ {
+ // Only act for the main process
+ return;
+ }
+ PrintThreadsStats ();
+ // It's not a good idea in general to do many things in a signal
+ // handler, (and especially the locking) but I guess it will
+ // have to do for now (and it's only for debugging).
+ (void) signr; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+LocalStats (SDL_Thread *thread) {
+#if defined (WIN32) || !defined(SDL_PTHREADS)
+ fprintf (stderr, "Thread ID %08lx\n", (Uint64)SDL_GetThreadID (thread));
+#else /* !defined (WIN32) && defined(SDL_PTHREADS) */
+ // This only works if SDL implements threads as processes
+ pid_t pid;
+ struct rusage ru;
+ long seconds;
+
+ pid = (pid_t) SDL_GetThreadID (thread);
+ fprintf (stderr, "Pid %d\n", (int) pid);
+ getrusage(RUSAGE_SELF, &ru);
+ seconds = ru.ru_utime.tv_sec + ru.ru_utime.tv_sec;
+ fprintf (stderr, "Used %ld.%ld minutes of processor time.\n",
+ seconds / 60, seconds % 60);
+#endif /* defined (WIN32) && defined(SDL_PTHREADS) */
+}
+
+void
+PrintThreadsStats_SDL (void)
+{
+ TrueThread ptr;
+ int now;
+
+ now = GetTimeCounter ();
+ SDL_mutexP (threadQueueMutex);
+ fprintf(stderr, "--- Active threads ---\n");
+ for (ptr = threadQueue; ptr != NULL; ptr = ptr->next) {
+ fprintf (stderr, "Thread named '%s'.\n", ptr->name);
+ fprintf (stderr, "Started %d.%d minutes ago.\n",
+ (now - ptr->startTime) / 60000,
+ ((now - ptr->startTime) / 1000) % 60);
+ LocalStats (ptr->native);
+ if (ptr->next != NULL)
+ fprintf(stderr, "\n");
+ }
+ SDL_mutexV (threadQueueMutex);
+ fprintf(stderr, "----------------------\n");
+ fflush (stderr);
+}
+#endif /* PROFILE_THREADS */
+
+void
+InitThreadSystem_SDL (void)
+{
+ threadQueueMutex = SDL_CreateMutex ();
+#ifdef PROFILE_THREADS
+ signal(SIGUSR1, SigUSR1Handler);
+#endif
+}
+
+void
+UnInitThreadSystem_SDL (void)
+{
+#ifdef PROFILE_THREADS
+ signal(SIGUSR1, SIG_DFL);
+#endif
+ SDL_DestroyMutex (threadQueueMutex);
+}
+
+static void
+QueueThread (TrueThread thread)
+{
+ SDL_mutexP (threadQueueMutex);
+ thread->next = threadQueue;
+ threadQueue = thread;
+ SDL_mutexV (threadQueueMutex);
+}
+
+static void
+UnQueueThread (TrueThread thread)
+{
+ volatile TrueThread *ptr;
+
+ SDL_mutexP (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;
+ SDL_mutexV (threadQueueMutex);
+}
+
+static TrueThread
+FindThreadInfo (SDL_threadID threadID)
+{
+ TrueThread ptr;
+
+ SDL_mutexP (threadQueueMutex);
+ ptr = threadQueue;
+ while (ptr)
+ {
+ if (SDL_GetThreadID (ptr->native) == threadID)
+ {
+ SDL_mutexV (threadQueueMutex);
+ return ptr;
+ }
+ ptr = ptr->next;
+ }
+ SDL_mutexV (threadQueueMutex);
+ return NULL;
+}
+
+#ifdef NAMED_SYNCHRO
+static const char *
+MyThreadName (void)
+{
+ TrueThread t = FindThreadInfo (SDL_ThreadID ());
+ return t ? t->name : "Unknown (probably renderer)";
+}
+#endif
+
+static int
+ThreadHelper (void *startInfo) {
+ ThreadFunction func;
+ void *data;
+ SDL_sem *sem;
+ TrueThread thread;
+ int result;
+
+ func = ((struct ThreadStartInfo *) startInfo)->func;
+ data = ((struct ThreadStartInfo *) startInfo)->data;
+ sem = ((struct ThreadStartInfo *) startInfo)->sem;
+
+ // Wait until the Thread structure is available.
+ SDL_SemWait (sem);
+ SDL_DestroySemaphore (sem);
+ 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 result;
+}
+
+void
+DestroyThread_SDL (Thread t)
+{
+ HFree (t);
+}
+
+Thread
+CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize
+#ifdef NAMED_SYNCHRO
+ , const char *name
+#endif
+ )
+{
+ TrueThread thread;
+ struct ThreadStartInfo *startInfo;
+
+ thread = (struct _thread *) HMalloc (sizeof *thread);
+#ifdef NAMED_SYNCHRO
+ thread->name = name;
+#endif
+#ifdef PROFILE_THREADS
+ thread->startTime = GetTimeCounter ();
+#endif
+
+ thread->localData = CreateThreadLocal ();
+
+ startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
+ startInfo->func = func;
+ startInfo->data = data;
+ startInfo->sem = SDL_CreateSemaphore (0);
+ startInfo->thread = thread;
+
+#if SDL_MAJOR_VERSION == 1
+ // SDL1 case
+ thread->native = SDL_CreateThread (ThreadHelper, (void *) startInfo);
+#elif defined(NAMED_SYNCHRO)
+ // SDL2 with UQM-aware named threads case
+ thread->native = SDL_CreateThread (ThreadHelper, thread->name, (void *) startInfo);
+#else
+ // SDL2 without UQM-aware named threads; use a placeholder for debuggers
+ thread->native = SDL_CreateThread (ThreadHelper, "UQM worker thread", (void *)startInfo);
+#endif
+ if (!(thread->native))
+ {
+ 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.", ThreadName (thread));
+ fflush (stderr);
+#endif
+#endif
+
+ // Signal to the new thread that the thread structure is ready
+ // and it can begin to use it.
+ SDL_SemPost (startInfo->sem);
+
+ (void) stackSize; /* Satisfying compiler (unused parameter) */
+ return thread;
+}
+
+void
+SleepThread_SDL (TimeCount sleepTime)
+{
+ SDL_Delay (sleepTime * 1000 / ONE_SECOND);
+}
+
+void
+SleepThreadUntil_SDL (TimeCount wakeTime) {
+ TimeCount now;
+
+ now = GetTimeCounter ();
+ if (wakeTime <= now)
+ TaskSwitch_SDL ();
+ else
+ SDL_Delay ((wakeTime - now) * 1000 / ONE_SECOND);
+}
+
+void
+TaskSwitch_SDL (void) {
+ SDL_Delay (1);
+}
+
+void
+WaitThread_SDL (Thread thread, int *status) {
+ SDL_WaitThread (((TrueThread)thread)->native, status);
+}
+
+ThreadLocal *
+GetMyThreadLocal_SDL (void)
+{
+ TrueThread t = FindThreadInfo (SDL_ThreadID ());
+ return t ? t->localData : NULL;
+}
+
+/* These are the SDL 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 {
+ SDL_mutex *mutex;
+#ifdef TRACK_CONTENTION
+ SDL_threadID owner;
+#endif
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Mut;
+
+
+Mutex
+#ifdef NAMED_SYNCHRO
+CreateMutex_SDL (const char *name, DWORD syncClass)
+#else
+CreateMutex_SDL (void)
+#endif
+{
+ Mut *mutex = malloc (sizeof (Mut));
+ if (mutex != NULL)
+ {
+ mutex->mutex = SDL_CreateMutex();
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+#ifdef NAMED_SYNCHRO
+ mutex->name = name;
+ mutex->syncClass = syncClass;
+#endif
+ }
+
+ if ((mutex == NULL) || (mutex->mutex == NULL))
+ {
+#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);
+ }
+
+ return mutex;
+}
+
+void
+DestroyMutex_SDL (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+ SDL_DestroyMutex (mutex->mutex);
+ free (mutex);
+}
+
+void
+LockMutex_SDL (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 (SDL_mutexP (mutex->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+#ifdef TRACK_CONTENTION
+ mutex->owner = SDL_ThreadID ();
+#endif
+}
+
+void
+UnlockMutex_SDL (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+ while (SDL_mutexV (mutex->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+}
+
+/* Semaphores. */
+
+typedef struct _sem {
+ SDL_sem *sem;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Sem;
+
+Semaphore
+CreateSemaphore_SDL (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
+ sem->sem = SDL_CreateSemaphore (initial);
+ if (sem->sem == NULL)
+ {
+#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);
+ }
+ return sem;
+}
+
+void
+DestroySemaphore_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ SDL_DestroySemaphore (sem->sem);
+ HFree (sem);
+}
+
+void
+SetSemaphore_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+#ifdef TRACK_CONTENTION
+ BOOLEAN contention = !(SDL_SemValue (sem->sem));
+ if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' blocking on semaphore '%s'",
+ MyThreadName (), sem->name);
+ }
+#endif
+ while (SDL_SemWait (sem->sem) == -1)
+ {
+ TaskSwitch_SDL ();
+ }
+#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_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ while (SDL_SemPost (sem->sem) == -1)
+ {
+ TaskSwitch_SDL ();
+ }
+}
+
+/* Recursive mutexes. Adapted from mixSDL code, which was adapted from
+ the original DCQ code. */
+
+typedef struct _recm {
+ SDL_mutex *mutex;
+ SDL_threadID thread_id;
+ Uint32 locks;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} RecM;
+
+RecursiveMutex
+#ifdef NAMED_SYNCHRO
+CreateRecursiveMutex_SDL (const char *name, DWORD syncClass)
+#else
+CreateRecursiveMutex_SDL (void)
+#endif
+{
+ RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
+
+ mtx->thread_id = 0;
+ mtx->mutex = SDL_CreateMutex ();
+ if (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_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_DestroyMutex (mtx->mutex);
+ HFree (mtx);
+}
+
+void
+LockRecursiveMutex_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_threadID thread_id = SDL_ThreadID();
+ 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 (SDL_mutexP (mtx->mutex))
+ TaskSwitch_SDL ();
+ mtx->thread_id = thread_id;
+ }
+ mtx->locks++;
+}
+
+void
+UnlockRecursiveMutex_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_threadID thread_id = SDL_ThreadID();
+ 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;
+ SDL_mutexV (mtx->mutex);
+ }
+ }
+}
+
+int
+GetRecursiveMutexDepth_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ return mtx->locks;
+}
+
+typedef struct _cond {
+ SDL_cond *cond;
+ SDL_mutex *mutex;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} cvar;
+
+CondVar
+#ifdef NAMED_SYNCHRO
+CreateCondVar_SDL (const char *name, DWORD syncClass)
+#else
+CreateCondVar_SDL (void)
+#endif
+{
+ cvar *cv = (cvar *) HMalloc (sizeof (cvar));
+ cv->cond = SDL_CreateCond ();
+ cv->mutex = SDL_CreateMutex ();
+ if ((cv->cond == NULL) || (cv->mutex == NULL))
+ {
+#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_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_DestroyCond (cv->cond);
+ SDL_DestroyMutex (cv->mutex);
+ HFree (cv);
+}
+
+void
+WaitCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_mutexP (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 (SDL_CondWait (cv->cond, cv->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+#ifdef TRACK_CONTENTION
+ if (cv->syncClass & TRACK_CONTENTION_CLASSES)
+ {
+ log_add (log_Debug, "Thread '%s' received signal from '%s',"
+ " awakening.", MyThreadName (), cv->name);
+ }
+#endif
+ SDL_mutexV (cv->mutex);
+}
+
+void
+SignalCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_CondSignal (cv->cond);
+}
+
+void
+BroadcastCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_CondBroadcast (cv->cond);
+}
diff --git a/src/libs/threads/sdl/sdlthreads.h b/src/libs/threads/sdl/sdlthreads.h
new file mode 100644
index 0000000..c93c947
--- /dev/null
+++ b/src/libs/threads/sdl/sdlthreads.h
@@ -0,0 +1,106 @@
+/*
+ * 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 LIBS_THREADS_SDL_SDLTHREADS_H_
+#define LIBS_THREADS_SDL_SDLTHREADS_H_
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_thread.h)
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+
+void InitThreadSystem_SDL (void);
+void UnInitThreadSystem_SDL (void);
+
+#ifdef NAMED_SYNCHRO
+/* Prototypes with the "name" field */
+Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
+Mutex CreateMutex_SDL (const char *name, DWORD syncClass);
+Semaphore CreateSemaphore_SDL (DWORD initial, const char *name, DWORD syncClass);
+RecursiveMutex CreateRecursiveMutex_SDL (const char *name, DWORD syncClass);
+CondVar CreateCondVar_SDL (const char *name, DWORD syncClass);
+#else
+/* Prototypes without the "name" field. */
+Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize);
+Mutex CreateMutex_SDL (void);
+Semaphore CreateSemaphore_SDL (DWORD initial);
+RecursiveMutex CreateRecursiveMutex_SDL (void);
+CondVar CreateCondVar_SDL (void);
+#endif
+
+ThreadLocal *GetMyThreadLocal_SDL (void);
+
+void SleepThread_SDL (TimeCount sleepTime);
+void SleepThreadUntil_SDL (TimeCount wakeTime);
+void TaskSwitch_SDL (void);
+void WaitThread_SDL (Thread thread, int *status);
+void DestroyThread_SDL (Thread thread);
+
+void DestroyMutex_SDL (Mutex m);
+void LockMutex_SDL (Mutex m);
+void UnlockMutex_SDL (Mutex m);
+
+void DestroySemaphore_SDL (Semaphore sem);
+void SetSemaphore_SDL (Semaphore sem);
+void ClearSemaphore_SDL (Semaphore sem);
+
+void DestroyCondVar_SDL (CondVar c);
+void WaitCondVar_SDL (CondVar c);
+void SignalCondVar_SDL (CondVar c);
+void BroadcastCondVar_SDL (CondVar c);
+
+void DestroyRecursiveMutex_SDL (RecursiveMutex m);
+void LockRecursiveMutex_SDL (RecursiveMutex m);
+void UnlockRecursiveMutex_SDL (RecursiveMutex m);
+int GetRecursiveMutexDepth_SDL (RecursiveMutex m);
+
+#define NativeInitThreadSystem InitThreadSystem_SDL
+#define NativeUnInitThreadSystem UnInitThreadSystem_SDL
+
+#define NativeGetMyThreadLocal GetMyThreadLocal_SDL
+
+#define NativeCreateThread CreateThread_SDL
+#define NativeSleepThread SleepThread_SDL
+#define NativeSleepThreadUntil SleepThreadUntil_SDL
+#define NativeTaskSwitch TaskSwitch_SDL
+#define NativeWaitThread WaitThread_SDL
+#define NativeDestroyThread DestroyThread_SDL
+
+#define NativeCreateMutex CreateMutex_SDL
+#define NativeDestroyMutex DestroyMutex_SDL
+#define NativeLockMutex LockMutex_SDL
+#define NativeUnlockMutex UnlockMutex_SDL
+
+#define NativeCreateSemaphore CreateSemaphore_SDL
+#define NativeDestroySemaphore DestroySemaphore_SDL
+#define NativeSetSemaphore SetSemaphore_SDL
+#define NativeClearSemaphore ClearSemaphore_SDL
+
+#define NativeCreateCondVar CreateCondVar_SDL
+#define NativeDestroyCondVar DestroyCondVar_SDL
+#define NativeWaitCondVar WaitCondVar_SDL
+#define NativeSignalCondVar SignalCondVar_SDL
+#define NativeBroadcastCondVar BroadcastCondVar_SDL
+
+#define NativeCreateRecursiveMutex CreateRecursiveMutex_SDL
+#define NativeDestroyRecursiveMutex DestroyRecursiveMutex_SDL
+#define NativeLockRecursiveMutex LockRecursiveMutex_SDL
+#define NativeUnlockRecursiveMutex UnlockRecursiveMutex_SDL
+#define NativeGetRecursiveMutexDepth GetRecursiveMutexDepth_SDL
+
+#endif /* LIBS_THREADS_SDL_SDLTHREADS_H_ */
+
diff --git a/src/libs/threads/thrcommon.c b/src/libs/threads/thrcommon.c
new file mode 100644
index 0000000..bc54751
--- /dev/null
+++ b/src/libs/threads/thrcommon.c
@@ -0,0 +1,451 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+#include "libs/log.h"
+#include "libs/async.h"
+#include "libs/memlib.h"
+#include "thrcommon.h"
+
+#define LIFECYCLE_SIZE 8
+typedef struct {
+ ThreadFunction func;
+ void *data;
+ SDWORD stackSize;
+ Semaphore sem;
+ Thread value;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+#endif
+} SpawnRequest_struct;
+
+typedef SpawnRequest_struct *SpawnRequest;
+
+static Mutex lifecycleMutex;
+static SpawnRequest pendingBirth[LIFECYCLE_SIZE];
+static Thread pendingDeath[LIFECYCLE_SIZE];
+
+void
+InitThreadSystem (void)
+{
+ int i;
+ NativeInitThreadSystem ();
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ pendingBirth[i] = NULL;
+ pendingDeath[i] = NULL;
+ }
+ lifecycleMutex = CreateMutex ("Thread Lifecycle Mutex", SYNC_CLASS_RESOURCE);
+}
+
+void
+UnInitThreadSystem (void)
+{
+ NativeUnInitThreadSystem ();
+ DestroyMutex (lifecycleMutex);
+}
+
+static Thread
+FlagStartThread (SpawnRequest s)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ if (pendingBirth[i] == NULL)
+ {
+ pendingBirth[i] = s;
+ UnlockMutex (lifecycleMutex);
+ if (s->sem)
+ {
+ Thread result;
+ SetSemaphore (s->sem);
+ DestroySemaphore (s->sem);
+ result = s->value;
+ HFree (s);
+ return result;
+ }
+ return NULL;
+ }
+ }
+ log_add (log_Fatal, "Thread Lifecycle array filled. This is a fatal error! Make LIFECYCLE_SIZE something larger than %d.", LIFECYCLE_SIZE);
+ exit (EXIT_FAILURE);
+}
+
+void
+FinishThread (Thread thread)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ if (pendingDeath[i] == NULL)
+ {
+ pendingDeath[i] = thread;
+ UnlockMutex (lifecycleMutex);
+ return;
+ }
+ }
+ log_add (log_Fatal, "Thread Lifecycle array filled. This is a fatal error! Make LIFECYCLE_SIZE something larger than %d.", LIFECYCLE_SIZE);
+ exit (EXIT_FAILURE);
+}
+
+/* Only call from main thread! */
+void
+ProcessThreadLifecycles (void)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ SpawnRequest s = pendingBirth[i];
+ if (s != NULL)
+ {
+#ifdef NAMED_SYNCHRO
+ s->value = NativeCreateThread (s->func, s->data, s->stackSize, s->name);
+#else
+ s->value = NativeCreateThread (s->func, s->data, s->stackSize);
+#endif
+ if (s->sem)
+ {
+ ClearSemaphore (s->sem);
+ /* The spawning thread's FlagStartThread will clean up s */
+ }
+ else
+ {
+ /* The thread value has been lost to the game logic. We must
+ clean up s ourself. */
+ HFree (s);
+ }
+ pendingBirth[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ Thread t = pendingDeath[i];
+ if (t != NULL)
+ {
+ WaitThread (t, NULL);
+ pendingDeath[i] = NULL;
+ DestroyThread (t);
+ }
+ }
+ UnlockMutex (lifecycleMutex);
+}
+
+
+/* The Create routines look different based on whether NAMED_SYNCHRO
+ is defined or not. */
+
+#ifdef NAMED_SYNCHRO
+Thread
+CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->name = name;
+ s->sem = CreateSemaphore (0, "SpawnRequest semaphore", SYNC_CLASS_RESOURCE);
+ return FlagStartThread (s);
+}
+
+void
+StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->name = name;
+ s->sem = NULL;
+ FlagStartThread (s);
+}
+
+Mutex
+CreateMutex_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateMutex (name, syncClass);
+}
+
+Semaphore
+CreateSemaphore_Core (DWORD initial, const char *name, DWORD syncClass)
+{
+ return NativeCreateSemaphore (initial, name, syncClass);
+}
+
+RecursiveMutex
+CreateRecursiveMutex_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateRecursiveMutex (name, syncClass);
+}
+
+CondVar
+CreateCondVar_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateCondVar (name, syncClass);
+}
+
+#else
+/* These are the versions of Create* without the names. */
+Thread
+CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->sem = CreateSemaphore (0, "SpawnRequest semaphore", SYNC_CLASS_RESOURCE);
+ return FlagStartThread (s);
+}
+
+void
+StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->sem = NULL;
+ FlagStartThread (s);
+}
+
+Mutex
+CreateMutex_Core (void)
+{
+ return NativeCreateMutex ();
+}
+
+Semaphore
+CreateSemaphore_Core (DWORD initial)
+{
+ return NativeCreateSemaphore (initial);
+}
+
+RecursiveMutex
+CreateRecursiveMutex_Core (void)
+{
+ return NativeCreateRecursiveMutex ();
+}
+
+CondVar
+CreateCondVar_Core (void)
+{
+ return NativeCreateCondVar ();
+}
+#endif
+
+void
+DestroyThread (Thread t)
+{
+ NativeDestroyThread (t);
+}
+
+ThreadLocal *
+CreateThreadLocal (void)
+{
+ ThreadLocal *tl = HMalloc (sizeof (ThreadLocal));
+ tl->flushSem = CreateSemaphore (0, "FlushGraphics", SYNC_CLASS_VIDEO);
+ return tl;
+}
+
+void
+DestroyThreadLocal (ThreadLocal *tl)
+{
+ DestroySemaphore (tl->flushSem);
+ HFree (tl);
+}
+
+ThreadLocal *
+GetMyThreadLocal (void)
+{
+ return NativeGetMyThreadLocal ();
+}
+
+void
+WaitThread (Thread thread, int *status)
+{
+ NativeWaitThread (thread, status);
+}
+
+#ifdef DEBUG_SLEEP
+extern uint32 mainThreadId;
+extern uint32 SDL_ThreadID(void);
+#endif /* DEBUG_SLEEP */
+
+void
+HibernateThread (TimePeriod timePeriod)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() == mainThreadId)
+ log_add (log_Debug, "HibernateThread called from main thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ NativeSleepThread (timePeriod);
+}
+
+void
+HibernateThreadUntil (TimeCount wakeTime)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() == mainThreadId)
+ log_add (log_Debug, "HibernateThreadUntil called from main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ NativeSleepThreadUntil (wakeTime);
+}
+
+void
+SleepThread (TimePeriod timePeriod)
+{
+ TimeCount now;
+
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() != mainThreadId)
+ log_add (log_Debug, "SleepThread called from non-main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ now = GetTimeCounter ();
+ SleepThreadUntil (now + timePeriod);
+}
+
+// Sleep until wakeTime, but call asynchrounous operations until then.
+void
+SleepThreadUntil (TimeCount wakeTime)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() != mainThreadId)
+ log_add (log_Debug, "SleepThreadUntil called from non-main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ for (;;) {
+ uint32 nextTimeMs;
+ TimeCount nextTime;
+ TimeCount now;
+
+ Async_process ();
+
+ now = GetTimeCounter ();
+ if (wakeTime <= now)
+ return;
+
+ nextTimeMs = Async_timeBeforeNextMs ();
+ nextTime = (nextTimeMs / 1000) * ONE_SECOND +
+ ((nextTimeMs % 1000) * ONE_SECOND / 1000);
+ // Overflow-safe conversion.
+ if (wakeTime < nextTime)
+ nextTime = wakeTime;
+
+ NativeSleepThreadUntil (nextTime);
+ }
+}
+
+void
+TaskSwitch (void)
+{
+ NativeTaskSwitch ();
+}
+
+void
+DestroyMutex (Mutex sem)
+{
+ NativeDestroyMutex (sem);
+}
+
+void
+LockMutex (Mutex sem)
+{
+ NativeLockMutex (sem);
+}
+
+void
+UnlockMutex (Mutex sem)
+{
+ NativeUnlockMutex (sem);
+}
+
+void
+DestroySemaphore (Semaphore sem)
+{
+ NativeDestroySemaphore (sem);
+}
+
+void
+SetSemaphore (Semaphore sem)
+{
+ NativeSetSemaphore (sem);
+}
+
+void
+ClearSemaphore (Semaphore sem)
+{
+ NativeClearSemaphore (sem);
+}
+
+void
+DestroyCondVar (CondVar cv)
+{
+ NativeDestroyCondVar (cv);
+}
+
+void
+WaitCondVar (CondVar cv)
+{
+ NativeWaitCondVar (cv);
+}
+
+void
+SignalCondVar (CondVar cv)
+{
+ NativeSignalCondVar (cv);
+}
+
+void
+BroadcastCondVar (CondVar cv)
+{
+ NativeBroadcastCondVar (cv);
+}
+
+void
+DestroyRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeDestroyRecursiveMutex (mutex);
+}
+
+void
+LockRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeLockRecursiveMutex (mutex);
+}
+
+void
+UnlockRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeUnlockRecursiveMutex (mutex);
+}
+
+int
+GetRecursiveMutexDepth (RecursiveMutex mutex)
+{
+ return NativeGetRecursiveMutexDepth (mutex);
+}
diff --git a/src/libs/threads/thrcommon.h b/src/libs/threads/thrcommon.h
new file mode 100644
index 0000000..d6d6d41
--- /dev/null
+++ b/src/libs/threads/thrcommon.h
@@ -0,0 +1,28 @@
+/*
+ * 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 LIBS_THREADS_THRCOMMON_H_
+#define LIBS_THREADS_THRCOMMON_H_
+
+
+#if defined(THREADLIB_SDL)
+# include "sdl/sdlthreads.h"
+#elif defined(THREADLIB_PTHREAD)
+# include "pthread/posixthreads.h"
+#endif /* defined(THREADLIB_PTHREAD) */
+
+
+#endif /* _THR_COMMON_H */