mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-01 17:27:19 -04:00
fc31053cb6
* Improving compatibility with standard cpp compiler * Add __REGISTER macro for non mwcc compatibility * Fix for PR comments * fix missing define for revolution * Simplifying cSAngle conversion
880 lines
28 KiB
C
880 lines
28 KiB
C
#include <dolphin.h>
|
|
#include <dolphin/os.h>
|
|
|
|
#include "__os.h"
|
|
|
|
#define ENQUEUE_THREAD(thread, queue, link) \
|
|
do { \
|
|
OSThread* __prev = (queue)->tail; \
|
|
if (__prev == NULL) { \
|
|
(queue)->head = (thread); \
|
|
} else { \
|
|
__prev->link.next = (thread); \
|
|
} \
|
|
(thread)->link.prev = __prev; \
|
|
(thread)->link.next = 0; \
|
|
(queue)->tail = (thread); \
|
|
} while(0);
|
|
|
|
#define DEQUEUE_THREAD(thread, queue, link) \
|
|
do { \
|
|
OSThread* __next = (thread)->link.next; \
|
|
OSThread* __prev = (thread)->link.prev; \
|
|
if (__next == NULL) { \
|
|
(queue)->tail = __prev; \
|
|
} else { \
|
|
__next->link.prev = __prev; \
|
|
} \
|
|
if (__prev == NULL) { \
|
|
(queue)->head = __next; \
|
|
} else { \
|
|
__prev->link.next = __next; \
|
|
} \
|
|
} while(0);
|
|
|
|
#define ENQUEUE_THREAD_PRIO(thread, queue, link) \
|
|
do { \
|
|
OSThread* __prev; \
|
|
OSThread* __next; \
|
|
for(__next = (queue)->head; __next \
|
|
&& (__next->priority <= (thread)->priority); \
|
|
__next = __next->link.next) ; \
|
|
\
|
|
if (__next == NULL) { \
|
|
ENQUEUE_THREAD(thread, queue, link); \
|
|
} else { \
|
|
(thread)->link.next = __next; \
|
|
__prev = __next->link.prev; \
|
|
__next->link.prev = (thread); \
|
|
(thread)->link.prev = __prev; \
|
|
if (__prev == NULL) { \
|
|
(queue)->head = (thread); \
|
|
} else { \
|
|
__prev->link.next = (thread); \
|
|
} \
|
|
} \
|
|
} while(0);
|
|
|
|
#define DEQUEUE_HEAD(thread, queue, link) \
|
|
do { \
|
|
OSThread* __next = thread->link.next; \
|
|
if (__next == NULL) { \
|
|
(queue)->tail = 0; \
|
|
} else { \
|
|
__next->link.prev = 0; \
|
|
} \
|
|
(queue)->head = __next; \
|
|
} while(0);
|
|
|
|
// defined in linkscript
|
|
extern u8 _stack_end[];
|
|
extern u8 _stack_addr[];
|
|
|
|
static OSThreadQueue RunQueue[32];
|
|
static OSThread IdleThread;
|
|
static OSThread DefaultThread;
|
|
static OSContext IdleContext;
|
|
static volatile u32 RunQueueBits;
|
|
static volatile int RunQueueHint;
|
|
static s32 Reschedule;
|
|
|
|
#define ALIGN4(val) (((val) + 0x3) & ~0x3)
|
|
#define ALIGN8(val) (((val) + 0x7) & ~0x7)
|
|
|
|
// prototypes
|
|
static void OSInitMutexQueue(OSMutexQueue* queue);
|
|
static void __OSSwitchThread(OSThread* nextThread);
|
|
static int __OSIsThreadActive(OSThread* thread);
|
|
static void SetRun(OSThread* thread);
|
|
static void UnsetRun(OSThread* thread);
|
|
static OSThread* SetEffectivePriority(OSThread* thread, s32 priority);
|
|
static void UpdatePriority(OSThread* thread);
|
|
static OSThread* SelectThread(int yield);
|
|
static int CheckThreadQueue(OSThreadQueue* queue);
|
|
static int IsMember(OSThreadQueue* queue, OSThread* thread);
|
|
static void OSSetCurrentThread(OSThread* thread);
|
|
|
|
static void DefaultSwitchThreadCallback(OSThread* from, OSThread* to) {}
|
|
static OSSwitchThreadCallback SwitchThreadCallback = DefaultSwitchThreadCallback;
|
|
|
|
OSSwitchThreadCallback OSSetSwitchThreadCallback(OSSwitchThreadCallback callback) {
|
|
OSSwitchThreadCallback prev;
|
|
BOOL enabled;
|
|
|
|
enabled = OSDisableInterrupts();
|
|
prev = SwitchThreadCallback;
|
|
|
|
SwitchThreadCallback = callback ? callback : DefaultSwitchThreadCallback;
|
|
OSRestoreInterrupts(enabled);
|
|
|
|
if (prev == DefaultSwitchThreadCallback) {
|
|
return NULL;
|
|
}
|
|
|
|
return prev;
|
|
}
|
|
|
|
static inline void OSSetCurrentThread(OSThread* thread) {
|
|
SwitchThreadCallback(__OSCurrentThread, thread);
|
|
__OSCurrentThread = thread;
|
|
}
|
|
|
|
void __OSThreadInit() {
|
|
OSThread* thread = &DefaultThread;
|
|
OSPriority prio;
|
|
|
|
thread->state = OS_THREAD_STATE_RUNNING;
|
|
thread->attr = 1;
|
|
thread->priority = thread->base = 0x10;
|
|
thread->suspend = 0;
|
|
thread->val = (void*)-1; // wut
|
|
thread->mutex = 0;
|
|
|
|
OSInitThreadQueue(&thread->queueJoin);
|
|
#if DEBUG
|
|
OSInitMutexQueue(&thread->queueMutex);
|
|
#else
|
|
thread->queueMutex.head = thread->queueMutex.tail = 0; // it got inlined? cant reproduce the inline...
|
|
#endif
|
|
|
|
ASSERTLINE(LINE(348, 357, 357), PPCMfmsr() & MSR_FP);
|
|
|
|
__gUnkThread1 = thread;
|
|
OSClearContext(&thread->context);
|
|
OSSetCurrentContext(&thread->context);
|
|
thread->stackBase = (u8*)&_stack_addr;
|
|
thread->stackEnd = (u32*)&_stack_end;
|
|
*(u32*)thread->stackEnd = OS_THREAD_STACK_MAGIC;
|
|
OSSetCurrentThread(thread);
|
|
OSClearStack(0);
|
|
RunQueueBits = 0;
|
|
RunQueueHint = 0;
|
|
|
|
for (prio = 0; prio <= 31; prio++) {
|
|
OSInitThreadQueue(&RunQueue[prio]);
|
|
}
|
|
OSInitThreadQueue(&__OSActiveThreadQueue);
|
|
|
|
ENQUEUE_THREAD(thread, &__OSActiveThreadQueue, linkActive);
|
|
|
|
OSClearContext(&IdleContext);
|
|
Reschedule = 0;
|
|
}
|
|
|
|
#if DEBUG
|
|
static void OSInitMutexQueue(OSMutexQueue* queue) {
|
|
queue->head = queue->tail = 0;
|
|
}
|
|
#endif
|
|
|
|
void OSInitThreadQueue(OSThreadQueue* queue) {
|
|
queue->head = queue->tail = 0;
|
|
}
|
|
|
|
OSThread* OSGetCurrentThread() {
|
|
return __OSCurrentThread;
|
|
}
|
|
|
|
static void __OSSwitchThread(OSThread* nextThread) {
|
|
OSSetCurrentThread(nextThread);
|
|
OSSetCurrentContext(&nextThread->context);
|
|
OSLoadContext(&nextThread->context);
|
|
}
|
|
|
|
BOOL OSIsThreadSuspended(OSThread* thread) {
|
|
if (thread->suspend > 0) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL OSIsThreadTerminated(OSThread* thread) {
|
|
return (thread->state == OS_THREAD_STATE_MORIBUND || thread->state == 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
static BOOL __OSIsThreadActive(OSThread* thread) {
|
|
OSThread* active;
|
|
|
|
if (thread->state == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (active = __OSActiveThreadQueue.head; active; active = active->linkActive.next) {
|
|
if (thread == active) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
s32 OSDisableScheduler(void) {
|
|
__REGISTER BOOL enabled;
|
|
s32 count;
|
|
|
|
enabled = OSDisableInterrupts();
|
|
count = Reschedule;
|
|
Reschedule = count + 1;
|
|
OSRestoreInterrupts(enabled);
|
|
return count;
|
|
}
|
|
|
|
s32 OSEnableScheduler(void) {
|
|
__REGISTER BOOL enabled;
|
|
s32 count;
|
|
|
|
enabled = OSDisableInterrupts();
|
|
count = Reschedule;
|
|
Reschedule = count - 1;
|
|
OSRestoreInterrupts(enabled);
|
|
return count;
|
|
}
|
|
|
|
static void SetRun(OSThread* thread) {
|
|
ASSERTLINE(LINE(536, 554, 554), !IsSuspended(thread->suspend));
|
|
ASSERTLINE(LINE(537, 555, 555), thread->state == OS_THREAD_STATE_READY);
|
|
|
|
ASSERTLINE(LINE(539, 557, 557), OS_PRIORITY_MIN <= thread->priority && thread->priority <= OS_PRIORITY_MAX);
|
|
|
|
thread->queue = &RunQueue[thread->priority];
|
|
|
|
ENQUEUE_THREAD(thread, thread->queue, link);
|
|
|
|
RunQueueBits |= 1 << (OS_PRIORITY_MAX - thread->priority);
|
|
RunQueueHint = 1;
|
|
}
|
|
|
|
static void UnsetRun(OSThread* thread) {
|
|
OSThreadQueue* queue;
|
|
|
|
ASSERTLINE(LINE(560, 578, 578), thread->state == OS_THREAD_STATE_READY);
|
|
|
|
ASSERTLINE(LINE(562, 580, 580), OS_PRIORITY_MIN <= thread->priority && thread->priority <= OS_PRIORITY_MAX);
|
|
ASSERTLINE(LINE(563, 581, 581), thread->queue == &RunQueue[thread->priority]);
|
|
|
|
queue = thread->queue;
|
|
|
|
DEQUEUE_THREAD(thread, queue, link);
|
|
|
|
if (!queue->head) {
|
|
RunQueueBits &= ~(1 << (OS_PRIORITY_MAX - thread->priority));
|
|
}
|
|
thread->queue = NULL;
|
|
}
|
|
|
|
s32 __OSGetEffectivePriority(OSThread* thread) {
|
|
s32 priority = thread->base;
|
|
OSMutex* mutex;
|
|
|
|
for (mutex = thread->queueMutex.head; mutex; mutex = mutex->link.next) {
|
|
OSThread* blocked = mutex->queue.head;
|
|
if (blocked && blocked->priority < priority) {
|
|
priority = blocked->priority;
|
|
}
|
|
}
|
|
return priority;
|
|
}
|
|
|
|
static OSThread* SetEffectivePriority(OSThread* thread, s32 priority) {
|
|
ASSERTLINE(LINE(614, 632, 632), !IsSuspended(thread->suspend));
|
|
|
|
switch(thread->state) {
|
|
case OS_THREAD_STATE_READY:
|
|
UnsetRun(thread);
|
|
thread->priority = priority;
|
|
SetRun(thread);
|
|
break;
|
|
case OS_THREAD_STATE_WAITING:
|
|
DEQUEUE_THREAD(thread, thread->queue, link);
|
|
thread->priority = priority;
|
|
|
|
ENQUEUE_THREAD_PRIO(thread, thread->queue, link);
|
|
|
|
if (thread->mutex) {
|
|
ASSERTLINE(LINE(629, 647, 647), thread->mutex->thread);
|
|
return thread->mutex->thread;
|
|
}
|
|
break;
|
|
case OS_THREAD_STATE_RUNNING:
|
|
RunQueueHint = 1;
|
|
thread->priority = priority;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void UpdatePriority(OSThread* thread) {
|
|
s32 priority;
|
|
|
|
while (1) {
|
|
if(thread->suspend > 0) {
|
|
break;
|
|
}
|
|
priority = __OSGetEffectivePriority(thread);
|
|
if (thread->priority == priority) {
|
|
break;
|
|
}
|
|
thread = SetEffectivePriority(thread, priority);
|
|
if (thread == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void __OSPromoteThread(OSThread* thread, s32 priority) {
|
|
while (1) {
|
|
if (thread->suspend > 0 || thread->priority <= priority) {
|
|
break;
|
|
}
|
|
thread = SetEffectivePriority(thread, priority);
|
|
if (thread == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static OSThread* SelectThread(int yield) {
|
|
OSContext* currentContext;
|
|
OSThread* currentThread;
|
|
OSThread* nextThread;
|
|
OSPriority priority;
|
|
OSThreadQueue* queue;
|
|
|
|
if (Reschedule > 0) {
|
|
return NULL;
|
|
}
|
|
|
|
currentContext = OSGetCurrentContext();
|
|
currentThread = OSGetCurrentThread();
|
|
|
|
if (currentContext != ¤tThread->context) {
|
|
return NULL;
|
|
}
|
|
|
|
if (currentThread) {
|
|
if (currentThread->state == 2) {
|
|
if (yield == 0) {
|
|
priority = __cntlzw(RunQueueBits);
|
|
if (currentThread->priority <= priority)
|
|
return NULL;
|
|
}
|
|
currentThread->state = OS_THREAD_STATE_READY;
|
|
SetRun(currentThread);
|
|
}
|
|
if (!(currentThread->context.state & 2) && (OSSaveContext(¤tThread->context) != 0)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (RunQueueBits == 0) {
|
|
OSSetCurrentThread(NULL);
|
|
OSSetCurrentContext(&IdleContext);
|
|
do {
|
|
OSEnableInterrupts();
|
|
while (RunQueueBits == 0) ;
|
|
OSDisableInterrupts();
|
|
} while (RunQueueBits == 0);
|
|
OSClearContext(&IdleContext);
|
|
}
|
|
|
|
RunQueueHint = 0;
|
|
priority = __cntlzw(RunQueueBits);
|
|
|
|
ASSERTLINE(LINE(777, 808, 808), OS_PRIORITY_MIN <= priority && priority <= OS_PRIORITY_MAX);
|
|
|
|
queue = &RunQueue[priority];
|
|
nextThread = queue->head;
|
|
|
|
DEQUEUE_HEAD(nextThread, queue, link);
|
|
|
|
ASSERTLINE(LINE(780, 811, 811), nextThread->priority == priority);
|
|
|
|
if (!queue->head) {
|
|
RunQueueBits &= ~(1 << (OS_PRIORITY_MAX - priority));
|
|
}
|
|
nextThread->queue = 0;
|
|
nextThread->state = OS_THREAD_STATE_RUNNING;
|
|
__OSSwitchThread(nextThread);
|
|
return nextThread;
|
|
}
|
|
|
|
void __OSReschedule(void) {
|
|
if (RunQueueHint != 0) {
|
|
SelectThread(0);
|
|
}
|
|
}
|
|
|
|
void OSYieldThread(void) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
SelectThread(1);
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
int OSCreateThread(OSThread* thread, void* (*func)(void*), void* param, void* stack, u32 stackSize, OSPriority priority, u16 attr) {
|
|
BOOL enabled;
|
|
u32 sp;
|
|
int i;
|
|
|
|
ASSERTMSGLINE(LINE(864, 895, 895), ((priority >= OS_PRIORITY_MIN) && (priority <= OS_PRIORITY_MAX)), "OSCreateThread(): priority out of range (0 <= priority <= 31).");
|
|
|
|
// why check this for an assert just to check it again right after?
|
|
if ((priority < OS_PRIORITY_MIN) || (priority > OS_PRIORITY_MAX)) {
|
|
return 0;
|
|
}
|
|
|
|
thread->state = OS_THREAD_STATE_READY;
|
|
thread->attr = attr & 1U;
|
|
thread->base = priority;
|
|
thread->priority = priority;
|
|
thread->suspend = 1;
|
|
thread->val = (void*)-1;
|
|
thread->mutex = 0;
|
|
OSInitThreadQueue(&thread->queueJoin);
|
|
#if DEBUG
|
|
OSInitMutexQueue(&thread->queueMutex);
|
|
#else
|
|
OSInitThreadQueue((void*)&thread->queueMutex); // why
|
|
#endif
|
|
sp = (u32)stack;
|
|
sp &= ~7;
|
|
sp -= 8;
|
|
((u32*)sp)[0] = 0;
|
|
((u32*)sp)[1] = 0;
|
|
OSInitContext(&thread->context, (u32)func, sp);
|
|
thread->context.lr = (u32)&OSExitThread;
|
|
thread->context.gpr[3] = (u32)param;
|
|
thread->stackBase = stack;
|
|
thread->stackEnd = (void*)((unsigned int)stack - stackSize);
|
|
*thread->stackEnd = OS_THREAD_STACK_MAGIC;
|
|
thread->error = 0;
|
|
for (i = 0; i < 2; i++) {
|
|
thread->specific[i] = NULL;
|
|
}
|
|
enabled = OSDisableInterrupts();
|
|
|
|
if (__OSErrorTable[16] != NULL) {
|
|
thread->context.srr1 |= 0x900;
|
|
thread->context.state |= 1;
|
|
thread->context.fpscr = (__OSFpscrEnableBits & 0xf8) | 4;
|
|
for (i = 0; i < 32; ++i) {
|
|
*(u64*)&thread->context.fpr[i] = (u64)0xffffffffffffffffLL;
|
|
*(u64*)&thread->context.psf[i] = (u64)0xffffffffffffffffLL;
|
|
}
|
|
}
|
|
|
|
ASSERTMSG1LINE(LINE(918, 949, 949), __OSIsThreadActive(thread) == 0L, "OSCreateThread(): thread %p is still active.", thread);
|
|
|
|
ENQUEUE_THREAD(thread, &__OSActiveThreadQueue, linkActive);
|
|
|
|
OSRestoreInterrupts(enabled);
|
|
return 1;
|
|
}
|
|
|
|
void OSExitThread(void* val) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
OSThread* currentThread = OSGetCurrentThread();
|
|
|
|
ASSERTMSGLINE(LINE(943, 974, 974), currentThread,
|
|
"OSExitThread(): current thread does not exist.");
|
|
ASSERTMSGLINE(LINE(945, 976, 976), currentThread->state == OS_THREAD_STATE_RUNNING,
|
|
"OSExitThread(): current thread is not running.");
|
|
ASSERTMSGLINE(LINE(947, 978, 978), __OSIsThreadActive(currentThread) != 0,
|
|
"OSExitThread(): current thread is not active.");
|
|
|
|
OSClearContext(¤tThread->context);
|
|
if (currentThread->attr & 1) {
|
|
DEQUEUE_THREAD(currentThread, &__OSActiveThreadQueue, linkActive);
|
|
currentThread->state = 0;
|
|
} else {
|
|
currentThread->state = 8;
|
|
currentThread->val = val;
|
|
}
|
|
__OSUnlockAllMutex(currentThread);
|
|
OSWakeupThread(¤tThread->queueJoin);
|
|
RunQueueHint = 1;
|
|
#if DEBUG
|
|
__OSReschedule();
|
|
#else
|
|
if (RunQueueHint != 0) {
|
|
SelectThread(0);
|
|
}
|
|
#endif
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
void OSCancelThread(OSThread* thread) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
ASSERTMSG1LINE(LINE(985, 1016, 1016), __OSIsThreadActive(thread) != 0,
|
|
"OSExitThread(): thread %p is not active.", thread);
|
|
|
|
switch(thread->state) {
|
|
case OS_THREAD_STATE_READY:
|
|
if (thread->suspend <= 0) {
|
|
UnsetRun(thread);
|
|
}
|
|
break;
|
|
case OS_THREAD_STATE_RUNNING:
|
|
RunQueueHint = 1;
|
|
break;
|
|
case OS_THREAD_STATE_WAITING:
|
|
DEQUEUE_THREAD(thread, thread->queue, link);
|
|
thread->queue = 0;
|
|
if ((thread->suspend <= 0) && (thread->mutex)) {
|
|
ASSERTLINE(LINE(1004, 1035, 1035), thread->mutex->thread);
|
|
UpdatePriority(thread->mutex->thread);
|
|
}
|
|
break;
|
|
default:
|
|
OSRestoreInterrupts(enabled);
|
|
return;
|
|
}
|
|
OSClearContext(&thread->context);
|
|
if (thread->attr & 1) {
|
|
DEQUEUE_THREAD(thread, &__OSActiveThreadQueue, linkActive);
|
|
thread->state = 0;
|
|
} else {
|
|
thread->state = 8;
|
|
}
|
|
__OSUnlockAllMutex(thread);
|
|
OSWakeupThread(&thread->queueJoin);
|
|
__OSReschedule();
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
int OSJoinThread(OSThread* thread, void* val) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
ASSERTMSG1LINE(LINE(1061, 1092, 1092), __OSIsThreadActive(thread) != 0, "OSJoinThread(): thread %p is not active.", thread);
|
|
|
|
if (!(thread->attr & 1) && (thread->state != OS_THREAD_STATE_MORIBUND) && (thread->queueJoin.head == NULL)) {
|
|
OSSleepThread(&thread->queueJoin);
|
|
if (__OSIsThreadActive(thread) == 0) {
|
|
OSRestoreInterrupts(enabled);
|
|
return 0;
|
|
}
|
|
}
|
|
if (thread->state == OS_THREAD_STATE_MORIBUND) {
|
|
if (val) {
|
|
*(s32*)val = (s32)thread->val;
|
|
}
|
|
DEQUEUE_THREAD(thread, &__OSActiveThreadQueue, linkActive);
|
|
thread->state = 0;
|
|
OSRestoreInterrupts(enabled);
|
|
return 1;
|
|
}
|
|
OSRestoreInterrupts(enabled);
|
|
return 0;
|
|
}
|
|
|
|
void OSDetachThread(OSThread* thread) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
ASSERTMSG1LINE(LINE(1111, 1142, 1142), __OSIsThreadActive(thread) != 0, "OSDetachThread(): thread %p is not active.", thread);
|
|
|
|
thread->attr |= 1;
|
|
if (thread->state == OS_THREAD_STATE_MORIBUND) {
|
|
DEQUEUE_THREAD(thread, &__OSActiveThreadQueue, linkActive);
|
|
thread->state = 0;
|
|
}
|
|
OSWakeupThread(&thread->queueJoin);
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
s32 OSResumeThread(OSThread* thread) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
s32 suspendCount;
|
|
|
|
ASSERTMSG1LINE(LINE(1140, 1171, 1171), __OSIsThreadActive(thread) != 0, "OSResumeThread(): thread %p is not active.", thread);
|
|
ASSERTMSG1LINE(LINE(1142, 1173, 1173), thread->state != OS_THREAD_STATE_MORIBUND, "OSResumeThread(): thread %p is terminated.", thread);
|
|
|
|
suspendCount = thread->suspend--;
|
|
if (thread->suspend < 0) {
|
|
thread->suspend = 0;
|
|
} else if (thread->suspend == 0) {
|
|
switch(thread->state) {
|
|
case OS_THREAD_STATE_READY:
|
|
thread->priority = __OSGetEffectivePriority(thread);
|
|
SetRun(thread);
|
|
break;
|
|
case OS_THREAD_STATE_WAITING:
|
|
ASSERTLINE(LINE(1157, 1188, 1188), thread->queue);
|
|
DEQUEUE_THREAD(thread, thread->queue, link);
|
|
thread->priority = __OSGetEffectivePriority(thread);
|
|
ENQUEUE_THREAD_PRIO(thread, thread->queue, link);
|
|
if (thread->mutex) {
|
|
UpdatePriority(thread->mutex->thread);
|
|
}
|
|
}
|
|
__OSReschedule();
|
|
}
|
|
OSRestoreInterrupts(enabled);
|
|
return suspendCount;
|
|
}
|
|
|
|
s32 OSSuspendThread(OSThread* thread) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
s32 suspendCount;
|
|
|
|
ASSERTMSG1LINE(LINE(1191, 1222, 1222), __OSIsThreadActive(thread) != 0, "OSSuspendThread(): thread %p is not active.", thread);
|
|
ASSERTMSG1LINE(LINE(1193, 1224, 1224), thread->state != OS_THREAD_STATE_MORIBUND, "OSSuspendThread(): thread %p is terminated.", thread);
|
|
|
|
suspendCount = thread->suspend++;
|
|
if (suspendCount == 0) {
|
|
switch(thread->state) {
|
|
case OS_THREAD_STATE_RUNNING:
|
|
RunQueueHint = 1;
|
|
thread->state = 1;
|
|
break;
|
|
case OS_THREAD_STATE_READY:
|
|
UnsetRun(thread);
|
|
break;
|
|
case OS_THREAD_STATE_WAITING:
|
|
DEQUEUE_THREAD(thread, thread->queue, link);
|
|
thread->priority = 0x20;
|
|
ENQUEUE_THREAD(thread, thread->queue, link);
|
|
if (thread->mutex) {
|
|
ASSERTLINE(LINE(1214, 1245, 1245), thread->mutex->thread);
|
|
UpdatePriority(thread->mutex->thread);
|
|
}
|
|
break;
|
|
}
|
|
__OSReschedule();
|
|
}
|
|
OSRestoreInterrupts(enabled);
|
|
return suspendCount;
|
|
}
|
|
|
|
void OSSleepThread(OSThreadQueue* queue) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
OSThread* currentThread = OSGetCurrentThread();
|
|
|
|
ASSERTMSGLINE(LINE(1247, 1278, 1278), currentThread, "OSSleepThread(): current thread does not exist.");
|
|
ASSERTMSG1LINE(LINE(1249, 1280, 1280), __OSIsThreadActive(currentThread) != 0, "OSSleepThread(): current thread %p is not active.", currentThread);
|
|
ASSERTMSG1LINE(LINE(1251, 1282, 1282), currentThread->state == OS_THREAD_STATE_RUNNING, "OSSleepThread(): current thread %p is not running.", currentThread);
|
|
ASSERTMSG1LINE(LINE(1253, 1284, 1284), currentThread->suspend <= 0, "OSSleepThread(): current thread %p is suspended.", currentThread);
|
|
|
|
currentThread->state = OS_THREAD_STATE_WAITING;
|
|
currentThread->queue = queue;
|
|
ENQUEUE_THREAD_PRIO(currentThread, queue, link);
|
|
RunQueueHint = 1;
|
|
__OSReschedule();
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
void OSWakeupThread(OSThreadQueue* queue) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
while (queue->head) {
|
|
OSThread* thread = queue->head;
|
|
|
|
DEQUEUE_HEAD(thread, queue, link);
|
|
|
|
ASSERTLINE(LINE(1282, 1313, 1313), __OSIsThreadActive(thread));
|
|
ASSERTLINE(LINE(1283, 1314, 1314), thread->state != OS_THREAD_STATE_MORIBUND);
|
|
ASSERTLINE(LINE(1284, 1315, 1315), thread->queue == queue);
|
|
thread->state = OS_THREAD_STATE_READY;
|
|
if (thread->suspend <= 0) {
|
|
SetRun(thread);
|
|
}
|
|
}
|
|
__OSReschedule();
|
|
OSRestoreInterrupts(enabled);
|
|
}
|
|
|
|
int OSSetThreadPriority(OSThread* thread, s32 priority) {
|
|
BOOL enabled;
|
|
|
|
ASSERTMSGLINE(LINE(1310, 1341, 1341), (priority >= OS_PRIORITY_MIN) && (priority <= OS_PRIORITY_MAX), "OSSetThreadPriority(): priority out of range (0 <= priority <= 31).");
|
|
|
|
if ((priority < OS_PRIORITY_MIN) || (priority > OS_PRIORITY_MAX)) {
|
|
return 0;
|
|
}
|
|
enabled = OSDisableInterrupts();
|
|
|
|
ASSERTMSG1LINE(LINE(1317, 1348, 1348), __OSIsThreadActive(thread) != 0, "OSSetThreadPriority(): thread %p is not active.", thread);
|
|
ASSERTMSG1LINE(LINE(1319, 1350, 1350), thread->state != 8, "OSSetThreadPriority(): thread %p is terminated.", thread);
|
|
|
|
if (thread->base != priority) {
|
|
thread->base = priority;
|
|
UpdatePriority(thread);
|
|
__OSReschedule();
|
|
}
|
|
OSRestoreInterrupts(enabled);
|
|
return 1;
|
|
}
|
|
|
|
s32 OSGetThreadPriority(OSThread* thread) {
|
|
return thread->base;
|
|
}
|
|
|
|
OSThread* OSSetIdleFunction(OSIdleFunction idleFunction, void* param, void* stack, u32 stackSize) {
|
|
if (idleFunction) {
|
|
if (IdleThread.state == 0) {
|
|
OSCreateThread(&IdleThread, (void*)idleFunction, param, stack, stackSize, OS_PRIORITY_MAX, 1);
|
|
OSResumeThread(&IdleThread);
|
|
return &IdleThread;
|
|
}
|
|
} else if (IdleThread.state != 0) {
|
|
OSCancelThread(&IdleThread);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
OSThread* OSGetIdleFunction(void) {
|
|
if (IdleThread.state != 0) {
|
|
return &IdleThread;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int CheckThreadQueue(OSThreadQueue* queue) {
|
|
OSThread* thread;
|
|
|
|
if ((queue->head != NULL) && (queue->head->link.prev != NULL)) {
|
|
return 0;
|
|
}
|
|
if ((queue->tail != NULL) && (queue->tail->link.next != NULL)) {
|
|
return 0;
|
|
}
|
|
thread = queue->head;
|
|
while (thread) {
|
|
if ((thread->link.next != NULL) && (thread != thread->link.next->link.prev)) {
|
|
return 0;
|
|
}
|
|
if ((thread->link.prev != NULL) && (thread != thread->link.prev->link.next)) {
|
|
return 0;
|
|
}
|
|
thread = thread->link.next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static BOOL IsMember(OSThreadQueue* queue, OSThread* thread) {
|
|
OSThread* member = queue->head;
|
|
|
|
while (member) {
|
|
if (thread == member) {
|
|
return TRUE;
|
|
}
|
|
member = member->link.next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// custom macro for OSCheckActiveThreads?
|
|
#define ASSERTREPORT(line, cond) \
|
|
if (!(cond)) { \
|
|
OSReport("OSCheckActiveThreads: Failed " #cond " in %d\n", line); \
|
|
OSPanic(__FILE__, line, ""); \
|
|
}
|
|
|
|
s32 OSCheckActiveThreads(void) {
|
|
OSThread* thread;
|
|
s32 prio;
|
|
s32 cThread;
|
|
BOOL enabled;
|
|
|
|
cThread = 0;
|
|
enabled = OSDisableInterrupts();
|
|
|
|
for (prio = 0; prio <= OS_PRIORITY_MAX; prio++) {
|
|
if (RunQueueBits & (1 << (OS_PRIORITY_MAX - prio))) {
|
|
ASSERTREPORT(LINE(1473, 1504, 1504), RunQueue[prio].head != NULL && RunQueue[prio].tail != NULL);
|
|
} else {
|
|
ASSERTREPORT(LINE(1478, 1509, 1509), RunQueue[prio].head == NULL && RunQueue[prio].tail == NULL);
|
|
}
|
|
ASSERTREPORT(LINE(1480, 1511, 1511), CheckThreadQueue(&RunQueue[prio]));
|
|
}
|
|
|
|
ASSERTREPORT(LINE(1485, 1516, 1516), __OSActiveThreadQueue.head == NULL || __OSActiveThreadQueue.head->linkActive.prev == NULL);
|
|
ASSERTREPORT(LINE(1487, 1518, 1518), __OSActiveThreadQueue.tail == NULL || __OSActiveThreadQueue.tail->linkActive.next == NULL);
|
|
|
|
thread = __OSActiveThreadQueue.head;
|
|
while (thread) {
|
|
cThread++;
|
|
ASSERTREPORT(LINE(1495, 1526, 1526), thread->linkActive.next == NULL || thread == thread->linkActive.next->linkActive.prev);
|
|
ASSERTREPORT(LINE(1497, 1528, 1528), thread->linkActive.prev == NULL || thread == thread->linkActive.prev->linkActive.next);
|
|
ASSERTREPORT(LINE(1500, 1531, 1531), *(thread->stackEnd) == OS_THREAD_STACK_MAGIC);
|
|
ASSERTREPORT(LINE(1503, 1534, 1534), OS_PRIORITY_MIN <= thread->priority && thread->priority <= OS_PRIORITY_MAX+1);
|
|
ASSERTREPORT(LINE(1504, 1535, 1535), 0 <= thread->suspend);
|
|
ASSERTREPORT(LINE(1505, 1536, 1536), CheckThreadQueue(&thread->queueJoin));
|
|
|
|
switch(thread->state) {
|
|
case OS_THREAD_STATE_READY:
|
|
if (thread->suspend <= 0) {
|
|
ASSERTREPORT(LINE(1511, 1542, 1542), thread->queue == &RunQueue[thread->priority]);
|
|
ASSERTREPORT(LINE(1512, 1543, 1543), IsMember(&RunQueue[thread->priority], thread));
|
|
ASSERTREPORT(LINE(1513, 1544, 1544), thread->priority == __OSGetEffectivePriority(thread));
|
|
}
|
|
break;
|
|
case OS_THREAD_STATE_RUNNING:
|
|
ASSERTREPORT(LINE(1517, 1548, 1548), !IsSuspended(thread->suspend));
|
|
ASSERTREPORT(LINE(1518, 1549, 1549), thread->queue == NULL);
|
|
ASSERTREPORT(LINE(1519, 1550, 1550), thread->priority == __OSGetEffectivePriority(thread));
|
|
break;
|
|
case OS_THREAD_STATE_WAITING:
|
|
ASSERTREPORT(LINE(1522, 1553, 1553), thread->queue != NULL);
|
|
ASSERTREPORT(LINE(1523, 1554, 1554), CheckThreadQueue(thread->queue));
|
|
ASSERTREPORT(LINE(1524, 1555, 1555), IsMember(thread->queue, thread));
|
|
if (thread->suspend <= 0) {
|
|
ASSERTREPORT(LINE(1527, 1558, 1558), thread->priority == __OSGetEffectivePriority(thread));
|
|
} else {
|
|
ASSERTREPORT(LINE(1531, 1562, 1562), thread->priority == 32);
|
|
}
|
|
ASSERTREPORT(LINE(1533, 1564, 1564), !__OSCheckDeadLock(thread));
|
|
break;
|
|
case OS_THREAD_STATE_MORIBUND:
|
|
ASSERTREPORT(LINE(1537, 1568, 1568), thread->queueMutex.head == NULL && thread->queueMutex.tail == NULL);
|
|
break;
|
|
default:
|
|
OSReport("OSCheckActiveThreads: Failed. unkown thread state (%d) of thread %p\n", thread->state, thread);
|
|
OSPanic(__FILE__, LINE(1543, 1574, 1574), "");
|
|
}
|
|
ASSERTREPORT(LINE(1548, 1579, 1579), __OSCheckMutexes(thread));
|
|
thread = thread->linkActive.next;
|
|
}
|
|
OSRestoreInterrupts(enabled);
|
|
return cThread;
|
|
}
|
|
|
|
void OSClearStack(u8 val) {
|
|
__REGISTER u32 sp;
|
|
__REGISTER u32* p;
|
|
__REGISTER u32 pattern;
|
|
|
|
pattern = (val << 24) | (val << 16) | (val << 8) | val;
|
|
sp = OSGetStackPointer();
|
|
for (p = __OSCurrentThread->stackEnd + 1; (u32)p < sp; ++p) {
|
|
*p = pattern;
|
|
}
|
|
}
|
|
|
|
void OSSetThreadSpecific(s32 index, void* ptr) {
|
|
OSThread* thread;
|
|
|
|
thread = __OSCurrentThread;
|
|
ASSERTLINE(LINE(1573, 1604, 1604), 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
|
|
|
|
if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) {
|
|
thread->specific[index] = ptr;
|
|
}
|
|
}
|
|
|
|
void* OSGetThreadSpecific(s32 index) {
|
|
OSThread* thread;
|
|
|
|
thread = __OSCurrentThread;
|
|
ASSERTLINE(LINE(1584, 1615, 1615), 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
|
|
|
|
if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) {
|
|
return thread->specific[index];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#include "global.h"
|
|
extern u8 Debug_BBA_804516D0;
|
|
u8 Debug_BBA_804516D0 ATTRIBUTE_ALIGN(8);
|