From 49bdfa7fdc32d2aac84ab7f128db29b5b205bfe3 Mon Sep 17 00:00:00 2001 From: Carl Wachter Date: Wed, 29 May 2024 12:53:11 +0200 Subject: [PATCH] POSIX compatibility --- platform/hermit/pte_osal.c | 24 +- platform/hermit/pte_osal.h | 4 +- platform/hermit/syscall.h | 8 +- platform/psp/psp_osal.c | 925 ++++++++++++++++++++++++++++++++++++- 4 files changed, 941 insertions(+), 20 deletions(-) diff --git a/platform/hermit/pte_osal.c b/platform/hermit/pte_osal.c index f78c317..5fec026 100644 --- a/platform/hermit/pte_osal.c +++ b/platform/hermit/pte_osal.c @@ -396,7 +396,7 @@ int pte_osThreadGetDefaultPriority(void) pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle) { - if (sys_sem_init(pHandle, 1)) + if (sys_sem_init(pHandle, 0, 1)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -404,7 +404,7 @@ pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle) pte_osResult pte_osMutexDelete(pte_osMutexHandle handle) { - if (sys_sem_destroy(handle)) + if (sys_sem_destroy(&handle)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -412,7 +412,7 @@ pte_osResult pte_osMutexDelete(pte_osMutexHandle handle) pte_osResult pte_osMutexLock(pte_osMutexHandle handle) { - if (sys_sem_wait(handle)) + if (sys_sem_wait(&handle)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -420,7 +420,7 @@ pte_osResult pte_osMutexLock(pte_osMutexHandle handle) pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs) { - if (sys_sem_timedwait(handle, timeoutMsecs)) + if (sys_sem_timedwait(&handle, timeoutMsecs)) return PTE_OS_TIMEOUT; return PTE_OS_OK; @@ -428,7 +428,7 @@ pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeout pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle) { - if (sys_sem_post(handle)) + if (sys_sem_post(&handle)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -442,7 +442,8 @@ pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle) pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle) { - if (sys_sem_init(pHandle, initialValue)) + + if (sys_sem_init(pHandle, 0, initialValue)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -450,7 +451,7 @@ pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHan pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle) { - if (sys_sem_destroy(handle)) + if (sys_sem_destroy(&handle)) return PTE_OS_NO_RESOURCES; return PTE_OS_OK; @@ -461,22 +462,21 @@ pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count) int i; for (i=0; i classical system calls are realized as normal function @@ -114,11 +112,11 @@ int sys_recmutex_init(HermitRecursiveMutex** recmutex); int sys_recmutex_destroy(HermitRecursiveMutex* recmutex); int sys_recmutex_lock(HermitRecursiveMutex* recmutex); int sys_recmutex_unlock(HermitRecursiveMutex* recmutex); -int sys_sem_init(HermitSemaphore** sem, unsigned int value); +int sys_sem_init(HermitSemaphore* sem, unsigned int pshared, unsigned int value); int sys_sem_destroy(HermitSemaphore* sem); int sys_sem_post(HermitSemaphore* sem); int sys_sem_trywait(HermitSemaphore* sem); -int sys_sem_timedwait(HermitSemaphore *sem, unsigned int ms); +int sys_sem_timedwait(HermitSemaphore* sem, unsigned int ms); #define sys_sem_wait(sem) sys_sem_timedwait(sem, 0) int sys_sem_timedwait(HermitSemaphore* sem, unsigned int ms); int sys_spinlock_init(HermitSpinlock** lock); diff --git a/platform/psp/psp_osal.c b/platform/psp/psp_osal.c index a3887a6..a7930ab 100644 --- a/platform/psp/psp_osal.c +++ b/platform/psp/psp_osal.c @@ -1 +1,924 @@ -/* * psp_osal.c * * Description: * * -------------------------------------------------------------------------- * * Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems * Copyright(C) 2008 Jason Schmidlapp * * Contact Email: jschmidlapp@users.sourceforge.net * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library in the file COPYING.LIB; * if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include "pte_osal.h" #include "pthread.h" #include "tls-helper.h" /* For ftime */ #include #include #include #define MAX_PSP_UID 2048 // SWAG #define DEFAULT_STACK_SIZE_BYTES 4096 #define PSP_MAX_TLS 32 #if 1 #define PSP_DEBUG(x) printf(x) #else #define PSP_DEBUG(x) #endif /* TLS key used to access pspThreadData struct for reach thread. */ static unsigned int threadDataKey; /* * 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; /* Structure used to emulate TLS on non-POSIX threads. * This limits us to one non-POSIX thread that can * call pthread functions. */ static void *globalTls; /* Helper functions */ static pspThreadData *getThreadData(SceUID threadHandle); static void *getTlsStructFromThread(SceUID thid); /* 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. */ int pspStubThreadEntry (unsigned int argc, void *argv) { int result; pspThreadData *pThreadData; pThreadData = getThreadData(sceKernelGetThreadId()); result = (*(pThreadData->entryPoint))(pThreadData->argv); return result; } /**************************************************************************** * * Initialization * ***************************************************************************/ 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; } /**************************************************************************** * * Threads * ***************************************************************************/ 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; } pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle) { sceKernelStartThread(osThreadHandle, 0, NULL); return PTE_OS_OK; } 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; } pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle) { pte_osThreadDelete(handle); sceKernelExitDeleteThread(0); return PTE_OS_OK; } void pte_osThreadExit() { sceKernelExitThread(0); } /* * 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. */ 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; } pte_osThreadHandle pte_osThreadGetHandle(void) { return sceKernelGetThreadId(); } int pte_osThreadGetPriority(pte_osThreadHandle threadHandle) { SceKernelThreadInfo thinfo; thinfo.size = sizeof(SceKernelThreadInfo); sceKernelReferThreadStatus(threadHandle, &thinfo); return thinfo.currentPriority; } pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority) { sceKernelChangeThreadPriority(threadHandle, newPriority); return PTE_OS_OK; } 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; } 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; } void pte_osThreadSleep(unsigned int msecs) { sceKernelDelayThread(msecs*1000); } int pte_osThreadGetMinPriority() { return 17; } int pte_osThreadGetMaxPriority() { return 30; } int pte_osThreadGetDefaultPriority() { return 18; } /**************************************************************************** * * Mutexes * ****************************************************************************/ 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; } pte_osResult pte_osMutexDelete(pte_osMutexHandle handle) { sceKernelDeleteSema(handle); return PTE_OS_OK; } pte_osResult pte_osMutexLock(pte_osMutexHandle handle) { sceKernelWaitSema(handle, 1, NULL); return PTE_OS_OK; } 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; } pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle) { sceKernelSignalSema(handle, 1); return PTE_OS_OK; } /**************************************************************************** * * Semaphores * ***************************************************************************/ 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; } pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle) { sceKernelDeleteSema(handle); return PTE_OS_OK; } pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count) { sceKernelSignalSema(handle, count); return PTE_OS_OK; } 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; } /* * 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. */ 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; } /**************************************************************************** * * Atomic Operations * ***************************************************************************/ int pte_osAtomicExchange(int *ptarg, int val) { int intc = pspSdkDisableInterrupts(); int origVal; origVal = *ptarg; *ptarg = val; pspSdkEnableInterrupts(intc); return origVal; } 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; } int pte_osAtomicExchangeAdd(int volatile* pAddend, int value) { int origVal; int intc = pspSdkDisableInterrupts(); origVal = *pAddend; *pAddend += value; pspSdkEnableInterrupts(intc); return origVal; } int pte_osAtomicDecrement(int *pdest) { int val; int intc = pspSdkDisableInterrupts(); (*pdest)--; val = *pdest; pspSdkEnableInterrupts(intc); return val; } int pte_osAtomicIncrement(int *pdest) { int val; int intc = pspSdkDisableInterrupts(); (*pdest)++; val = *pdest; pspSdkEnableInterrupts(intc); return val; } /**************************************************************************** * * Helper functions * ***************************************************************************/ static pspThreadData *getThreadData(SceUID threadHandle) { pspThreadData *pThreadData; void *pTls; pTls = getTlsStructFromThread(threadHandle); pThreadData = (pspThreadData *) pteTlsGetValue(pTls, threadDataKey); return pThreadData; } static 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; } /**************************************************************************** * * Thread Local Storage * ***************************************************************************/ pte_osResult pte_osTlsSetValue(unsigned int key, void * value) { void *pTls; pTls = getTlsStructFromThread(sceKernelGetThreadId()); return pteTlsSetValue(pTls, key, value); } void * pte_osTlsGetValue(unsigned int index) { void *pTls; pTls = getTlsStructFromThread(sceKernelGetThreadId()); return (void *) pteTlsGetValue(pTls, index); } pte_osResult pte_osTlsAlloc(unsigned int *pKey) { void * pTls; pTls = getTlsStructFromThread(sceKernelGetThreadId()); return pteTlsAlloc(pKey); } pte_osResult pte_osTlsFree(unsigned int index) { return pteTlsFree(index); } /**************************************************************************** * * Miscellaneous * ***************************************************************************/ int ftime(struct timeb *tb) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); tb->time = tv.tv_sec; tb->millitm = tv.tv_usec / 1000; tb->timezone = tz.tz_minuteswest; tb->dstflag = tz.tz_dsttime; return 0; } \ No newline at end of file +/* + * psp_osal.c + * + * Description: + * + * -------------------------------------------------------------------------- + * + * Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems + * Copyright(C) 2008 Jason Schmidlapp + * + * Contact Email: jschmidlapp@users.sourceforge.net + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#include +#include +#include +#include +#include "pte_osal.h" +#include "pthread.h" +#include "tls-helper.h" + +/* For ftime */ +#include +#include +#include + +#define MAX_PSP_UID 2048 // SWAG + +#define DEFAULT_STACK_SIZE_BYTES 4096 + +#define PSP_MAX_TLS 32 + +#if 1 +#define PSP_DEBUG(x) printf(x) +#else +#define PSP_DEBUG(x) +#endif + +/* TLS key used to access pspThreadData struct for reach thread. */ +static unsigned int threadDataKey; + +/* + * 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; + + +/* Structure used to emulate TLS on non-POSIX threads. + * This limits us to one non-POSIX thread that can + * call pthread functions. */ +static void *globalTls; + +/* Helper functions */ +static pspThreadData *getThreadData(SceUID threadHandle); +static void *getTlsStructFromThread(SceUID thid); + +/* 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. + */ +int pspStubThreadEntry (unsigned int argc, void *argv) +{ + int result; + pspThreadData *pThreadData; + + pThreadData = getThreadData(sceKernelGetThreadId()); + + result = (*(pThreadData->entryPoint))(pThreadData->argv); + + return result; +} + +/**************************************************************************** + * + * Initialization + * + ***************************************************************************/ + +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; +} + +/**************************************************************************** + * + * Threads + * + ***************************************************************************/ + +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; +} + + +pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle) +{ + sceKernelStartThread(osThreadHandle, 0, NULL); + + return PTE_OS_OK; +} + + +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; +} + +pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle) +{ + pte_osThreadDelete(handle); + + sceKernelExitDeleteThread(0); + + return PTE_OS_OK; +} + +void pte_osThreadExit() +{ + sceKernelExitThread(0); +} + +/* + * 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. + */ +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; +} + +pte_osThreadHandle pte_osThreadGetHandle(void) +{ + return sceKernelGetThreadId(); +} + +int pte_osThreadGetPriority(pte_osThreadHandle threadHandle) +{ + SceKernelThreadInfo thinfo; + + thinfo.size = sizeof(SceKernelThreadInfo); + + sceKernelReferThreadStatus(threadHandle, &thinfo); + + return thinfo.currentPriority; +} + +pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority) +{ + sceKernelChangeThreadPriority(threadHandle, newPriority); + return PTE_OS_OK; +} + +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; +} + + +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; +} + +void pte_osThreadSleep(unsigned int msecs) +{ + sceKernelDelayThread(msecs*1000); +} + +int pte_osThreadGetMinPriority() +{ + return 17; +} + +int pte_osThreadGetMaxPriority() +{ + return 30; +} + +int pte_osThreadGetDefaultPriority() +{ + return 18; +} + +/**************************************************************************** + * + * Mutexes + * + ****************************************************************************/ + +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; +} + +pte_osResult pte_osMutexDelete(pte_osMutexHandle handle) +{ + sceKernelDeleteSema(handle); + + return PTE_OS_OK; +} + + + +pte_osResult pte_osMutexLock(pte_osMutexHandle handle) +{ + sceKernelWaitSema(handle, 1, NULL); + + return PTE_OS_OK; +} + +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; +} + + +pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle) +{ + sceKernelSignalSema(handle, 1); + + return PTE_OS_OK; +} + +/**************************************************************************** + * + * Semaphores + * + ***************************************************************************/ + +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; +} + +pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle) +{ + sceKernelDeleteSema(handle); + + return PTE_OS_OK; +} + + +pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count) +{ + sceKernelSignalSema(handle, count); + + return PTE_OS_OK; +} + +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; +} + + +/* + * 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. + */ +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; +} + + +/**************************************************************************** + * + * Atomic Operations + * + ***************************************************************************/ + +int pte_osAtomicExchange(int *ptarg, int val) +{ + int intc = pspSdkDisableInterrupts(); + int origVal; + + origVal = *ptarg; + + *ptarg = val; + + pspSdkEnableInterrupts(intc); + + return origVal; + +} + +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; +} + + +int pte_osAtomicExchangeAdd(int volatile* pAddend, int value) +{ + int origVal; + int intc = pspSdkDisableInterrupts(); + + origVal = *pAddend; + + *pAddend += value; + + pspSdkEnableInterrupts(intc); + + return origVal; +} + +int pte_osAtomicDecrement(int *pdest) +{ + int val; + + int intc = pspSdkDisableInterrupts(); + + (*pdest)--; + val = *pdest; + + pspSdkEnableInterrupts(intc); + + return val; +} + +int pte_osAtomicIncrement(int *pdest) +{ + int val; + + int intc = pspSdkDisableInterrupts(); + + (*pdest)++; + val = *pdest; + + pspSdkEnableInterrupts(intc); + + return val; +} + +/**************************************************************************** + * + * Helper functions + * + ***************************************************************************/ + +static pspThreadData *getThreadData(SceUID threadHandle) +{ + pspThreadData *pThreadData; + void *pTls; + + pTls = getTlsStructFromThread(threadHandle); + + pThreadData = (pspThreadData *) pteTlsGetValue(pTls, threadDataKey); + + return pThreadData; +} + +static 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; +} + +/**************************************************************************** + * + * Thread Local Storage + * + ***************************************************************************/ + +pte_osResult pte_osTlsSetValue(unsigned int key, void * value) +{ + void *pTls; + + pTls = getTlsStructFromThread(sceKernelGetThreadId()); + + return pteTlsSetValue(pTls, key, value); +} + +void * pte_osTlsGetValue(unsigned int index) +{ + + void *pTls; + + pTls = getTlsStructFromThread(sceKernelGetThreadId()); + + return (void *) pteTlsGetValue(pTls, index); + +} + + +pte_osResult pte_osTlsAlloc(unsigned int *pKey) +{ + void * pTls; + + pTls = getTlsStructFromThread(sceKernelGetThreadId()); + + return pteTlsAlloc(pKey); + +} + +pte_osResult pte_osTlsFree(unsigned int index) +{ + return pteTlsFree(index); +} + +/**************************************************************************** + * + * Miscellaneous + * + ***************************************************************************/ + +int ftime(struct timeb *tb) +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + tb->time = tv.tv_sec; + tb->millitm = tv.tv_usec / 1000; + tb->timezone = tz.tz_minuteswest; + tb->dstflag = tz.tz_dsttime; + + return 0; +} \ No newline at end of file