diff --git a/TODO b/TODO deleted file mode 100644 index 431153197594d50690812811561a9cc08be857ac..0000000000000000000000000000000000000000 --- a/TODO +++ /dev/null @@ -1,5 +0,0 @@ -1. Clean up compiler #defines -8. Check NP routines. -9. Check for printfs -10. Remove -Werror from Makefiles - diff --git a/doc/pte_dspbios.html b/doc/pte_dspbios.html new file mode 100644 index 0000000000000000000000000000000000000000..488b963bc5196f0f65c74e8327a26e1e2a2de9eb --- /dev/null +++ b/doc/pte_dspbios.html @@ -0,0 +1,108 @@ + +PTE on DSP/BIOS
PTE on DSP/BIOS + + + + +


+

+

The DSP/BIOS OSAL is relatively +straightforward and maps relatively cleanly to the DSP/BIOS API. For +instance, most mutex and semaphore operations are supported directly +using the LCK and SEM API's. Specific details and exceptions are +described below.

+

Threads

+

Thread creation

+

OsThreadCreate allocates a dspbiosThreadData structure that +contains semaphores used for joining to a thread, cancelling a thread +and the initial priority requested by the user. A pointer to this +structure is stored as a TLS value. +

+

OsThreadCreate must create threads in a suspended state. In +DSP/BIOS this is done by setting the initial priority to zero. The +initial priority is stored in the per thread control data. When +OsThreadStart is called, it retrieves the initial priority from the +control data attached to the thread and then sets the priority of the +thread, which starts execution.

+

Thread termination

+

In order for pthread_join to wait for a thread, OsThreadWaitForEnd +is called. Since DSP/BIOS doesn't have a explicit system call for +this, we emulate it using a semaphore that is posted to when the +thread exits. This semaphore is saved in the per thread control +structure.

+

Thread cleanup

+


+

+

The PTE library calls OsThreadDelete +to signal to the OSAL that the resources for a task can be freed. +For DSP/BIOS, this means a call to TSK_delete (task resources are not +automatically freed by DSP/BIOS when the thread exits).

+


+

+

OsThreadDelete +will be called at a number of points. For attached threads, it will +be called when pthread_join +is called and returns or when pthread_detach +is called for a terminated thread. For detached threads +OsThreadDelete +will be called when the thread exits.

+


+

+

The +problem is that TSK_delete +naturally can not be called from the same context as the thread that +we're trying to free. For the first two cases (pthread_join +and pthread_detach) +this is not a problem as these will always be called from another +thread. However, for detached threads OsThreadDelete +will be called from the same context.

+


+

+

To +work around it, the DSP/BIOS OSAL includes a low priority that will +clean up detached threads. If OsThreadDelete +detects that it is being called from the same context, it will post a +message to a mailbox that the garbage collector thread is waiting on. + The garbage collector thread will then call TSK_delete +for that thread, as well as to clean up any resources that were +allocated when the thread was created.

+


+

+

Thread Local Storage

+


+

+

TLS is implemented using the +“environment” of DSP/BIOS threads. This allows a single value to +be associated with a thread. The TLS helper routines are used to +provide full TLS functionality; these routines allocate a structure +that holds multiple TLS keys and values. The DSP/BIOS environment is +used to hold a pointer to this structure.

+


+

+

Thread Cancellation

+


+

+

Since DSP/BIOS does not natively +provide a way to break out of blocked operations, this functionality +is emulated using a cancellation semaphore that is stored in the per +thread control data. When the user requests that a thread be +canceled (i.e. OsThreadCancel +is called) this semaphore is posted to. In +OsSemaphoreCancellablePend, +the cancellation semaphore as well as the user semaphore are polled +(rather than blocking) and the routine returns if the cancellation +semaphore is posted to.

+ \ No newline at end of file diff --git a/doc/pte_main.html b/doc/pte_main.html new file mode 100644 index 0000000000000000000000000000000000000000..8841bed84933240031bfd681708f8b18dc15a061 --- /dev/null +++ b/doc/pte_main.html @@ -0,0 +1,12 @@ + + +POSIX Threads for embedded systems (PTE) + + +
POSIX Threads for embedded systems (PTE)

PTE is an open source implementation of the POSIX API +for multithreaded applications (pthreads).  It is intended to be +used to provide a pthreads API for embedded operating systems that +do not natively provide a pthreads API. PTE is designed to be easily +portable to such operating systems and only relies on basic primitives +(e.g. semaphores) that are widely supported on most embedded operating +systems.

Currently, PTE has been ported to Texas Instrument's DSP/BIOS and Sony's PSP OS.

Download
Contact

Documentation
User's Manual
Porting Guide
Notes on DSP/BIOS port
Notes on PSP OS port




\ No newline at end of file diff --git a/doc/pte_porting_guide.html b/doc/pte_porting_guide.html new file mode 100644 index 0000000000000000000000000000000000000000..d9f22be65309435a185a98a1dcfff83c8e2bc293 --- /dev/null +++ b/doc/pte_porting_guide.html @@ -0,0 +1,267 @@ + +PTE Porting Guide + + + + + + + + + + + +


+

+

Pthreads-Embedded (PTE) Porting +Guide

+

The PTE library consists of both a platform independent component, +which contains the bulk of the pthreads functionality, and a platform +specific component which connects the platform independent component +to the underlying OS. Naturally, the platform specific layer is the +only layer that needs to be modified when porting PTE.

+

The OS adaptation layer (OSAL) must provide the following +functionality:

+
  1. Threads

    +
  2. Semaphores

    +
  3. Mutexes

    +
  4. Atomic operations

    +
  5. Thread local storage

    +
+

The underlying OS does not necessarily have to provide all of this +functionality – it is possible for the OSAL to emulate behavior. +For instance, mutexes can be emulated using semaphores provided by +the OS. The sections below present a high level of the required +functionality as well as how it fits into a “typical” embedded +OS. Specifics such as function parameters, etc are covered in the +OSAL API reference.

+

Threads

+

Thread Initialization

+

OsThreadCreate, OsThreadStart

+

Thread initialization is separated into two steps: create and +start. When OSThreadCreate +is called, the OS should create a new thread, but it must be started +in a suspended state. The thread should not start executing until +OSThreadStart is called. +This separation in functionality is due necessary to avoid race +condFor instance, if the target OS supports TLS but only allows a +single TLS value, this single value could contain a pointer to a +structure that contains multiple TLS values. The PTE distribution +includes a helper file to implement this functionality +(/platform/helper/tls-helper.c). See the DSP/BIOS port for an +example of using tls-helper.c.itions in the PTE library.

+

Since the actual prototype of an +thread entry point varies between OS and thus will more than likely +not match that used by +OsThreadCreate, +it will usually be necessary to create a stub function that matches +your OS's entry point prototype. This stub function simply calls the +entry point specified by OsThreadCreate +(see DSP/BIOS and PSP-OS ports).

+

Typically, OsThreadCreate will also perform other initialization; +for instance to initialize TLS structures, allocate other control +resources. This of course varies depending on the target OS. +

+

Some OS's require additional parameters to start a thread. For +instance, DSP/BIOS requires the priority in order to start the +thread. Rather than pass these parameters to both OsThreadCreate +and OsThreadStart, the +OSAL should store any necessary information as thread specific values +during OsThreadCreate and +then retrieve them as necessary in OsThreadStart.

+



+

+

Thread Destruction

+

OsThreadExit, OsThreadDelete, +OsThreadExitAndDelete, OsThreadWaitForEnd

+

Thread destruction is broken into three API calls to support the +different use cases of the pthreads API. OsThreadExit +should cause the currently executing thread to stop execution; +resources should not yet be freed. This is called when a thread +exits but the thread is not detached – resource deallocation must +wait until the user calls pthread_join +(or pthread_detach) at +which point OsThreadDelete +will be called.

+

Alternatively, if a detached thread exits, thread resource +deallocation and thread termination can occur simultaneously. In +this case, OsThreadExitAndDelete +will be called.

+

OsThreadWaitForEnd +should block until the specified thread exists. For OS's that do not +directly support this functionality, a semaphore can be used to +emulate this behavior (see DSP/BIOS port). Note that this call +should be cancellable – that is, it should return (even if the +target thread has not exited) if OsThreadCancel +is called.

+

Thread Priority

+

OsThreadSetPriority, OsThreadGetPriority, +OsThreadGetMaxPriority, OsThreadGetMinPriority

+

The OSAL provides the upper and lower bounds of it's priority +range when OsThreadGetMaxPriority +and OsThreadGetMinPriority +are called. The PTE library will ensure that all priorities passed +to the OSAL (e.g. through OsThreadCreate) +are within these bounds.

+

Thread Cancellation

+

OsThreadCancel, OsThreadCheckCancel

+

Currently, the PTE library only supports deferred cancellation +(see PTE notes). While the PTE library handles most of the +complexities of cancellation, there are three hooks required from the +OSAL. When OsThreadCancel +is called, it must cause OsSemaphorePendCancellable +and OsThreadWaitForEnd to +return (this function is used by the PTE library to implement pthread +cancellation points). Since most embedded OS's do not support this +kind of functionality, it can be implemented using semaphores (see +DSP/BIOS and PSP-OS ports). OsThreadCheckCancel +simply returns whether OsThreadCancel +has been called for this thread.

+

Miscellaneous Thread Functionality

+ +

OsThreadGetHandle, OsThreadSleep, +OsThreadGetMaxPriority, OsThreadGetMinPriority, +OsThreadGetDefaultPriority

+

+

+

Semaphores

+



+

+

OsSemaphoreCreate, OsSemaphoreDelete, +OsSemaphorePend, OsSemaphorePort

+

This basic semaphore functionality should map directly to almost +all embedded OS's. +

+

OsSemaphoreCancellablePend

+

In order to implement deferred cancellation, a “cancellable” +pend (OsSemaphorePendCancellable) +must also be supported. As discussed above, +OsSemaphorePendCancellable +must return when OsThreadCancel +has been called on the thread that is currently pending, regardless +of whether the semaphore has been posted to or not. The way that this +is implemented in other ports (e.g. DSP/BIOS and PSP-OS) is to use an +additional semaphore, and then poll on both semaphores, as shown in +the pseudo-code below:

+ +

loop forever:

+

poll main semaphore

+

if semaphore was posted to, +return OK

+

else

+

check timeout

+

if timeout has expired, return +'timed out'

+

else

+

poll cancellation semaphore +

+

if cancellation semaphore has +been posted to, return 'canceled'

+

else

+

sleep for small amount of time

+

For instance, if the target OS supports TLS but only allows a +single TLS value, this single value could contain a pointer to a +structure that contains multiple TLS values. The PTE distribution +includes a helper file to implement this functionality +(/platform/helper/tls-helper.c). See the DSP/BIOS port for an +example of using tls-helper.c.

+

Mutexes

+

Mutexes are only included as an optimization as some OS's mutex +operation is much faster than semaphore operations. If the target OS +does not support mutexes, they can easily be implemented using +semaphores.

+



+

+

Atomic operations

+

OsAtomicExchange, OsAtomicCompareExchange, +OsAtomicExchangeIncrement, OsAtomicDecrement, OsAtomicIncrement

+

The PTE library requires five atomic operations to be +supplied by the OSAL. Macros are used in case the target platform +supports direct assembly instructions to perform some or all of these +operations. However, under most OS's these macros will simply refer +to functions that disable interrupts and then perform the required +operations.

+



+

+

Thread local storage

+

OsTlsInit, OsTlsAlloc, OsTlsFree, +OsTlsSetValue, OsTlsGetValue

+

The OSAL must be able to allocate and free TLS keys, and retrieve +thread specific data. If the target OS does not support this level +of TLS functionality, but does have limited TLS support, it is +possible to emulate the behavior required by the PTE library.

+

For instance, if the target OS supports TLS but only allows a +single TLS value, this single value could contain a pointer to a +structure that contains multiple TLS values. The PTE distribution +includes a helper file to implement this functionality +(/platform/helper/tls-helper.c). See the DSP/BIOS port for an +example of using tls-helper.c.

+

If the OS contains no TLS support, it might still be possible to +emulate TLS functionality. See the PSP-OS port for an example of how +this can be accomplished. Be warned – it is not a pretty solution, +but it works.

+

It is important to note that TLS functionality is required by +the PTE library – it is used for more than just the pthread TLS +functions, but is used extensively throughout the library.

+

One potentially tricky issue with TLS +is how to handle the case of when pthread TLS functions are called +from non-pthread threads (i.e. pure native threads that were not +created through pthread_create). Technically, according to the +pthread spec, this should work. However, it is problematic in that +OsTlsInit would not have been called for that thread, since it is +called in response to pthread_create(). Different ports handle this +differently – see the notes for a particular ports.

+

Miscellaneous Functionality

+

ftime

+

Since pthreads uses absolute time for timeouts, the PTE library +requires the OS to supply the current time. Note that this does not +have to be the actual world time, but can be an internal timebase +(for example, since the unit started up). However, the time source +should be the same one that the caller to pthread would use.

+

Types and Constants

+

The OSAL layer must declare a number of types and +constants. +

+

The following types must be defined to map to the +appropriate OS constructs:

+

OsThreadHandle

+

OsSemaphoreHandle

+ +

OsMutexHandle


+

+

The following constants must be defined:

+

OS_DEFAULT_PRIO +– default priority for a created thread.

+

OS_MIN_PRIO +– minimum thread priority.

+

OS_MAX_PRIO +– maximum thread priority.

+

OS_MAX_SIMUL_THREADS +– maximum number of threads that may be active simultaneously.

+



+

+

Each port must also include a file, pte_types.h, that +defines all of the following types. This may be done by explicitly +typedef'ing the structure (for OS's that do not natively support the +type) or simply by including the appropriate header file:

+

pid_t

+

struct timespec

+

mode_t

+

struct timeb

+

File structure

+

The OSAL layer must include a file named pte_osal.h. +This file must include pte_generic_osal.h as well as the platform +specific header file (e.g. dspbios_osal.h).

+ \ No newline at end of file diff --git a/doc/pte_psp.html b/doc/pte_psp.html new file mode 100644 index 0000000000000000000000000000000000000000..50fbdc77e5730e7ea7ed346cc920e74e6168d73c --- /dev/null +++ b/doc/pte_psp.html @@ -0,0 +1,88 @@ + +PTE on PSP OS + + + + + + + +

PTE on PSP OS


+

+

All PSP OS objects that are created +require a name to be associated with them. The name must be unique +among objects that currently exist. To accomplish this, any routines +that allocate OS objects (e.g. OsMutexCreate) +keep a static local variable that acts as a counter and is +incremented every time an object is created. This value is appended +to the name of the resource. For instance “mutex04” would be the +name of the fourth mutex created.

+


+

+

Thread creation

+

OsThreadCreate +allocates a pspThreadData +structure that contains a semaphore used for cancelling a thread, the +thread's entry point and parameters to the thread's entry point. A +pointer to this structure is stored as a TLS value.

+

OsThreadCreate +calls sceKernelCreateThread +with a entry point of pspStubThreadEntry. + The stub entry point retrieves the per thread control structure +(allocated during OsThreadCreate) +and gets the thread's real entry point and parameters and then calls +the real entry point.

+


+

+

Thread Local Storage

+

Unfortunately, PSP OS does not include +any kind of thread local storage functionality. To emulate +TLS, when a new POSIX thread is created, a structure is allocated to +contain TLS keys and values. A pointer to this structure is appended +to the thread name. For instance, a new threads name might be +“pthread10_80001234”, where 0x80001234 is the address of the TLS +structure. When a thread wants to access TLS information, it +retrieves the thread's name from the OS, parses the name to extract +the pointer and then utilizes the TLS helper library to set or get +TLS values. +

+


+

+

Unfortunately, this mechanism only +works for threads that are created through pthread_create; it does +not work for OS threads that are using pthread calls. To emulate +this (at this for one thread) we allocate a single TLS structure +(like the ones associated with each POSIX thread). This “global” +TLS structure is used when pthread is called from a non-POSIX OS +thread. This introduces the important limitation that pthread calls +can be made from ONLY ONE non-POSIX OS thread. Behavior when calling +from multiple different non-POSIX OS threads is undefined.

+


+

+

Mutexes

+

PSP OS does not supply routines for +mutexes. A counting semaphore initialized to 0 is used.

+


+

+

Thread Cancellation

+

Since PSP OS does not natively provide +a way to break out of blocked operations, this functionality is +emulated using a cancellation semaphore that is stored in the per +thread control data. When the user requests that a thread be +cancelled (i.e. OsThreadCancel +is called) this semaphore is posted to. In +OsSemaphoreCancellablePend, +the cancellation semaphore as well as the user semaphore are polled +(rather than blocking) and the routine returns if the cancellation +semaphore is posted to. A similar technique is used for +OsThreadWaitForEnd.

+ \ No newline at end of file diff --git a/platform/dspbios/pte-config.h b/platform/dspbios/pte-config.h deleted file mode 100644 index 1591dfe7b25b863d6192e4d26dea56520dd22aa0..0000000000000000000000000000000000000000 --- a/platform/dspbios/pte-config.h +++ /dev/null @@ -1,36 +0,0 @@ -/* config.h */ - -#ifndef PTE_CONFIG_H -#define PTE_CONFIG_H - -#include - -/********************************************************************* - * Defaults: see target specific redefinitions below. - *********************************************************************/ - -/* We're building the pthreads-win32 library */ -#define PTE_BUILD - -#undef PTE_SUPPORT_ASYNC_CANCEL - -typedef int pid_t; - -struct timespec -{ - time_t tv_sec; - long tv_nsec; -}; - -typedef unsigned int mode_t; - - -struct timeb -{ - time_t time; - unsigned short millitm; - short timezone; - short dstflag; -}; - -#endif diff --git a/platform/dspbios/pte_test/Configuration1.cdb b/platform/dspbios/pte_test/Configuration1.cdb index 198d47ddfe9765e4a9e62cddc58a3bd6f83577e0..f9ab9e8936f2757164ad836908aedaf98440fa23 100644 --- a/platform/dspbios/pte_test/Configuration1.cdb +++ b/platform/dspbios/pte_test/Configuration1.cdb @@ -75,7 +75,7 @@ type GlobalStatus { prop Visible :: 0 prop Writable :: 1 } - global DATE :: "Fri Apr 04 06:42:26 2008" { + global DATE :: "Fri Apr 04 15:41:02 2008" { prop Type :: "{21455EA3-B96A-11cf-9BFE-0000C0AC14C7}" prop Visible :: 0 prop Writable :: 0 diff --git a/platform/dspbios/pte_test/pte_test.pjt b/platform/dspbios/pte_test/pte_test.pjt index 1aabab745da29adca413e231fb9ad153e3d4bb2b..fff0f3357af701a7f42271a0991c5cee989b2af5 100644 --- a/platform/dspbios/pte_test/pte_test.pjt +++ b/platform/dspbios/pte_test/pte_test.pjt @@ -70,6 +70,7 @@ Source="..\..\..\tests\join0.c" Source="..\..\..\tests\join1.c" Source="..\..\..\tests\join2.c" Source="..\..\..\tests\join3.c" +Source="..\..\..\tests\join4.c" Source="..\..\..\tests\kill1.c" Source="..\..\..\tests\mutex1.c" Source="..\..\..\tests\mutex1e.c" @@ -127,6 +128,8 @@ Source="..\..\..\tests\semaphore2.c" Source="..\..\..\tests\semaphore3.c" Source="..\..\..\tests\semaphore4.c" Source="..\..\..\tests\semaphore4t.c" +Source="..\..\..\tests\semaphore5.c" +Source="..\..\..\tests\semaphore6.c" Source="..\..\..\tests\spin1.c" Source="..\..\..\tests\spin2.c" Source="..\..\..\tests\spin3.c" diff --git a/platform/dspbios/pte_types.h b/platform/dspbios/pte_types.h new file mode 100644 index 0000000000000000000000000000000000000000..6f1260982cf030cebe90d2386e549ca734bf26c9 --- /dev/null +++ b/platform/dspbios/pte_types.h @@ -0,0 +1,27 @@ +/* pte_types.h */ + +#ifndef PTE_TYPES_H +#define PTE_TYPES_H + +#include + +typedef int pid_t; + +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +typedef unsigned int mode_t; + + +struct timeb +{ + time_t time; + unsigned short millitm; + short timezone; + short dstflag; +}; + +#endif /* PTE_TYPES_H */ diff --git a/platform/psp/Makefile b/platform/psp/Makefile index e33ab9318b5808b8632f5dd6a65ba614479bd876..f9491e1f6dd1fdd349a72ac7c91391b1d9f5ce4a 100644 --- a/platform/psp/Makefile +++ b/platform/psp/Makefile @@ -162,7 +162,7 @@ OS_OBJS = \ OBJS = $(MUTEX_OBJS) $(MUTEXATTR_OBJS) $(THREAD_OBJS) $(SUPPORT_OBJS) $(TLS_OBJS) $(MISC_OBJS) $(SEM_OBJS) $(BARRIER_OBJS) $(SPIN_OBJS) $(CONDVAR_OBJS) $(RWLOCK_OBJS) $(CANCEL_OBJS) $(OS_OBJS) INCDIR = -CFLAGS = $(GLOBAL_CFLAGS) -G0 -O2 -Wall -g -fno-strict-aliasing -Werror -I../.. -I../helper +CFLAGS = $(GLOBAL_CFLAGS) -G0 -O2 -Wall -g -fno-strict-aliasing -I../.. -I../helper CXXFLAGS = $(CFLAGS) -fexceptions -fno-rtti -Werror -D__CLEANUP_CXX ASFLAGS = $(CFLAGS) diff --git a/platform/psp/psp_osal.c b/platform/psp/psp_osal.c index 8b176532c62cd04e4cff45e6edde157d7b08f8e8..a3887a6093446f67921ea3bd1dbae8a310723ac6 100644 --- a/platform/psp/psp_osal.c +++ b/platform/psp/psp_osal.c @@ -1 +1 @@ -/* * 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 diff --git a/platform/psp/pte-config.h b/platform/psp/pte-config.h deleted file mode 100644 index 115c274dbb2411adfd4edc29825fc677af11eb15..0000000000000000000000000000000000000000 --- a/platform/psp/pte-config.h +++ /dev/null @@ -1,34 +0,0 @@ -/* config.h */ - -#ifndef PTE_CONFIG_H -#define PTE_CONFIG_H - -#include -#include -#include - -#define PTE_STATIC_LIB - -/********************************************************************* - * Defaults: see target specific redefinitions below. - *********************************************************************/ - -/* We're building the pthreads-win32 library */ -#define PTE_BUILD - -typedef int pid_t; - -/* Define if you don't have Win32 errno. (eg. WinCE) */ -//#undef NEED_ERRNO - -/* Do we know about type mode_t? */ -//#undef HAVE_MODE_T - -//#undef HAVE_TIMEB -//#define HAVE_TIMEB - -/* Define if you have the timespec struct */ -//#undef HAVE_STRUCT_TIMESPEC -//#define HAVE_STRUCT_TIMESPEC - -#endif diff --git a/platform/psp/pte_types.h b/platform/psp/pte_types.h new file mode 100644 index 0000000000000000000000000000000000000000..4fc2b422b8945dce6ed8a78ddf84254e4e6dc022 --- /dev/null +++ b/platform/psp/pte_types.h @@ -0,0 +1,12 @@ +/* pte_types.h */ + +#ifndef PTE_TYPES_H +#define PTE_TYPES_H + +#include +#include +#include + +typedef int pid_t; + +#endif /* PTE_TYPES_H */ diff --git a/pthread.h b/pthread.h index 9a6e76b55fc28589f614c2ac9030e54bb3a96578..d593d2e3db743f2f3e74a578c7664c5f5bdd6fff 100644 --- a/pthread.h +++ b/pthread.h @@ -44,7 +44,7 @@ #if !defined( PTHREAD_H ) #define PTHREAD_H -#include +#include #include @@ -984,9 +984,7 @@ enum #endif -#ifndef PTE_BUILD - -#ifdef PTE_CLEANUP_CXX +#ifdef PTE_CXX_EXCEPTIONS /* * Redefine the C++ catch keyword to ensure that applications @@ -996,9 +994,7 @@ enum catch( pte_exception & ) { throw; } \ catch( E ) -#endif /* PTE_CLEANUP_CXX */ - -#endif /* ! PTE_BUILD */ +#endif /* ! PTE_CXX_EXCEPTIONS */ #undef PTE_LEVEL #undef PTE_LEVEL_MAX diff --git a/sched.h b/sched.h index 40d42375093a68c5e56f767ecb4c2fa822286ac8..464d0395f904b485f64bf166a27a2c0153273639 100644 --- a/sched.h +++ b/sched.h @@ -51,7 +51,7 @@ #ifndef _SCHED_H #define _SCHED_H -#include +#include #undef PTE_LEVEL