SDL: Add Thread drivers

From 52f00833b0b38129b6d887617112d978e1d9ee29 Mon Sep 17 00:00:00 2001
From: Francisco Javier Trujillo Mata <[EMAIL REDACTED]>
Date: Mon, 13 Jun 2022 20:58:18 +0200
Subject: [PATCH] Add Thread drivers

---
 CMakeLists.txt                   |   6 ++
 include/SDL_config.h.cmake       |   1 +
 src/thread/SDL_thread_c.h        |   2 +
 src/thread/ps2/SDL_syssem.c      | 159 +++++++++++++++++++++++++++++++
 src/thread/ps2/SDL_systhread.c   | 140 +++++++++++++++++++++++++++
 src/thread/ps2/SDL_systhread_c.h |  24 +++++
 6 files changed, 332 insertions(+)
 create mode 100644 src/thread/ps2/SDL_syssem.c
 create mode 100644 src/thread/ps2/SDL_systhread.c
 create mode 100644 src/thread/ps2/SDL_systhread_c.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d27fa084bcb..e1e1dfc76c5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2524,6 +2524,12 @@ elseif(PSP)
 elseif(PS2)
   list(APPEND EXTRA_CFLAGS "-DPS2")
 
+  if(SDL_THREADS)
+    set(SDL_THREAD_PS2 1)
+    file(GLOB PS2_THREAD_SOURCES ${SDL2_SOURCE_DIR}/src/thread/generic/SDL_systls.c ${SDL2_SOURCE_DIR}/src/thread/generic/SDL_sysmutex.c ${SDL2_SOURCE_DIR}/src/thread/ps2/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${PS2_THREAD_SOURCES})
+    set(HAVE_SDL_THREADS TRUE)
+  endif()
   if(SDL_TIMERS)
     set(SDL_TIMER_PS2 1)
     file(GLOB PS2_TIMER_SOURCES ${SDL2_SOURCE_DIR}/src/timer/ps2/*.c)
diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake
index 5d476194cb5..7048dcdd3f5 100644
--- a/include/SDL_config.h.cmake
+++ b/include/SDL_config.h.cmake
@@ -378,6 +378,7 @@
 #cmakedefine SDL_THREAD_OS2 @SDL_THREAD_OS2@
 #cmakedefine SDL_THREAD_VITA @SDL_THREAD_VITA@
 #cmakedefine SDL_THREAD_PSP @SDL_THREAD_PSP@
+#cmakedefine SDL_THREAD_PS2 @SDL_THREAD_PS2@
 
 /* Enable various timer systems */
 #cmakedefine SDL_TIMER_HAIKU @SDL_TIMER_HAIKU@
diff --git a/src/thread/SDL_thread_c.h b/src/thread/SDL_thread_c.h
index b4df526d1cc..55a4a8875d4 100644
--- a/src/thread/SDL_thread_c.h
+++ b/src/thread/SDL_thread_c.h
@@ -32,6 +32,8 @@
 #include "pthread/SDL_systhread_c.h"
 #elif SDL_THREAD_WINDOWS
 #include "windows/SDL_systhread_c.h"
+#elif SDL_THREAD_PS2
+#include "ps2/SDL_systhread_c.h"
 #elif SDL_THREAD_PSP
 #include "psp/SDL_systhread_c.h"
 #elif SDL_THREAD_VITA
diff --git a/src/thread/ps2/SDL_syssem.c b/src/thread/ps2/SDL_syssem.c
new file mode 100644
index 00000000000..e6b0aee2558
--- /dev/null
+++ b/src/thread/ps2/SDL_syssem.c
@@ -0,0 +1,159 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_PS2
+
+/* Semaphore functions for the PS2. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <timer_alarm.h>
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+
+#include <kernel.h>
+
+struct SDL_semaphore {
+    s32  semid;
+};
+
+static void usercb(struct timer_alarm_t *alarm, void *arg) {
+    iReleaseWaitThread((int)arg);
+}
+
+/* Create a semaphore */
+SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
+{
+    SDL_sem *sem;
+    ee_sema_t sema;
+
+    sem = (SDL_sem *) SDL_malloc(sizeof(*sem));
+    if (sem != NULL) {
+        /* TODO: Figure out the limit on the maximum value. */
+        sema.init_count = initial_value;
+        sema.max_count  = 255;
+        sema.option     = 0;
+        sem->semid = CreateSema(&sema);
+
+        if (sem->semid < 0) {
+            SDL_SetError("Couldn't create semaphore");
+            SDL_free(sem);
+            sem = NULL;
+        }
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return sem;
+}
+
+/* Free the semaphore */
+void SDL_DestroySemaphore(SDL_sem *sem)
+{
+    if (sem != NULL) {
+        if (sem->semid > 0) {
+            DeleteSema(sem->semid);
+            sem->semid = 0;
+        }
+
+        SDL_free(sem);
+    }
+}
+
+int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
+{
+    int ret;
+    struct timer_alarm_t alarm;
+    InitializeTimerAlarm(&alarm);
+    
+    if (sem == NULL) {
+        SDL_InvalidParamError("sem");
+        return 0;
+    }
+
+    if (timeout == 0) {
+        if (PollSema(sem->semid) < 0) {
+            return SDL_MUTEX_TIMEDOUT;
+        }
+        return 0;
+    }
+
+    if (timeout != SDL_MUTEX_MAXWAIT) {
+        SetTimerAlarm(&alarm, MSec2TimerBusClock(timeout), &usercb, (void *)GetThreadId());
+    }
+
+    ret = WaitSema(sem->semid);
+    StopTimerAlarm(&alarm);
+
+    if (ret < 0)
+        return SDL_MUTEX_TIMEDOUT;
+    return 0; //Wait condition satisfied.
+}
+
+int SDL_SemTryWait(SDL_sem *sem)
+{
+    return SDL_SemWaitTimeout(sem, 0);
+}
+
+int SDL_SemWait(SDL_sem *sem)
+{
+    return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
+}
+
+/* Returns the current count of the semaphore */
+Uint32 SDL_SemValue(SDL_sem *sem)
+{
+    ee_sema_t info;
+
+    if (sem == NULL) {
+        SDL_InvalidParamError("sem");
+        return 0;
+    }
+
+    if (ReferSemaStatus(sem->semid, &info) >= 0) {
+        return info.count;
+    }
+
+    return 0;
+}
+
+int SDL_SemPost(SDL_sem *sem)
+{
+    int res;
+
+    if (sem == NULL) {
+        return SDL_InvalidParamError("sem");
+    }
+
+    res = SignalSema(sem->semid);
+    if (res < 0) {
+        return SDL_SetError("sceKernelSignalSema() failed");
+    }
+
+    return 0;
+}
+
+#endif /* SDL_THREAD_PS2 */
+
+/* vim: ts=4 sw=4
+ */
diff --git a/src/thread/ps2/SDL_systhread.c b/src/thread/ps2/SDL_systhread.c
new file mode 100644
index 00000000000..4df65abf1d1
--- /dev/null
+++ b/src/thread/ps2/SDL_systhread.c
@@ -0,0 +1,140 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_THREAD_PS2
+
+/* PS2 thread management routines for SDL */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL_error.h"
+#include "SDL_thread.h"
+#include "../SDL_systhread.h"
+#include "../SDL_thread_c.h"
+#include <kernel.h>
+
+static void FinishThread(SDL_Thread *thread) {
+    ee_thread_status_t info;
+    int res;
+
+    res = ReferThreadStatus(thread->handle, &info);
+    TerminateThread(thread->handle);
+    DeleteThread(thread->handle);
+    DeleteSema((int)thread->endfunc);
+
+    if (res > 0) {
+        SDL_free(info.stack);
+    }
+}
+
+static int childThread(void *arg)
+{
+    SDL_Thread *thread = (SDL_Thread *)arg;
+    int res = thread->userfunc(thread->userdata);
+    SignalSema((int)thread->endfunc);
+    return res;
+}
+
+int SDL_SYS_CreateThread(SDL_Thread *thread)
+{
+    ee_thread_status_t status;
+    ee_thread_t eethread;
+    ee_sema_t sema;
+    size_t stack_size;
+    int priority = 32;
+
+    /* Set priority of new thread to the same as the current thread */
+    // status.size = sizeof(ee_thread_t);
+    if (ReferThreadStatus(GetThreadId(), &status) == 0) {
+        priority = status.current_priority;
+    }
+
+    stack_size = thread->stacksize ? ((int) thread->stacksize) : 0x1800;
+
+
+    /* Create EE Thread */
+	eethread.attr = 0;
+	eethread.option = 0;
+	eethread.func = &childThread;
+	eethread.stack = SDL_malloc(stack_size);
+	eethread.stack_size = stack_size;
+	eethread.gp_reg = &_gp;
+	eethread.initial_priority = priority;
+	thread->handle = CreateThread(&eethread);
+
+    if (thread->handle < 0) {
+        return SDL_SetError("CreateThread() failed");
+    }
+
+    // Prepare el semaphore for the ending function
+    sema.init_count = 0;
+	sema.max_count = 1;
+	sema.option = 0;
+    thread->endfunc = (void *)CreateSema(&sema);
+
+    return StartThread(thread->handle, thread);
+}
+
+void SDL_SYS_SetupThread(const char *name)
+{
+    /* Do nothing. */
+}
+
+SDL_threadID SDL_ThreadID(void)
+{
+    return (SDL_threadID) GetThreadId();
+}
+
+void SDL_SYS_WaitThread(SDL_Thread *thread)
+{
+    WaitSema((int)thread->endfunc);
+    ReleaseWaitThread(thread->handle);
+    FinishThread(thread);
+}
+
+void SDL_SYS_DetachThread(SDL_Thread *thread)
+{
+    /* Do nothing. */
+}
+
+int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
+{
+    int value;
+
+    if (priority == SDL_THREAD_PRIORITY_LOW) {
+        value = 111;
+    } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
+        value = 32;
+    } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
+        value = 16;
+    } else {
+        value = 50;
+    }
+
+    return ChangeThreadPriority(GetThreadId(),value);
+}
+
+#endif /* SDL_THREAD_PS2 */
+
+/* vim: ts=4 sw=4
+ */
diff --git a/src/thread/ps2/SDL_systhread_c.h b/src/thread/ps2/SDL_systhread_c.h
new file mode 100644
index 00000000000..ffeca76d62f
--- /dev/null
+++ b/src/thread/ps2/SDL_systhread_c.h
@@ -0,0 +1,24 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include <stdint.h>
+
+typedef int32_t SYS_ThreadHandle;