From 8a0ab6b14e00b4beaaecde30251d9cb12dfb6b13 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Fri, 19 Nov 2021 17:25:38 +0100 Subject: [PATCH 1/2] Adding pthreadglue support --- Dockerfile | 3 +- configure.ac | 1 + src/Makefile.am | 1 + src/base/build.mak | 2 +- src/base/build_prx.mak | 2 +- src/libcglue/init.c | 4 + src/libcglue/sleep.c | 3 + src/libpthreadglue/Makefile.am | 42 ++ src/libpthreadglue/osal.c | 788 ++++++++++++++++++++++++++++++++ src/libpthreadglue/tls-helper.c | 254 ++++++++++ src/user/pspmoduleinfo.h | 4 + 11 files changed, 1101 insertions(+), 3 deletions(-) create mode 100644 src/libpthreadglue/Makefile.am create mode 100644 src/libpthreadglue/osal.c create mode 100644 src/libpthreadglue/tls-helper.c diff --git a/Dockerfile b/Dockerfile index b84dcbce..fec01edf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,9 +13,10 @@ RUN cd /src && \ make -j $(getconf _NPROCESSORS_ONLN) && \ make -j $(getconf _NPROCESSORS_ONLN) install -## gcc needs to include libcglue libpsputility libpsprtc libpspnet_inet libpspnet_resolver libpspmodinfo libpspuser libpspkernel +## gcc needs to include libcglue libpthreadglue libpsputility libpsprtc libpspnet_inet libpspnet_resolver libpspmodinfo libpspuser libpspkernel ## from pspsdk to be able to build executables, because they are part of the standard libraries RUN ln -sf "$PSPDEV/psp/sdk/lib/libcglue.a" "$PSPDEV/psp/lib/libcglue.a" || { exit 1; } +RUN ln -sf "$PSPDEV/psp/sdk/lib/libpthreadglue.a" "$PSPDEV/psp/lib/libpthreadglue.a" || { exit 1; } RUN ln -sf "$PSPDEV/psp/sdk/lib/libpsputility.a" "$PSPDEV/psp/lib/libpsputility.a" || { exit 1; } RUN ln -sf "$PSPDEV/psp/sdk/lib/libpsprtc.a" "$PSPDEV/psp/lib/libpsprtc.a" || { exit 1; } RUN ln -sf "$PSPDEV/psp/sdk/lib/libpspnet_inet.a" "$PSPDEV/psp/lib/libpspnet_inet.a" || { exit 1; } diff --git a/configure.ac b/configure.ac index abd1581d..67e591b0 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,7 @@ AC_CONFIG_FILES([Makefile src/hprm/Makefile src/kernel/Makefile src/libcglue/Makefile + src/libpthreadglue/Makefile src/modinfo/Makefile src/mp3/Makefile src/mpeg/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index fd4c7d69..2bba1f6f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,6 +13,7 @@ SUBDIRS = \ hprm \ kernel \ libcglue \ + libpthreadglue \ modinfo \ mp3 \ mpeg \ diff --git a/src/base/build.mak b/src/base/build.mak index ba05225c..9081a75f 100644 --- a/src/base/build.mak +++ b/src/base/build.mak @@ -65,7 +65,7 @@ LDFLAGS += -Wl,-zmax-page-size=128 ifeq ($(USE_KERNEL_LIBS),1) LIBS := -nostdlib $(LIBS) -lpspdebug -lpspdisplay_driver -lpspctrl_driver -lpspmodinfo -lpspsdk -lpspkernel else -LIBS := $(LIBS) -lpspdebug -lpspdisplay -lpspge -lpspctrl -lpspsdk \ +LIBS := $(LIBS) -lpspdebug -lpspdisplay -lpspge -lpspctrl \ -lpspnet -lpspnet_apctl endif diff --git a/src/base/build_prx.mak b/src/base/build_prx.mak index 7e1a6194..e4acbd17 100644 --- a/src/base/build_prx.mak +++ b/src/base/build_prx.mak @@ -33,7 +33,7 @@ LDFLAGS := $(addprefix -L,$(LIBDIR)) -Wl,-q,-T$(PSPSDK)/lib/linkfile.prx -nosta ifeq ($(USE_KERNEL_LIBS),1) LIBS := -nostdlib $(LIBS) -lpspdebug -lpspdisplay_driver -lpspctrl_driver -lpspmodinfo -lpspsdk -lpspkernel else -LIBS := $(LIBS) -lpspdebug -lpspdisplay -lpspge -lpspctrl -lpspsdk +LIBS := $(LIBS) -lpspdebug -lpspdisplay -lpspge -lpspctrl endif ifeq ($(PSP_FW_VERSION),) diff --git a/src/libcglue/init.c b/src/libcglue/init.c index ca630196..ed3b6654 100644 --- a/src/libcglue/init.c +++ b/src/libcglue/init.c @@ -23,6 +23,7 @@ void __init_cwd(char *argv_0); void __timezone_update(); void __fdman_init(); void __init_mutex(); +void pthread_init(); void __psp_free_heap(); void __deinit_mutex(); @@ -45,6 +46,9 @@ void __libcglue_init(int argc, char *argv[]) /* Initialize filedescriptor management */ __fdman_init(); + /* Initialize pthread library */ + pthread_init(); + /* Initialize cwd from this program's path */ __init_cwd(argv[0]); diff --git a/src/libcglue/sleep.c b/src/libcglue/sleep.c index 0ab3870e..444a61f9 100644 --- a/src/libcglue/sleep.c +++ b/src/libcglue/sleep.c @@ -19,6 +19,9 @@ /* Fuctions from errno.c */ int __set_errno(int code); +/* Fuctions from errno.c */ +int __set_errno(int code); + #ifdef F_nanosleep /* note: we don't use rem as we have no signals */ int nanosleep(const struct timespec *req, struct timespec *rem) diff --git a/src/libpthreadglue/Makefile.am b/src/libpthreadglue/Makefile.am new file mode 100644 index 00000000..4b571005 --- /dev/null +++ b/src/libpthreadglue/Makefile.am @@ -0,0 +1,42 @@ + +libdir = @PSPSDK_LIBDIR@ + +CC = @PSP_CC@ +CCAS = $(CC) +AR = @PSP_AR@ +RANLIB = @PSP_RANLIB@ + +CPPFLAGS = -I$(top_srcdir)/src/base \ + -I$(top_srcdir)/src/debug \ + -I$(top_srcdir)/src/kernel \ + -I$(top_srcdir)/src/sdk \ + -I$(top_srcdir)/src/user + +CFLAGS = @PSPSDK_CFLAGS@ +CCASFLAGS = $(CFLAGS) + +OSAL_OBJS = __threadDataKey.o __getThreadData.o __pspStubThreadEntry.o \ + pte_osInit.o pte_osTerminate.o pte_osThreadCreate.o pte_osThreadStart.o pte_osThreadDelete.o \ + pte_osThreadExitAndDelete.o pte_osThreadExit.o pte_osThreadWaitForEnd.o pte_osThreadGetHandle.o pte_osThreadGetPriority.o pthread_num_processors_np.o \ + pte_osThreadSetPriority.o pte_osThreadCancel.o pte_osThreadCheckCancel.o pte_osThreadSleep.o pte_osThreadGetMinPriority.o pte_osThreadGetMaxPriority.o \ + pte_osThreadGetDefaultPriority.o pte_osMutexCreate.o pte_osMutexDelete.o pte_osMutexLock.o pte_osMutexTimedLock.o pte_osMutexUnlock.o \ + pte_osSemaphoreCreate.o pte_osSemaphoreDelete.o pte_osSemaphorePost.o pte_osSemaphorePend.o pte_osSemaphoreCancellablePend.o \ + pte_osAtomicExchange.o pte_osAtomicCompareExchange.o pte_osAtomicExchangeAdd.o pte_osAtomicDecrement.o pte_osAtomicIncrement.o + +TLS_HELPER_OBJS = __keysUsed.o __maxTlsValues.o __globalTlsLock.o __globalTls.o pteTlsGlobalInit.o pteTlsThreadInit.o \ + __pteTlsAlloc.o pteTlsGetValue.o __pteTlsSetValue.o __getTlsStructFromThread.o getTlsStructFromThread.o pteTlsFree.o \ + pteTlsThreadDestroy.o pteTlsGlobalDestroy.o pte_osTlsSetValue.o pte_osTlsGetValue.o pte_osTlsAlloc.o pte_osTlsFree.o + +lib_LIBRARIES = libpthreadglue.a + +libpthreadglue_a_SOURCES = \ + osal.c \ + tls-helper.c + +libpthreadglue_a_LIBADD = $(OSAL_OBJS) $(TLS_HELPER_OBJS) + +$(OSAL_OBJS): osal.c + $(AM_V_CC)$(COMPILE) -DF_$* $< -c -o $@ + +$(TLS_HELPER_OBJS): tls-helper.c + $(AM_V_CC)$(COMPILE) -DF_$* $< -c -o $@ \ No newline at end of file diff --git a/src/libpthreadglue/osal.c b/src/libpthreadglue/osal.c new file mode 100644 index 00000000..f7128d5b --- /dev/null +++ b/src/libpthreadglue/osal.c @@ -0,0 +1,788 @@ +/* + * PSP Software Development Kit - https://github.com/pspdev + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * osal.c - Pthread compatible system calls. + * + * Copyright (c) 2021 Francisco J Trujillo + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +typedef int pte_osThreadHandle; +typedef int pte_osSemaphoreHandle; +typedef int pte_osMutexHandle; +#include + +#define MAX_PSP_UID 2048 // SWAG +#define DEFAULT_STACK_SIZE_BYTES 4096 +#define PSP_MAX_TLS 32 + +pte_osResult pteTlsGlobalInit(int maxEntries); +void * pteTlsThreadInit(void); + +pte_osResult __pteTlsAlloc(unsigned int *pKey); +void * pteTlsGetValue(void *pTlsThreadStruct, unsigned int index); +pte_osResult __pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void * value); +void *__getTlsStructFromThread(SceUID thid); +pte_osResult pteTlsFree(unsigned int index); + +void pteTlsThreadDestroy(void * pTlsThreadStruct); +void pteTlsGlobalDestroy(void); + +#if 0 +#define PSP_DEBUG(x) printf(x) +#else +#define PSP_DEBUG(x) +#endif + +#define POLLING_DELAY_IN_us 100 + +/* TLS key used to access pspThreadData struct for reach thread. */ +#ifdef F___threadDataKey +unsigned int __threadDataKey; +#else +extern unsigned int __threadDataKey; +#endif + +extern void *__globalTls; + +/* + * Data stored on a per-thread basis - allocated in pte_osThreadCreate + * and freed in pte_osThreadDelete. + */ +typedef struct pspThreadData + { + /* Entry point and parameters to thread's main function */ + pte_osThreadEntryPoint entryPoint; + void * argv; + + /* Semaphore used for cancellation. Posted to by pte_osThreadCancel, + polled in pte_osSemaphoreCancellablePend */ + SceUID cancelSem; + + } pspThreadData; + + +/**************************************************************************** + * + * Helper functions + * + ***************************************************************************/ +#ifdef F___getThreadData +pspThreadData *__getThreadData(SceUID threadHandle) +{ + pspThreadData *pThreadData; + void *pTls; + + pTls = __getTlsStructFromThread(threadHandle); + pThreadData = (pspThreadData *) pteTlsGetValue(pTls, __threadDataKey); + + return pThreadData; +} +#else +pspThreadData *__getThreadData(SceUID threadHandle); +#endif + +/* A new thread's stub entry point. It retrieves the real entry point from the per thread control + * data as well as any parameters to this function, and then calls the entry point. + */ +#ifdef F___pspStubThreadEntry +int __pspStubThreadEntry(unsigned int argc, void *argv) +{ + int result; + pspThreadData *pThreadData; + + pThreadData = __getThreadData(sceKernelGetThreadId()); + result = (*(pThreadData->entryPoint))(pThreadData->argv); + + return result; +} +#else +extern int __pspStubThreadEntry(unsigned int argc, void *argv); +#endif + +/**************************************************************************** + * + * Initialization + * + ***************************************************************************/ +#ifdef F_pte_osInit +pte_osResult pte_osInit(void) +{ + pte_osResult result; + pspThreadData *pThreadData; + char cancelSemName[64]; + + /* Allocate and initialize TLS support */ + result = pteTlsGlobalInit(PSP_MAX_TLS); + + if (result == PTE_OS_OK) { + /* Allocate a key that we use to store control information (e.g. cancellation semaphore) per thread */ + result = __pteTlsAlloc(&__threadDataKey); + + if (result == PTE_OS_OK) { + /* Initialize the structure used to emulate TLS for + * non-POSIX threads + */ + __globalTls = pteTlsThreadInit(); + + /* Also create a "thread data" structure for a single non-POSIX thread. */ + + /* Allocate some memory for our per-thread control data. We use this for: + * 1. Entry point and parameters for the user thread's main function. + * 2. Semaphore used for thread cancellation. + */ + pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData)); + + if (pThreadData == NULL) { + result = PTE_OS_NO_RESOURCES; + } else { + /* Save a pointer to our per-thread control data as a TLS value */ + __pteTlsSetValue(__globalTls, __threadDataKey, pThreadData); + + /* Create a semaphore used to cancel threads */ + snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSemGlobal"); + + pThreadData->cancelSem = sceKernelCreateSema(cancelSemName, + 0, /* attributes (default) */ + 0, /* initial value */ + 255, /* maximum value */ + 0); /* options (default) */ + result = PTE_OS_OK; + } + } + } + + return result; +} +#endif + +#ifdef F_pte_osTerminate +pte_osResult pte_osTerminate(void) { + pteTlsGlobalDestroy(); + return PTE_OS_OK; +} +#endif + +/**************************************************************************** + * + * Threads + * + ***************************************************************************/ +#ifdef F_pte_osThreadCreate +pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint, + int stackSize, + int initialPriority, + void *argv, + pte_osThreadHandle* ppte_osThreadHandle) +{ + char threadName[64]; + char cancelSemName[64]; + static int threadNum = 1; + int pspAttr; + void *pTls; + SceUID threadId; + pte_osResult result; + pspThreadData *pThreadData; + + if (threadNum++ > MAX_PSP_UID) { + threadNum = 0; + } + + /* Make sure that the stack we're going to allocate is big enough */ + if (stackSize < DEFAULT_STACK_SIZE_BYTES) { + stackSize = DEFAULT_STACK_SIZE_BYTES; + } + + /* Allocate TLS structure for this thread. */ + pTls = pteTlsThreadInit(); + if (pTls == NULL) { + PSP_DEBUG("pteTlsThreadInit: PTE_OS_NO_RESOURCES\n"); + result = PTE_OS_NO_RESOURCES; + goto FAIL0; + } + + /* Allocate some memory for our per-thread control data. We use this for: + * 1. Entry point and parameters for the user thread's main function. + * 2. Semaphore used for thread cancellation. + */ + pThreadData = (pspThreadData *) malloc(sizeof(pspThreadData)); + + if (pThreadData == NULL) { + pteTlsThreadDestroy(pTls); + + PSP_DEBUG("malloc(pspThreadData): PTE_OS_NO_RESOURCES\n"); + result = PTE_OS_NO_RESOURCES; + goto FAIL0; + } + + /* Save a pointer to our per-thread control data as a TLS value */ + __pteTlsSetValue(pTls, __threadDataKey, pThreadData); + + pThreadData->entryPoint = entryPoint; + pThreadData->argv = argv; + + /* Create a semaphore used to cancel threads */ + snprintf(cancelSemName, sizeof(cancelSemName), "pthread_cancelSem%04d", threadNum); + + pThreadData->cancelSem = sceKernelCreateSema(cancelSemName, + 0, /* attributes (default) */ + 0, /* initial value */ + 255, /* maximum value */ + 0); /* options (default) */ + + + /* In order to emulate TLS functionality, we append the address of the TLS structure that we + * allocated above to the thread's name. To set or get TLS values for this thread, the user + * needs to get the name of the thread from the OS and then parse the name to extract + * a pointer to the TLS structure. + */ + snprintf(threadName, sizeof(threadName), "pthread%04d__%x", threadNum, (unsigned int) pTls); + + pspAttr = 0; + + // printf("%s %p %d %d %d\n",threadName, __pspStubThreadEntry, initialPriority, stackSize, pspAttr); + threadId = sceKernelCreateThread(threadName, + __pspStubThreadEntry, + initialPriority, + stackSize, + pspAttr, + NULL); + + if (threadId == (SceUID) SCE_KERNEL_ERROR_NO_MEMORY) { + free(pThreadData); + pteTlsThreadDestroy(pTls); + + PSP_DEBUG("sceKernelCreateThread: PTE_OS_NO_RESOURCES\n"); + result = PTE_OS_NO_RESOURCES; + } else if (threadId < 0) { + free(pThreadData); + pteTlsThreadDestroy(pTls); + + PSP_DEBUG("sceKernelCreateThread: PTE_OS_GENERAL_FAILURE\n"); + result = PTE_OS_GENERAL_FAILURE; + } else { + *ppte_osThreadHandle = threadId; + result = PTE_OS_OK; + } + +FAIL0: + return result; +} +#endif + +#ifdef F_pte_osThreadStart +pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle) +{ + sceKernelStartThread(osThreadHandle, 0, 0); + + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osThreadDelete +pte_osResult pte_osThreadDelete(pte_osThreadHandle handle) +{ + pspThreadData *pThreadData; + void *pTls; + + pTls = __getTlsStructFromThread(handle); + pThreadData = __getThreadData(handle); + sceKernelDeleteSema(pThreadData->cancelSem); + free(pThreadData); + pteTlsThreadDestroy(pTls); + sceKernelDeleteThread(handle); + + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osThreadExitAndDelete +pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle) +{ + pte_osThreadDelete(handle); + sceKernelExitDeleteThread(0); + + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osThreadExit +void pte_osThreadExit() +{ + sceKernelExitThread(0); +} +#endif + +/* + * This has to be cancellable, so we can't just call sceKernelWaitThreadEnd. + * Instead, poll on this in a loop, like we do for a cancellable semaphore. + */ +#ifdef F_pte_osThreadWaitForEnd +pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle) +{ + pte_osResult result; + pspThreadData *pThreadData; + + pThreadData = __getThreadData(sceKernelGetThreadId()); + + while (1) { + SceKernelThreadRunStatus info; + + /* Poll task to see if it has ended */ + memset(&info,0,sizeof(info)); + info.size = sizeof(info); + sceKernelReferThreadRunStatus(threadHandle, &info); + + if (info.status == PSP_THREAD_STOPPED) { + /* Thread has ended */ + result = PTE_OS_OK; + break; + } else { + SceKernelSemaInfo semInfo; + + if (pThreadData != NULL) { + SceUID osResult; + + osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo); + if (osResult == SCE_KERNEL_ERROR_OK) { + if (semInfo.currentCount > 0) { + result = PTE_OS_INTERRUPTED; + break; + } else { + /* Nothing found and not timed out yet; let's yield so we're not + * in busy loop. + */ + sceKernelDelayThread(POLLING_DELAY_IN_us); + } + } else { + result = PTE_OS_GENERAL_FAILURE; + break; + } + } + } + } + + return result; +} +#endif + +#ifdef F_pte_osThreadGetHandle +pte_osThreadHandle pte_osThreadGetHandle(void) +{ + return sceKernelGetThreadId(); +} +#endif + +#ifdef F_pte_osThreadGetPriority +int pte_osThreadGetPriority(pte_osThreadHandle threadHandle) +{ + SceKernelThreadInfo thinfo; + + thinfo.size = sizeof(SceKernelThreadInfo); + sceKernelReferThreadStatus(threadHandle, &thinfo); + + return thinfo.currentPriority; +} +#endif + +#ifdef F_pte_osThreadSetPriority +pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority) +{ + sceKernelChangeThreadPriority(threadHandle, newPriority); + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osThreadCancel +pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle) +{ + SceUID osResult; + pte_osResult result; + pspThreadData *pThreadData; + + pThreadData = __getThreadData(threadHandle); + osResult = sceKernelSignalSema(pThreadData->cancelSem, 1); + + if (osResult == SCE_KERNEL_ERROR_OK) { + result = PTE_OS_OK; + } else { + result = PTE_OS_GENERAL_FAILURE; + } + + return result; +} +#endif + +#ifdef F_pte_osThreadCheckCancel +pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle) +{ + pspThreadData *pThreadData; + SceKernelSemaInfo semInfo; + SceUID osResult; + pte_osResult result; + + pThreadData = __getThreadData(threadHandle); + if (pThreadData != NULL) { + osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo); + + if (osResult == SCE_KERNEL_ERROR_OK) { + if (semInfo.currentCount > 0) { + result = PTE_OS_INTERRUPTED; + } else { + result = PTE_OS_OK; + } + } else { + /* sceKernelReferSemaStatus returned an error */ + result = PTE_OS_GENERAL_FAILURE; + } + } else { + /* For some reason, we couldn't get thread data */ + result = PTE_OS_GENERAL_FAILURE; + } + + return result; +} +#endif + +#ifdef F_pte_osThreadSleep +void pte_osThreadSleep(unsigned int msecs) +{ + sceKernelDelayThread(msecs*1000); +} +#endif + +#ifdef F_pte_osThreadGetMinPriority +int pte_osThreadGetMinPriority() +{ + return 17; +} +#endif + +#ifdef F_pte_osThreadGetMaxPriority +int pte_osThreadGetMaxPriority() +{ + return 30; +} +#endif + +#ifdef F_pte_osThreadGetDefaultPriority +int pte_osThreadGetDefaultPriority() +{ + return 18; +} +#endif + +#ifdef F_pthread_num_processors_np +int pthread_num_processors_np(void) +{ + return 1; +} +#endif + +/**************************************************************************** + * + * Mutexes + * + ****************************************************************************/ +#ifdef F_pte_osMutexCreate +pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle) +{ + static int mutexCtr = 0; + char mutexName[32]; + pte_osMutexHandle handle; + + if (mutexCtr++ > MAX_PSP_UID) { + mutexCtr = 0; + } + + snprintf(mutexName,sizeof(mutexName),"mutex%d",mutexCtr); + handle = sceKernelCreateSema(mutexName, + 0, /* attributes (default) */ + 1, /* initial value */ + 1, /* maximum value */ + 0); /* options (default) */ + + *pHandle = handle; + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osMutexDelete +pte_osResult pte_osMutexDelete(pte_osMutexHandle handle) +{ + sceKernelDeleteSema(handle); + + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osMutexLock +pte_osResult pte_osMutexLock(pte_osMutexHandle handle) +{ + sceKernelWaitSema(handle, 1, NULL); + + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osMutexTimedLock +pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs) +{ + pte_osResult result; + SceUInt timeoutUsecs = timeoutMsecs*1000; + + int status = sceKernelWaitSema(handle, 1, &timeoutUsecs); + if (status < 0) { + // Assume that any error from sceKernelWaitSema was due to a timeout + result = PTE_OS_TIMEOUT; + } else { + result = PTE_OS_OK; + } + + return result; +} +#endif + +#ifdef F_pte_osMutexUnlock +pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle) +{ + sceKernelSignalSema(handle, 1); + return PTE_OS_OK; +} +#endif + +/**************************************************************************** + * + * Semaphores + * + ***************************************************************************/ +#ifdef F_pte_osSemaphoreCreate +pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle) +{ + pte_osSemaphoreHandle handle; + static int semCtr = 0; + char semName[32]; + + if (semCtr++ > MAX_PSP_UID) { + semCtr = 0; + } + + snprintf(semName,sizeof(semName),"pthread_sem%d",semCtr); + handle = sceKernelCreateSema(semName, + 0, /* attributes (default) */ + initialValue, /* initial value */ + SEM_VALUE_MAX, /* maximum value */ + 0); /* options (default) */ + + *pHandle = handle; + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osSemaphoreDelete +pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle) +{ + sceKernelDeleteSema(handle); + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osSemaphorePost +pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count) +{ + sceKernelSignalSema(handle, count); + return PTE_OS_OK; +} +#endif + +#ifdef F_pte_osSemaphorePend +pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeoutMsecs) +{ + unsigned int timeoutUsecs; + unsigned int *pTimeoutUsecs; + SceUInt result; + pte_osResult osResult; + + if (pTimeoutMsecs == NULL) { + pTimeoutUsecs = NULL; + } else { + timeoutUsecs = *pTimeoutMsecs * 1000; + pTimeoutUsecs = &timeoutUsecs; + } + + result = sceKernelWaitSema(handle, 1, pTimeoutUsecs); + if (result == SCE_KERNEL_ERROR_OK) { + osResult = PTE_OS_OK; + } else if (result == SCE_KERNEL_ERROR_WAIT_TIMEOUT) { + osResult = PTE_OS_TIMEOUT; + } else { + osResult = PTE_OS_GENERAL_FAILURE; + } + + return osResult; +} +#endif + + +/* + * Pend on a semaphore- and allow the pend to be cancelled. + * + * PSP OS provides no functionality to asynchronously interrupt a blocked call. We simulte + * this by polling on the main semaphore and the cancellation semaphore and sleeping in a loop. + */ +#ifdef F_pte_osSemaphoreCancellablePend +pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle semHandle, unsigned int *pTimeout) +{ + pspThreadData *pThreadData; + + pThreadData = __getThreadData(sceKernelGetThreadId()); + + clock_t start_time; + pte_osResult result = PTE_OS_OK; + unsigned int timeout; + unsigned char timeoutEnabled; + + start_time = clock(); + + // clock() is in microseconds, timeout as passed in was in milliseconds + if (pTimeout == NULL) { + timeout = 0; + timeoutEnabled = 0; + } else { + timeout = *pTimeout * 1000; + timeoutEnabled = 1; + } + + while (1) { + SceUInt semTimeout; + int status; + + /* Poll semaphore */ + semTimeout = 0; + status = sceKernelWaitSema(semHandle, 1, &semTimeout); + + if (status == SCE_KERNEL_ERROR_OK) { + /* User semaphore posted to */ + result = PTE_OS_OK; + break; + } else if ((timeoutEnabled) && ((clock() - start_time) > timeout)) { + /* Timeout expired */ + result = PTE_OS_TIMEOUT; + break; + } else { + SceKernelSemaInfo semInfo; + + if (pThreadData != NULL) { + SceUID osResult; + + osResult = sceKernelReferSemaStatus(pThreadData->cancelSem, &semInfo); + if (osResult == SCE_KERNEL_ERROR_OK) { + if (semInfo.currentCount > 0) { + result = PTE_OS_INTERRUPTED; + break; + } else { + /* Nothing found and not timed out yet; let's yield so we're not + * in busy loop. + */ + sceKernelDelayThread(POLLING_DELAY_IN_us); + } + } else { + result = PTE_OS_GENERAL_FAILURE; + break; + } + } + } + } + + return result; +} +#endif + + +/**************************************************************************** + * + * Atomic Operations + * + ***************************************************************************/ +#ifdef F_pte_osAtomicExchange +int pte_osAtomicExchange(int *ptarg, int val) +{ + int intc = pspSdkDisableInterrupts(); + int origVal; + + origVal = *ptarg; + *ptarg = val; + + pspSdkEnableInterrupts(intc); + return origVal; +} +#endif + +#ifdef F_pte_osAtomicCompareExchange +int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp) +{ + int intc = pspSdkDisableInterrupts(); + int origVal; + + origVal = *pdest; + if (*pdest == comp){ + *pdest = exchange; + } + + pspSdkEnableInterrupts(intc); + return origVal; +} +#endif + +#ifdef F_pte_osAtomicExchangeAdd +int pte_osAtomicExchangeAdd(int volatile* pAddend, int value) +{ + int origVal; + int intc = pspSdkDisableInterrupts(); + + origVal = *pAddend; + *pAddend += value; + + pspSdkEnableInterrupts(intc); + return origVal; +} +#endif + +#ifdef F_pte_osAtomicDecrement +int pte_osAtomicDecrement(int *pdest) +{ + int val; + int intc = pspSdkDisableInterrupts(); + + (*pdest)--; + val = *pdest; + + pspSdkEnableInterrupts(intc); + return val; +} +#endif + +#ifdef F_pte_osAtomicIncrement +int pte_osAtomicIncrement(int *pdest) +{ + int val; + int intc = pspSdkDisableInterrupts(); + + (*pdest)++; + val = *pdest; + + pspSdkEnableInterrupts(intc); + return val; +} +#endif diff --git a/src/libpthreadglue/tls-helper.c b/src/libpthreadglue/tls-helper.c new file mode 100644 index 00000000..797f6d51 --- /dev/null +++ b/src/libpthreadglue/tls-helper.c @@ -0,0 +1,254 @@ +/* + * PSP Software Development Kit - https://github.com/pspdev + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * tls-helper.c - Pthread compatible system calls. + * + * Copyright (c) 2021 Francisco J Trujillo + * + */ + +#include +#include + +#include +#include + +typedef int pte_osThreadHandle; +typedef int pte_osSemaphoreHandle; +typedef int pte_osMutexHandle; +#include + +#ifdef F___keysUsed +int *__keysUsed; +#else +extern int *__keysUsed; +#endif + +#ifdef F___maxTlsValues +int __maxTlsValues; +#else +extern int __maxTlsValues; +#endif + +#ifdef F___globalTlsLock +pte_osMutexHandle __globalTlsLock; +#else +extern pte_osMutexHandle __globalTlsLock; +#endif + +/* Structure used to emulate TLS on non-POSIX threads. + * This limits us to one non-POSIX thread that can + * call pthread functions. */ +#ifdef F___globalTls +void *__globalTls; +#else +extern void *__globalTls; +#endif + +#ifdef F_pteTlsGlobalInit +pte_osResult pteTlsGlobalInit(int maxEntries) +{ + int i; + pte_osResult result; + + pte_osMutexCreate(&__globalTlsLock); + __keysUsed = (int *)malloc(maxEntries * sizeof(int)); + + if (__keysUsed != NULL) { + for (i = 0; i < maxEntries; i++) { + __keysUsed[i] = 0; + } + + __maxTlsValues = maxEntries; + result = PTE_OS_OK; + } else { + result = PTE_OS_NO_RESOURCES; + } + + return result; +} +#endif + +#ifdef F_pteTlsThreadInit +void *pteTlsThreadInit(void) +{ + void **pTlsStruct; + int i; + + pTlsStruct = (void **)malloc(__maxTlsValues * sizeof(void *)); + + // PTE library assumes that keys are initialized to zero + for (i = 0; i < __maxTlsValues; i++) { + pTlsStruct[i] = 0; + } + + return (void *)pTlsStruct; +} +#endif + +#ifdef F___pteTlsAlloc +pte_osResult __pteTlsAlloc(unsigned int *pKey) +{ + int i; + pte_osResult result = PTE_OS_NO_RESOURCES; + + pte_osMutexLock(__globalTlsLock); + + for (i = 0; i < __maxTlsValues; i++) { + if (__keysUsed[i] == 0) { + __keysUsed[i] = 1; + + *pKey = i + 1; + result = PTE_OS_OK; + break; + } + } + + pte_osMutexUnlock(__globalTlsLock); + return result; +} +#else +pte_osResult __pteTlsAlloc(unsigned int *pKey); +#endif + +#ifdef F_pteTlsGetValue +void *pteTlsGetValue(void *pTlsThreadStruct, unsigned int index) +{ + void **pTls = (void **)pTlsThreadStruct; + + if (__keysUsed[index - 1] && pTls != NULL) { + return pTls[index - 1]; + } else { + return NULL; + } +} +#else +void *pteTlsGetValue(void *pTlsThreadStruct, unsigned int index); +#endif + +#ifdef F___pteTlsSetValue +pte_osResult __pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void *value) +{ + pte_osResult result; + void **pTls = (void **)pTlsThreadStruct; + + if (pTls != NULL) { + pTls[index - 1] = value; + result = PTE_OS_OK; + } else { + result = PTE_OS_INVALID_PARAM; + } + + return result; +} +#else +pte_osResult __pteTlsSetValue(void *pTlsThreadStruct, unsigned int index, void *value); +#endif + +#ifdef F___getTlsStructFromThread +void *__getTlsStructFromThread(SceUID thid) +{ + SceKernelThreadInfo thinfo; + unsigned int ptr; + unsigned int thrNum; + void *pTls; + int numMatches; + + + thinfo.size = sizeof(SceKernelThreadInfo); + sceKernelReferThreadStatus(thid, &thinfo); + numMatches = sscanf(thinfo.name,"pthread%04d__%x", &thrNum, &ptr); + + /* If we were called from a pthread, use the TLS allocated when the thread + * was created. Otherwise, we were called from a non-pthread, so use the + * "global". This is a pretty bad hack, but necessary due to lack of TLS on PSP. + */ + if (numMatches == 2) { + pTls = (void *) ptr; + } else { + pTls = __globalTls; + } + + return pTls; +} +#else +void *__getTlsStructFromThread(SceUID thid); +#endif + +#ifdef F_pteTlsFree +pte_osResult pteTlsFree(unsigned int index) +{ + pte_osResult result; + + if (__keysUsed != NULL) { + pte_osMutexLock(__globalTlsLock); + __keysUsed[index - 1] = 0; + pte_osMutexUnlock(__globalTlsLock); + + result = PTE_OS_OK; + } else { + result = PTE_OS_GENERAL_FAILURE; + } + + return result; +} +#else +pte_osResult pteTlsFree(unsigned int index); +#endif + +#ifdef F_pteTlsThreadDestroy +void pteTlsThreadDestroy(void *pTlsThreadStruct) +{ + free(pTlsThreadStruct); +} +#endif + +#ifdef F_pteTlsGlobalDestroy +void pteTlsGlobalDestroy(void) +{ + pte_osMutexDelete(__globalTlsLock); + free(__keysUsed); +} +#endif + +#ifdef F_pte_osTlsSetValue +pte_osResult pte_osTlsSetValue(unsigned int key, void * value) +{ + void *pTls; + + pTls = __getTlsStructFromThread(sceKernelGetThreadId()); + + return __pteTlsSetValue(pTls, key, value); +} +#endif + +#ifdef F_pte_osTlsGetValue +void * pte_osTlsGetValue(unsigned int index) +{ + void *pTls; + + pTls = __getTlsStructFromThread(sceKernelGetThreadId()); + + return (void *) pteTlsGetValue(pTls, index); + +} +#endif + +#ifdef F_pte_osTlsAlloc +pte_osResult pte_osTlsAlloc(unsigned int *pKey) +{ + __getTlsStructFromThread(sceKernelGetThreadId()); + + return __pteTlsAlloc(pKey); + +} +#endif + +#ifdef F_pte_osTlsFree +pte_osResult pte_osTlsFree(unsigned int index) +{ + return pteTlsFree(index); +} +#endif \ No newline at end of file diff --git a/src/user/pspmoduleinfo.h b/src/user/pspmoduleinfo.h index a7a83ff3..68c80f1b 100644 --- a/src/user/pspmoduleinfo.h +++ b/src/user/pspmoduleinfo.h @@ -107,4 +107,8 @@ enum PspModuleInfoAttr void __libcglue_init(int argc, char *argv[]) {} \ void __libcglue_deinit() {} +/* Disable the use of pthread for reducing binary size */ +#define PSP_DISABLE_PTHREAD() \ + void __libpthreadglue_init() {} + #endif /* PSPMODULEINFO_H */ From 4cbabb73ae17145e8dab828d44953a52fdc74d17 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Thu, 24 Mar 2022 22:56:58 +0100 Subject: [PATCH 2/2] Macro for disabling usage of pthread --- src/libcglue/Makefile.am | 2 +- src/libcglue/init.c | 15 ++++++++++++++- src/user/pspmoduleinfo.h | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/libcglue/Makefile.am b/src/libcglue/Makefile.am index 7d59741a..39ef8e73 100644 --- a/src/libcglue/Makefile.am +++ b/src/libcglue/Makefile.am @@ -31,7 +31,7 @@ GLUE_OBJS = __dummy_passwd.o __fill_stat.o __psp_heap_blockid.o __psp_free_heap. fsync.o getpwnam.o getuid.o geteuid.o -INIT_OBJS = __libcglue_init.o __libcglue_deinit.o _exit.o abort.o exit.o +INIT_OBJS = __libpthreadglue_init.o __libcglue_init.o __libcglue_deinit.o _exit.o abort.o exit.o MUTEXMAN_OBJS = __malloc_mutex.o __sbrk_mutex.o __fdman_mutex.o __init_mutex.o __deinit_mutex.o diff --git a/src/libcglue/init.c b/src/libcglue/init.c index ed3b6654..d78ac6a3 100644 --- a/src/libcglue/init.c +++ b/src/libcglue/init.c @@ -29,6 +29,19 @@ void __deinit_mutex(); extern int sce_newlib_nocreate_thread_in_start __attribute__((weak)); +#ifdef F___libpthreadglue_init +/* Note: This function is being called from __libcglue_init. +* It is a weak function because can be override by user program +*/ +__attribute__((weak)) +void __libpthreadglue_init() +{ + pthread_init(); +} +#else +void __libpthreadglue_init(); +#endif + #ifdef F___libcglue_init /* Note: This function is being called from crt0.c/crt0_prx.c. * It is a weak function because can be override by user program, @@ -47,7 +60,7 @@ void __libcglue_init(int argc, char *argv[]) __fdman_init(); /* Initialize pthread library */ - pthread_init(); + __libpthreadglue_init(); /* Initialize cwd from this program's path */ __init_cwd(argv[0]); diff --git a/src/user/pspmoduleinfo.h b/src/user/pspmoduleinfo.h index 68c80f1b..13427269 100644 --- a/src/user/pspmoduleinfo.h +++ b/src/user/pspmoduleinfo.h @@ -107,8 +107,8 @@ enum PspModuleInfoAttr void __libcglue_init(int argc, char *argv[]) {} \ void __libcglue_deinit() {} -/* Disable the use of pthread for reducing binary size */ -#define PSP_DISABLE_PTHREAD() \ +/* Disable the auto start of pthread on init for reducing binary size if not used. */ +#define PSP_DISABLE_AUTOSTART_PTHREAD() \ void __libpthreadglue_init() {} #endif /* PSPMODULEINFO_H */