The core library of the EE.
Based on the EE kernel emulation in the Play! PS2 Emulator and my own tests.
Semaphores
Synchronization on the EE is provided by semaphores. Semaphores have a wait queue for threads when the resource is used.
Attributes
- sema.count
- Filled by ReferSemaStatus(). The current count.
- sema.max_count
- The number of times the semaphore can be used.
- sema.init_count
- The initial count value when creating the semaphore.
- sema.wait_threads
- The number of threads waiting on the semaphore.
- sema.option
- A pointer to user data. E.g. A string constant describing its use.
Mutexes
A mutex is a semaphore that allows only one running thread into a critical section of code.
- sema.max_count
- Set the max count to 1.
- sema.init_count
- To create a mutex locked, set the init_count to 0.
- sema.init_count
- To create a mutex unlocked, set the init_count to 1.
Using Semaphores
The functions with a lowercase 'i' should be used when interrupts are disabled like when in an interrupt handler.
s32 CreateSema(ee_sema_t *sema)
Returns the id of the semaphore.
s32 WaitSema(s32 sema_id)
A thread calls WaitSema() decreasing the count using the resource. When another thread calls WaitSema(), if the count is 0, that thread is put into the semaphore's wait queue.
s32 SignalSema(s32 sema_id)
s32 iSignalSema(s32 sema_id)
A thread calls SignalSema() increasing the count returning the resource. The next thread waiting in the semaphore's queue is run.
s32 iPollSema(s32 sema_id)
s32 PollSema(s32 sema_id)
A thread calls PollSema() decreasing the count using the resource. If the count is already zero, it does not put the thread calling PollSema() into a wait state, but returns an error.
Using PollSema() prior to WaitSema() on a semaphore acting as a mutex can create a race condition.
- E.g.
- A thread calls SignalSema() on a semaphore, then a thread using PollSema() uses the resource. WaitSema() then puts the next thread into wait status. No other threads are in the critical section of code to call SignalSema() to free the resource.
s32 DeleteSema(s32 sema_id)
s32 iDeleteSema(s32 sema_id)
After DeleteSema() is called, threads in the semaphore's wait queue are returned to running status, the semaphore is deleted, and calls referring to the semaphore return an error.
s32 ReferSemaStatus(s32 sema_id, ee_sema_t *sema)
s32 iReferSemaStatus(s32 sema_id, ee_sema_t *sema)
ReferSemaStatus() can be used to check the current count, the max_count, and number of threads waiting on the semaphore.
Threads
Multithreading on the EE kernel is cooperative. The kernel provides a pool of 256 threads to be used. A few are reserved for fixing bugs and various libraries on the EE may run their own.
Cooperative Multithreading
Threads with a higher priority have to cede control to other threads. Threads with a longer amount of execution time will take time away from other threads since there isn't a regular preemptive schedule.
Attributes
- thread.status
- Status of the thread.
Status | Effect |
THS_RUN | Running |
THS_READY | Ready to run |
THS_WAIT | Waiting |
THS_SUSPEND | Suspended |
THS_DORMANT | Newly created or terminated |
Threads in THS_WAIT state can be suspended, too.
- thread.func
- Function for the thread to run.
- thread.stack
- Memory aligned to 16 byte boundary to use as stack.
- thread.stack_size
- Since the stack is 16-byte aligned, size is a multiple of 16.
- thread.gp_reg
- Pointer to global offset (the symbol _gp is defined by the linkscript).
- thread.initial_priority
- Initial priority when using CreateThread(). 0 - 127 (lower number is higher priority, but 0 is reserved by the kernel).
- thread.current_priority
- Filled by ReferThreadStatus(). Contains the thread's current priority.
- thread.option
- Pointer to user data. Documented not to work.
- thread.waitType
- Filled by ReferThreadStatus(). Contains why the thread is waiting.
Type | Reason |
THS_WT_NONE | Not waiting |
THS_WT_WAKE | Waiting for wakeup |
THS_WT_SEMA | Waiting for semaphore |
If a thread state is THS_WAIT and the wait type is THS_WT_WAKE, then the thread has been put to sleep using SleepThread().
- thread.waitId
- Filled by ReferThreadStatus(). Contains the semaphore's ID if waiting on a semaphore.
- thread.wakeupCount
- Filled by ReferThreadStatus(). Contains the number of times other threads have tried to wake up the thread when it wasn't sleeping.
Using Threads
The functions with a lowercase 'i' should be used when interrupts are disabled like when in an interrupt handler.
s32 CreateThread(ee_thread_t *thread)
Create the thread and put it into THS_DORMANT state. Returns thread's id on success.
s32 DeleteThread(s32 thread_id)
Deletes THS_DORMANT thread from another thread.
s32 StartThread(s32 thread_id, void *args)
Starts a THS_DORMANT thread.
Changes the calling thread to THS_DORMANT.
void ExitDeleteThread(void)
Changes the calling thread to THS_DORMANT then returns it to internal pool.
If the wakeupCount is 0, puts the current thread into THS_WAIT status.
Returns the id of the current thread.
s32 iTerminateThread(s32 thread_id)
s32 TerminateThread(s32 thread_id)
Changes a thread to THS_DORMANT state from another thread.
s32 iChangeThreadPriority(s32 thread_id, s32 priority)
s32 ChangeThreadPriority(s32 thread_id, s32 priority)
Changes a thread's priority and schedules it at the bottom of that priority's queue
s32 iRotateThreadReadyQueue(s32 priority)
s32 RotateThreadReadyQueue(s32 priority)
Finds first ready or running thread of a given priority and schedules it at the bottom of the queue.
s32 iReleaseWaitThread(s32 thread_id)
s32 ReleaseWaitThread(s32 thread_id)
Release a thread from THS_WAIT state.
s32 iWakeupThread(s32 thread_id)
s32 WakeupThread(s32 thread_id)
Removes THS_WAIT status from a thread. If the thread isn't in wait status, the wakeupCount is incremented.
s32 iCancelWakeupThread(s32 thread_id)
s32 CancelWakeupThread(s32 thread_id)
Set thread's wakeupCount to 0.
s32 iSuspendThread(s32 thread_id)
s32 SuspendThread(s32 thread_id)
Suspends a thread, adding THS_SUSPEND to its status.
s32 iResumeThread(s32 thread_id)
s32 ResumeThread(s32 thread_id)
Removes THS_SUSPEND state from a thread.
s32 ReferThreadStatus(s32 thread_id, ee_thread_status_t *info)
s32 iReferThreadStatus(s32 thread_id, ee_thread_status_t *info)
Retrieve the status of a thread.
Example
This example shows how to implement a preemptive schedule for a group of threads.
#include <stdio.h>
#define SECONDS_TO_RUN 10
#define THREAD_STACK_SIZE 1024
volatile int counter = 0;
volatile int executing = 0;
#define SCHEDULER_PRIORITY 30
#define SCHEDULER_SECOND 30
#define SCHEDULER_TIME 525
#ifdef USE_SEMA
volatile int scheduler_sema = 0;
#endif
unsigned char scheduler_stack[THREAD_STACK_SIZE]
ALIGNED(16);
int scheduler_id;
unsigned char thread_a_stack[THREAD_STACK_SIZE]
ALIGNED(16);
int thread_a_id;
volatile unsigned int thread_a_cycles = 0;
unsigned char thread_b_stack[THREAD_STACK_SIZE]
ALIGNED(16);
int thread_b_id;
volatile unsigned int thread_b_cycles = 0;
void thread_a(void *arg);
void thread_b(void *arg);
void scheduler(void* a)
{
while(1)
{
#ifdef USE_SEMA
#else
#endif
if (counter > SCHEDULER_SECOND * SECONDS_TO_RUN)
executing = 0;
#ifdef USE_SEMA
#endif
}
}
{
counter++;
#ifdef USE_SEMA
#else
#endif
}
int main(
int argc,
char **argv)
{
volatile unsigned int main_cycles = 0;
int interrupt_id = -1;
#ifdef USE_SEMA
return -1;
#endif
thread.
stack = scheduler_stack;
if (scheduler_id < 0)
return -1;
thread.
stack = thread_a_stack;
{
return -1;
}
thread.
stack = thread_b_stack;
{
return -1;
}
executing = 1;
printf("Starting threads\n");
while(executing)
{
main_cycles++;
}
printf("Threads done: Executed %d %d %d cycles in %d seconds\n",
thread_a_cycles, thread_b_cycles, main_cycles,
counter/SCHEDULER_SECOND);
#ifdef USE_SEMA
#endif
return 0;
}
void thread_a(void *arg)
{
while(1)
{
thread_a_cycles++;
}
}
void thread_b(void *arg)
{
volatile int i;
while(1)
{
thread_b_cycles++;
#if 1
for (i = 0; i > thread_b_cycles; i++)
#endif
{
;
}
}
}
s32 iSetAlarm(u16 time, void(*callback)(s32 alarm_id, u16 time, void *common), void *common)
s32 SetAlarm(u16 time, void(*callback)(s32 alarm_id, u16 time, void *common), void *common)
s32 ReleaseAlarm(s32 alarm_id)