*/
#include <acess.h>
#include <threads.h>
+#include <threads_int.h>
#include <errno.h>
+#include <mutex.h>
#include <semaphore.h>
+#include <hal_proc.h>
// Configuration
#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
#define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake)
+#define SEMAPHORE_DEBUG 0
// --- Schedulers ---
#define SCHED_UNDEF 0
#define SCHED_RR_SIM 2 // Single Queue Round Robin
#define SCHED_RR_PRI 3 // Multi Queue Round Robin
// Set scheduler type
-#define SCHEDULER_TYPE SCHED_LOTTERY
+#define SCHEDULER_TYPE SCHED_RR_PRI
// === CONSTANTS ===
-#define DEFAULT_QUANTUM 10
+#define DEFAULT_QUANTUM 5
#define DEFAULT_PRIORITY 5
#define MIN_PRIORITY 10
const enum eConfigTypes cCONFIG_TYPES[] = {
};
// === IMPORTS ===
-extern void ArchThreads_Init(void);
-extern void Proc_CallFaultHandler(tThread *Thread);
-extern void Proc_DumpThreadCPUState(tThread *Thread);
-extern int GetCPUNum(void);
// === PROTOTYPES ===
void Threads_Init(void);
void Threads_AddActive(tThread *Thread);
tThread *Threads_RemActive(void);
#endif
+void Threads_ToggleTrace(int TID);
void Threads_Fault(int Num);
void Threads_SegFault(tVAddr Addr);
#if 0
#endif
void Threads_Dump(void);
void Threads_DumpActive(void);
-
#if 0
int Mutex_Acquire(tMutex *Mutex);
void Mutex_Release(tMutex *Mutex);
{
ArchThreads_Init();
+ Log_Debug("Threads", "Offsets of tThread");
+ Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority));
+ Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack));
+
// Create Initial Task
#if SCHEDULER_TYPE == SCHED_RR_PRI
gaActiveThreads[gThreadZero.Priority] = &gThreadZero;
if(Thread == NULL) Thread = Proc_GetCurThread();
// Bounds checking
// - If < 0, set to lowest priority
+ // - Minumum priority is actualy a high number, 0 is highest
if(Pri < 0) Pri = MIN_PRIORITY;
if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY;
#if SCHEDULER_TYPE == SCHED_LOTTERY
giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri];
# if DEBUG_TRACE_TICKETS
- Log("Threads_SetTickets: new giFreeTickets = %i", giFreeTickets);
+ Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]",
+ giFreeTickets,
+ caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]);
# endif
#endif
Thread->Priority = Pri;
else
Thread->Priority = Pri;
#endif
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_SetPriority: %p(%i %s) pri set %i",
+ Thread, Thread->TID, Thread->ThreadName,
+ Pri);
+ #endif
}
/**
- * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
* \brief Clone the TCB of the current thread
- * \param Err Error pointer
* \param Flags Flags for something... (What is this for?)
*/
-tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
+tThread *Threads_CloneTCB(Uint Flags)
{
tThread *cur, *new;
int i;
// Allocate and duplicate
new = malloc(sizeof(tThread));
- if(new == NULL) {
- *Err = -ENOMEM;
- return NULL;
- }
+ if(new == NULL) { errno = -ENOMEM; return NULL; }
memcpy(new, cur, sizeof(tThread));
new->CurCPU = -1;
// Get Thread ID
new->TID = giNextTID++;
new->Parent = cur;
+ new->bInstrTrace = 0;
// Clone Name
new->ThreadName = strdup(cur->ThreadName);
}
/**
- * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
- * \brief Clone the TCB of the current thread
+ * \brief Clone the TCB of the kernel thread
*/
tThread *Threads_CloneThreadZero(void)
{
// Set State
new->Remaining = new->Quantum = cur->Quantum;
new->Priority = cur->Priority;
+ new->bInstrTrace = 0;
// Set Signal Handlers
new->CurFaultNum = 0;
SHORTREL( &Thread->IsLocked ); // TODO: We may not actually be released...
// And, reschedule
- if(isCurThread) {
+ if(isCurThread)
+ {
for( ;; )
- HALT();
+ Proc_Reschedule();
}
}
*/
void Threads_Yield(void)
{
- tThread *thread = Proc_GetCurThread();
- thread->Remaining = 0;
- //while(thread->Remaining == 0)
- HALT();
+ Proc_Reschedule();
}
/**
// Release Spinlock
SHORTREL( &glThreadListLock );
-
- while(cur->Status != THREAD_STAT_ACTIVE) HALT();
+
+ while(cur->Status != THREAD_STAT_ACTIVE) {
+ Proc_Reschedule();
+ if( cur->Status != THREAD_STAT_ACTIVE )
+ Log("%i - Huh? why am I up? zzzz...", cur->TID);
+ }
}
SHORTREL( &glThreadListLock );
return -EOK;
+ case THREAD_STAT_SEMAPHORESLEEP: {
+ tSemaphore *sem;
+ tThread *th, *prev=NULL;
+
+ sem = Thread->WaitPointer;
+
+ SHORTLOCK( &sem->Protector );
+
+ // Remove from sleeping queue
+ for( th = sem->Waiting; th; prev = th, th = th->Next )
+ if( th == Thread ) break;
+ if( th )
+ {
+ if(prev)
+ prev->Next = Thread->Next;
+ else
+ sem->Waiting = Thread->Next;
+ if(sem->LastWaiting == Thread)
+ sem->LastWaiting = prev;
+ }
+ else
+ {
+ prev = NULL;
+ for( th = sem->Signaling; th; prev = th, th = th->Next )
+ if( th == Thread ) break;
+ if( !th ) {
+ Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)",
+ Thread, Thread->TID, Thread->ThreadName,
+ sem, sem->ModName, sem->Name);
+ return -EINTERNAL;
+ }
+
+ if(prev)
+ prev->Next = Thread->Next;
+ else
+ sem->Signaling = Thread->Next;
+ if(sem->LastSignaling == Thread)
+ sem->LastSignaling = prev;
+ }
+
+ SHORTLOCK( &glThreadListLock );
+ Threads_AddActive( Thread );
+ SHORTREL( &glThreadListLock );
+
+ #if DEBUG_TRACE_STATE
+ Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName);
+ #endif
+ SHORTREL( &sem->Protector );
+ } return -EOK;
+
case THREAD_STAT_WAITING:
Warning("Threads_Wake - Waiting threads are not currently supported");
return -ENOTIMPL;
return ret;
}
+void Threads_ToggleTrace(int TID)
+{
+ tThread *thread = Threads_GetThread(TID);
+ if(!thread) return ;
+ thread->bInstrTrace = !thread->bInstrTrace;
+}
+
/**
* \brief Adds a thread to the active queue
*/
if( Thread->Status == THREAD_STAT_ACTIVE ) {
tThread *cur = Proc_GetCurThread();
- Warning("WTF, CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
+ Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active",
+ __builtin_return_address(0),
GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName);
SHORTREL( &glThreadListLock );
return ;
// Set state
Thread->Status = THREAD_STAT_ACTIVE;
+// Thread->CurCPU = -1;
// Add to active list
- #if SCHEDULER_TYPE == SCHED_RR_PRI
- Thread->Next = gaActiveThreads[Thread->Priority];
- gaActiveThreads[Thread->Priority] = Thread;
- #else
- Thread->Next = gActiveThreads;
- gActiveThreads = Thread;
- #endif
+ {
+ tThread *tmp, *prev = NULL;
+ #if SCHEDULER_TYPE == SCHED_RR_PRI
+ for( tmp = gaActiveThreads[Thread->Priority]; tmp; prev = tmp, tmp = tmp->Next );
+ if(prev)
+ prev->Next = Thread;
+ else
+ gaActiveThreads[Thread->Priority] = Thread;
+ #else
+ for( tmp = gActiveThreads; tmp; prev = tmp, tmp = tmp->Next );
+ if(prev)
+ prev->Next = Thread;
+ else
+ gActiveThreads = Thread;
+ #endif
+ Thread->Next = NULL;
+ }
// Update bookkeeping
giNumActiveThreads ++;
/**
* \brief Removes the current thread from the active queue
- * \warning This should ONLY be called with task switches disabled
+ * \warning This should ONLY be called with the lock held
* \return Current thread pointer
*/
tThread *Threads_RemActive(void)
{
tThread *ret = Proc_GetCurThread();
-
- SHORTLOCK( &glThreadListLock );
+
+ if( !IS_LOCKED(&glThreadListLock) ) {
+ Log_KernelPanic("Threads", "Threads_RemActive called without lock held");
+ return NULL;
+ }
// Delete from active queue
#if SCHEDULER_TYPE == SCHED_RR_PRI
#endif
{
SHORTREL( &glThreadListLock );
+ Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue",
+ ret, ret->TID, ret->ThreadName
+ );
return NULL;
}
GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets);
#endif
- SHORTREL( &glThreadListLock );
-
return ret;
}
{
tThread *thread = Proc_GetCurThread();
- Log_Log("Threads", "Threads_Fault: thread = %p", thread);
-
if(!thread) return ;
Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler);
// Double Fault? Oh, F**k
if(thread->CurFaultNum != 0) {
+ Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID);
Threads_Kill(thread, -1); // For now, just kill
HALT();
}
*/
void Threads_SegFault(tVAddr Addr)
{
- Warning("Thread #%i committed a segfault at address %p", Proc_GetCurThread()->TID, Addr);
+ tThread *cur = Proc_GetCurThread();
+ cur->bInstrTrace = 0;
+ Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr);
+ MM_DumpTables(0, USER_MAX);
Threads_Fault( 1 );
//Threads_Exit( 0, -1 );
}
Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", thread->Status, THREAD_STAT_ACTIVE);
Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
Log(" KStack 0x%x", thread->KernelStack);
+ if( thread->bInstrTrace )
+ Log(" Tracing Enabled");
Proc_DumpThreadCPUState(thread);
}
}
Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum);
Log(" KStack 0x%x", thread->KernelStack);
+ if( thread->bInstrTrace )
+ Log(" Tracing Enabled");
+ Proc_DumpThreadCPUState(thread);
}
}
if( gaThreads_NoTaskSwitch[CPU] )
return Last;
-
// Lock thread list
SHORTLOCK( &glThreadListLock );
// Clear Delete Queue
// - I should probably put this in a worker thread to avoid calling free() in the scheduler
// DEFINITELY - free() can deadlock in this case
+ // I'll do it when it becomes an issue
while(gDeleteThreads)
{
thread = gDeleteThreads->Next;
}
gDeleteThreads = thread;
}
-
+
+ // Make sure the current (well, old) thread is marked as de-scheduled
+ if(Last) Last->CurCPU = -1;
+
// No active threads, just take a nap
if(giNumActiveThreads == 0) {
SHORTREL( &glThreadListLock );
}
#if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS
else
- LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released)\n",
+ LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n",
CPU, Last, Last->TID, Last->ThreadName, Last->Status);
#endif
Last->CurCPU = -1;
}
// If we fall onto the same queue again, special handling is
// needed
- if( i == Last->Priority ) {
+ if( Last && Last->Status == THREAD_STAT_ACTIVE && i == Last->Priority ) {
tThread *savedThread = thread;
// Find the next unscheduled thread in the list
SHORTREL(&glThreadListLock);
return NULL;
}
+ if( thread->Status != THREAD_STAT_ACTIVE ) {
+ LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName);
+ }
}
#elif SCHEDULER_TYPE == SCHED_RR_SIM
{
// Make the new thread non-schedulable
thread->CurCPU = CPU;
+ thread->Remaining = thread->Quantum;
SHORTREL( &glThreadListLock );
return thread;
}
-/**
- * \brief Acquire a heavy mutex
- * \param Mutex Mutex to acquire
- *
- * This type of mutex checks if the mutex is avaliable, and acquires it
- * if it is. Otherwise, the current thread is added to the mutex's wait
- * queue and the thread suspends. When the holder of the mutex completes,
- * the oldest thread (top thread) on the queue is given the lock and
- * restarted.
- */
+// Acquire mutex (see mutex.h for documentation)
int Mutex_Acquire(tMutex *Mutex)
{
tThread *us = Proc_GetCurThread();
Mutex->Waiting = us;
Mutex->LastWaiting = us;
}
+
+ #if DEBUG_TRACE_STATE
+ Log("%p (%i %s) waiting on mutex %p",
+ us, us->TID, us->ThreadName, Mutex);
+ #endif
+
#if 0
{
int i = 0;
return 0;
}
-/**
- * \brief Release a held mutex
- * \param Mutex Mutex to release
- * \note Releasing a non-held mutex has no effect
- */
+// Release a mutex
void Mutex_Release(tMutex *Mutex)
{
SHORTLOCK( &Mutex->Protector );
#endif
}
-/**
- * \brief Is this mutex locked?
- * \param Mutex Mutex pointer
- */
+// Check if a mutex is locked
int Mutex_IsLocked(tMutex *Mutex)
{
return Mutex->Owner != NULL;
else
taken = Sem->Value;
Sem->Value -= taken;
- SHORTREL( &Sem->Protector );
}
else
{
Sem->LastWaiting = us;
}
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) waiting on semaphore %p %s:%s",
+ us, us->TID, us->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
+ SHORTREL( &Sem->Protector ); // Release first to make sure it is released
SHORTREL( &glThreadListLock );
- SHORTREL( &Sem->Protector );
while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
- // We're only woken when there's something avaliable
+ // We're only woken when there's something avaliable (or a signal arrives)
us->WaitPointer = NULL;
taken = us->RetStatus;
given = Sem->MaxValue - Sem->Value;
Sem->Value -= given;
+
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) woken by wait on %p %s:%s",
+ toWake, toWake->TID, toWake->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
// Save the number we gave to the thread's status
toWake->RetStatus = given;
Sem->LastSignaling = us;
}
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) signaling semaphore %p %s:%s",
+ us, us->TID, us->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
SHORTREL( &glThreadListLock );
SHORTREL( &Sem->Protector );
while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
{
tThread *toWake = Sem->Waiting;
+ // Remove thread from list (double ended, so clear LastWaiting if needed)
Sem->Waiting = Sem->Waiting->Next;
- // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
if( Sem->Waiting == NULL )
Sem->LastWaiting = NULL;
- // Figure out how much to give
+ // Figure out how much to give to woken thread
+ // - Requested count is stored in ->RetStatus
if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
given = toWake->RetStatus;
else
// Save the number we gave to the thread's status
toWake->RetStatus = given;
+ if(toWake->bInstrTrace)
+ Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem);
+ #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG
+ Log("%p (%i %s) woken by signal on %p %s:%s",
+ toWake, toWake->TID, toWake->ThreadName,
+ Sem, Sem->ModName, Sem->Name);
+ #endif
+
// Wake the sleeper
SHORTLOCK( &glThreadListLock );
if( toWake->Status != THREAD_STAT_ACTIVE )
Threads_AddActive(toWake);
+ else
+ Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName);
SHORTREL( &glThreadListLock );
}
SHORTREL( &Sem->Protector );