#define KERNEL_BASE 0xC0000000
#define BITS 32
+// Allow nested spinlocks?
+#define STACKED_LOCKS 1
+
// - Processor/Machine Specific Features
#if ARCH != i386 && ARCH != i486 && ARCH != i586
# error "Unknown architecture '" #ARCH "'"
struct sShortSpinlock {
volatile int Lock; //!< Lock value
int IF; //!< Interrupt state on call to SHORTLOCK
+ #if STACKED_LOCKS
+ int Depth;
+ #endif
};
/**
* \brief Determine if a short spinlock is locked
* an element to linked list (usually two assignement lines in C)
*
* \note This type of lock halts interrupts, so ensure that no timing
- * functions are called while it is held.
+ * functions are called while it is held. As a matter of fact, spend as
+ * little time as possible with this lock held
*/
static inline void SHORTLOCK(struct sShortSpinlock *Lock) {
int v = 1;
int IF;
- // int cpu = GetCPUNum() + 1;
+ #if STACKED_LOCKS
+ extern int GetCPUNum(void);
+ int cpu = GetCPUNum() + 1;
+ #endif
// Save interrupt state and clear interrupts
__ASM__ ("pushf;\n\tpop %%eax\n\tcli" : "=a"(IF));
IF &= 0x200; // AND out all but the interrupt flag
+ #if STACKED_LOCKS
+ if( Lock->Lock == cpu ) {
+ Lock->Depth ++;
+ return ;
+ }
+ #endif
+
// Wait for another CPU to release
- while(v)
+ while(v) {
+ #if STACKED_LOCKS
+ // CMPXCHG:
+ // If r/m32 == EAX, set ZF and set r/m32 = r32
+ // Else, clear ZF and set EAX = r/m32
+ __ASM__("lock cmpxchgl %%ecx, (%%edi)"
+ : "=a"(v)
+ : "a"(0), "c"(cpu), "D"(&Lock->Lock)
+ );
+ #else
__ASM__("xchgl %%eax, (%%edi)":"=a"(v):"a"(1),"D"(&Lock->Lock));
+ #endif
+ }
Lock->IF = IF;
}
* \param Lock Lock pointer
*/
static inline void SHORTREL(struct sShortSpinlock *Lock) {
+ #if STACKED_LOCKS
+ if( Lock->Depth ) {
+ Lock->Depth --;
+ return ;
+ }
+ #endif
// Lock->IF can change anytime once Lock->Lock is zeroed
if(Lock->IF) {
Lock->Lock = 0;
void Threads_Sleep(void);
int Threads_Wake(tThread *Thread);
void Threads_AddActive(tThread *Thread);
+void Threads_int_AddActive(tThread *Thread);
tThread *Threads_RemActive(void);
int Threads_GetPID(void);
int Threads_GetTID(void);
// --- Locks ---
tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked
// --- Current State ---
-volatile int giNumActiveThreads = 0;
-volatile int giFreeTickets = 0;
-volatile Uint giNextTID = 1;
+volatile int giNumActiveThreads = 0; // Number of threads on the active queue
+volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads
+volatile Uint giNextTID = 1; // Next TID to allocate
// --- Thread Lists ---
tThread *gAllThreads = NULL; // All allocated threads
tThread *gActiveThreads = NULL; // Currently Running Threads
tThread *gSleepingThreads = NULL; // Sleeping Threads
tThread *gDeleteThreads = NULL; // Threads to delete
- int giNumCPUs = 1;
+ int giNumCPUs = 1; // Number of CPUs
// === CODE ===
/**
tThread *cur = Proc_GetCurThread();
char *oldname = cur->ThreadName;
+ // NOTE: There is a possibility of non-thread safety here
+ // A thread could read the current name pointer before it is zeroed
+
cur->ThreadName = NULL;
if( IsHeap(oldname) ) free( oldname );
if(ID == -1) {
return Proc_GetCurThread()->ThreadName;
}
- // TODO: Find a thread and get its name
- return NULL;
+ return Threads_GetThread(ID)->ThreadName;
}
/**
int i;
cur = Proc_GetCurThread();
+ // Allocate and duplicate
new = malloc(sizeof(tThread));
if(new == NULL) {
*Err = -ENOMEM;
new->CurCPU = -1;
new->Next = NULL;
memset( &new->IsLocked, 0, sizeof(new->IsLocked));
- new->Status = THREAD_STAT_ACTIVE;
+ new->Status = THREAD_STAT_PREINIT;
new->RetStatus = 0;
// Get Thread ID
}
/**
- * \fn Uint *Threads_GetCfgPtr(int Id)
* \brief Get a configuration pointer from the Per-Thread data area
* \param ID Config slot ID
* \return Pointer at ID
}
/**
- * \fn tTID Threads_WaitTID(int TID, int *Status)
* \brief Wait for a task to change state
* \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID)
* \param Status Thread return status
int initStatus = t->Status;
tTID ret;
+ // Wait for the thread to die!
if(initStatus != THREAD_STAT_ZOMBIE) {
// TODO: Handle child also being suspended if wanted
while(t->Status != THREAD_STAT_ZOMBIE) {
}
}
+ // Set return status
Log_Debug("Threads", "%i waiting for %i, t->Status = %i",
Threads_GetTID(), t->TID, t->Status);
ret = t->TID;
}
/**
- * \fn tThread *Threads_GetThread(Uint TID)
* \brief Gets a thread given its TID
* \param TID Thread ID
* \return Thread pointer
}
/**
- * \fn void Threads_AddToDelete(tThread *Thread)
* \brief Adds a thread to the delete queue
* \param Thread Thread to delete
*/
void Threads_AddToDelete(tThread *Thread)
{
// Add to delete queue
+ // TODO: Is locking needed?
if(gDeleteThreads) {
Thread->Next = gDeleteThreads;
gDeleteThreads = Thread;
}
/**
- * \fn tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
* \brief Gets the previous entry in a thead linked list
* \param List Pointer to the list head
* \param Thread Thread to find
* \return Thread before \a Thread on \a List
+ * \note This uses a massive hack of assuming that the first field in the
+ * structure is the .Next pointer. By doing this, we can return \a List
+ * as a (tThread*) and simplify other code.
*/
tThread *Threads_int_GetPrev(tThread **List, tThread *Thread)
{
}
/**
- * \fn void Threads_Exit(int TID, int Status)
* \brief Exit the current process (or another?)
* \param TID Thread ID to kill
* \param Status Exit status
Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF );
else
Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF );
+
// Halt forever, just in case
- for(;;)
- HALT();
+ for(;;) HALT();
}
/**
#if 0
{
tThread *child;
+ // TODO: I should keep a .Parent pointer, and a .Children list
for(child = gActiveThreads;
child;
child = child->Next)
while( Thread->Messages )
{
msg = Thread->Messages->Next;
- free( Thread->Messages );
+ free( Thread->Messages ); // BIG NO-NO
Thread->Messages = msg;
}
+ // Ensure that we are not rescheduled
Thread->Remaining = 0; // Clear Remaining Quantum
Thread->Quantum = 0; // Clear Quantum to indicate dead thread
prev->Next = Thread->Next; // Remove from active
+ // Update bookkeeping
giNumActiveThreads --;
if( Thread != Proc_GetCurThread() )
giFreeTickets -= Thread->NumTickets;
// Save exit status
Thread->RetStatus = Status;
- // Don't Zombie if we are being killed as part of a tree
+ // Don't Zombie if we are being killed because our parent is
if(Status == -1)
{
Thread->Status = THREAD_STAT_DEAD;
Log("Thread %i went *hurk* (%i)", Thread->TID, Thread->Status);
// Release spinlocks
- SHORTREL( &Thread->IsLocked );
SHORTREL( &glThreadListLock );
+ SHORTREL( &Thread->IsLocked ); // TODO: We may not actually be released...
- if(Status != -1) HALT();
+ // And, reschedule
+ if(Status != -1) {
+ for( ;; )
+ HALT();
+ }
}
/**
- * \fn void Threads_Yield(void)
* \brief Yield remainder of the current thread's timeslice
*/
void Threads_Yield(void)
{
- Proc_GetCurThread()->Remaining = 0;
- HALT();
+ tThread *thread = Proc_GetCurThread();
+ thread->Remaining = 0;
+ //while(thread->Remaining == 0)
+ HALT();
}
/**
// Remove us from running queue
Threads_RemActive();
+ // Mark thread as sleeping
+ cur->Status = THREAD_STAT_SLEEPING;
// Add to Sleeping List (at the top)
cur->Next = gSleepingThreads;
gSleepingThreads = cur;
- // Mark thread as sleeping
- cur->Status = THREAD_STAT_SLEEPING;
#if DEBUG_TRACE_STATE
Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName);
prev = Threads_int_GetPrev(&gSleepingThreads, Thread);
prev->Next = Thread->Next;
- // Add to active queue
- Thread->Next = gActiveThreads;
- gActiveThreads = Thread;
+ Threads_int_AddActive( Thread );
- // Update bookkeeping
- giNumActiveThreads ++;
- giFreeTickets += Thread->NumTickets;
- #if DEBUG_TRACE_TICKETS
- Log("Threads_Wake: new giFreeTickets = %i", giFreeTickets);
- #endif
-
- Thread->CurCPU = -1;
- Thread->Status = THREAD_STAT_ACTIVE;
#if DEBUG_TRACE_STATE
Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName);
#endif
}
/**
- * \fn void Threads_AddActive(tThread *Thread)
* \brief Adds a thread to the active queue
*/
void Threads_AddActive(tThread *Thread)
{
SHORTLOCK( &glThreadListLock );
+ Threads_int_AddActive(Thread);
+ SHORTREL( &glThreadListLock );
+}
+
+/**
+ * \brief Adds a thread to the active queue
+ * \note This version MUST have the thread list lock held
+ */
+void Threads_int_AddActive(tThread *Thread)
+{
// Add to active list
Thread->Next = gActiveThreads;
gActiveThreads = Thread;
+ // Set state
+ Thread->Status = THREAD_STAT_ACTIVE;
+ Thread->CurCPU = -1;
+
// Update bookkeeping
giNumActiveThreads ++;
giFreeTickets += Thread->NumTickets;
+
#if DEBUG_TRACE_TICKETS
- Log("Threads_AddActive: new giFreeTickets = %i", giFreeTickets);
+ Log("Threads_int_AddActive: new giFreeTickets = %i", giFreeTickets);
#endif
- SHORTREL( &glThreadListLock );
}
/**
}
/**
- * \fn tThread *Threads_GetNextToRun(int CPU, tThread *Last)
* \brief Gets the next thread to run
* \param CPU Current CPU
* \param Last The thread the CPU was running
Mutex->Owner = Mutex->Waiting; // Set owner
Mutex->Waiting = Mutex->Waiting->Next; // Next!
// Wake new owner
- Mutex->Owner->Status = THREAD_STAT_ACTIVE;
Threads_AddActive(Mutex->Owner);
//Log("Mutex %p Woke %p", Mutex, Mutex->Owner);
}