diff --git a/sys/SConscript b/sys/SConscript index ac2552a..4d82a61 100644 --- a/sys/SConscript +++ b/sys/SConscript @@ -41,6 +41,7 @@ src_amd64 = [ src_common = [ "kern/copy.c", "kern/bufcache.c", + "kern/cv.c", "kern/debug.c", "kern/disk.c", "kern/handle.c", @@ -48,6 +49,7 @@ src_common = [ "kern/ktimer.c", "kern/libc.c", "kern/loader.c", + "kern/mutex.c", "kern/nic.c", "kern/palloc.c", "kern/printf.c", diff --git a/sys/include/cv.h b/sys/include/cv.h new file mode 100644 index 0000000..1456b7e --- /dev/null +++ b/sys/include/cv.h @@ -0,0 +1,16 @@ + +#ifndef __CV_H__ +#define __CV_H__ + +typedef struct CV { + WaitChannel chan; +} CV; + +void CV_Init(CV *cv, const char *name); +void CV_Destroy(CV *cv); +void CV_Wait(CV *cv, Mutex *mtx); +void CV_Signal(CV *cv); +void CV_Broadcast(CV *cv); + +#endif /* __CV_H__ */ + diff --git a/sys/include/mutex.h b/sys/include/mutex.h new file mode 100644 index 0000000..13136f5 --- /dev/null +++ b/sys/include/mutex.h @@ -0,0 +1,23 @@ + +#ifndef __MUTEX_H__ +#define __MUTEX_H__ + +#define MTX_STATUS_UNLOCKED 0 +#define MTX_STATUS_LOCKED 1 + +typedef struct Mutex { + uint64_t status; + Thread *owner; + Spinlock lock; + WaitChannel chan; + LIST_ENTRY(Mutex) buckets; +} Mutex; + +void Mutex_Init(Mutex *mtx, const char *name); +void Mutex_Destroy(Mutex *mtx); +void Mutex_Lock(Mutex *mtx); +int Mutex_TryLock(Mutex *mtx); +void Mutex_Unlock(Mutex *mtx); + +#endif /* __MUTEX_H__ */ + diff --git a/sys/include/thread.h b/sys/include/thread.h index f4037e0..d5b2d36 100644 --- a/sys/include/thread.h +++ b/sys/include/thread.h @@ -13,6 +13,8 @@ struct Process; typedef struct Process Process; #include +#include +#include #include #include @@ -72,7 +74,8 @@ typedef struct Process { TAILQ_ENTRY(Process) siblingList; ProcessQueue childrenList; ProcessQueue zombieProc; - Semaphore zombieProcSemaphore; + Mutex zombieProcLock; + CV zombieProcCV; // Threads uint64_t threads; ThreadQueue threadList; diff --git a/sys/include/waitchannel.h b/sys/include/waitchannel.h index 23d5f48..7866a31 100644 --- a/sys/include/waitchannel.h +++ b/sys/include/waitchannel.h @@ -16,12 +16,12 @@ typedef struct WaitChannel } WaitChannel; void WaitChannel_EarlyInit(); -void WaitChannel_Init(WaitChannel *lock, const char *name); -void WaitChannel_Destroy(WaitChannel *lock); -void WaitChannel_Lock(WaitChannel *lock) __LOCK_EX(lock->lock); -void WaitChannel_Sleep(WaitChannel *lock) __UNLOCK_EX(lock->lock); -void WaitChannel_Wake(WaitChannel *lock); -void WaitChannel_WakeAll(WaitChannel *lock); +void WaitChannel_Init(WaitChannel *wc, const char *name); +void WaitChannel_Destroy(WaitChannel *wc); +void WaitChannel_Lock(WaitChannel *wc) __LOCK_EX(wc->lock); +void WaitChannel_Sleep(WaitChannel *wc) __UNLOCK_EX(wc->lock); +void WaitChannel_Wake(WaitChannel *wc); +void WaitChannel_WakeAll(WaitChannel *wc); #endif /* __WAITCHANNEL_H__ */ diff --git a/sys/kern/cv.c b/sys/kern/cv.c new file mode 100644 index 0000000..293a4fc --- /dev/null +++ b/sys/kern/cv.c @@ -0,0 +1,77 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +CV_Init(CV *cv, const char *name) +{ + WaitChannel_Init(&cv->chan, name); + + return; +} + +void +CV_Destroy(CV *cv) +{ + WaitChannel_Destroy(&cv->chan); + + return; +} + +/* + * CV_Wait -- + * + * Wait to be woken up on a condition. + */ +void +CV_Wait(CV *cv, Mutex *mtx) +{ + WaitChannel_Lock(&cv->chan); + Mutex_Unlock(mtx); + WaitChannel_Sleep(&cv->chan); + Mutex_Lock(mtx); + + return; +} + +/* + * CV_Signal -- + * + * Wake a single thread waiting on the condition. + */ +void +CV_Signal(CV *cv) +{ + WaitChannel_Wake(&cv->chan); + + return; +} + +/* + * CV_WakeAll -- + * + * Wake all threads waiting on the condition. + */ +void +CV_WakeAll(CV *cv) +{ + WaitChannel_WakeAll(&cv->chan); + + return; +} + diff --git a/sys/kern/mutex.c b/sys/kern/mutex.c new file mode 100644 index 0000000..84c8c94 --- /dev/null +++ b/sys/kern/mutex.c @@ -0,0 +1,102 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * For debugging so we can assert the owner without holding a reference to the + * thread. You can access the current thread through curProc[CPU()]. + */ +extern Thread *curProc[MAX_CPUS]; + +void +Mutex_Init(Mutex *mtx, const char *name) +{ + Spinlock_Init(&mtx->lock, name, SPINLOCK_TYPE_NORMAL); + WaitChannel_Init(&mtx->chan, name); + + return; +} + +void +Mutex_Destroy(Mutex *mtx) +{ + WaitChannel_Destroy(&mtx->chan); + Spinlock_Destroy(&mtx->lock); + return; +} + +/* + * Mutex_Lock -- + * + * Acquires the mutex. + */ +void +Mutex_Lock(Mutex *mtx) +{ + Spinlock_Lock(&mtx->lock); + while (mtx->status == MTX_STATUS_LOCKED) { + WaitChannel_Lock(&mtx->chan); + Spinlock_Unlock(&mtx->lock); + WaitChannel_Sleep(&mtx->chan); + Spinlock_Lock(&mtx->lock); + } + mtx->status = MTX_STATUS_LOCKED; + mtx->owner = curProc[CPU()]; + Spinlock_Unlock(&mtx->lock); + + return; +} + +/* + * Mutex_TryLock -- + * + * Attempts to acquire the user mutex. Returns EBUSY if the lock is + * already taken, otherwise 0 on success. + */ +int +Mutex_TryLock(Mutex *mtx) +{ + Spinlock_Lock(&mtx->lock); + if (mtx->status == MTX_STATUS_LOCKED) { + Spinlock_Unlock(&mtx->lock); + return EBUSY; + } + mtx->status = MTX_STATUS_LOCKED; + mtx->owner = curProc[CPU()]; + Spinlock_Unlock(&mtx->lock); + + return 0; +} + +/* + * Mutex_Unlock -- + * + * Releases the user mutex. + */ +void +Mutex_Unlock(Mutex *mtx) +{ + Spinlock_Lock(&mtx->lock); + ASSERT(mtx->owner == curProc[CPU()]); + + mtx->status = MTX_STATUS_UNLOCKED; + mtx->owner = NULL; + Spinlock_Unlock(&mtx->lock); + + return; +} + diff --git a/sys/kern/process.c b/sys/kern/process.c index d9ad90f..0ace2a2 100644 --- a/sys/kern/process.c +++ b/sys/kern/process.c @@ -75,7 +75,8 @@ Process_Create(Process *parent, const char *title) } TAILQ_INIT(&proc->childrenList); TAILQ_INIT(&proc->zombieProc); - Semaphore_Init(&proc->zombieProcSemaphore, 0, "Zombie Process Semaphore"); + Mutex_Init(&proc->zombieProcLock, "Zombie Process Lock"); + CV_Init(&proc->zombieProcCV, "Zombie Process CV"); Spinlock_Lock(&procLock); TAILQ_INSERT_TAIL(&processList, proc, processList); @@ -91,7 +92,8 @@ Process_Destroy(Process *proc) Spinlock_Destroy(&proc->lock); Semaphore_Destroy(&proc->zombieSemaphore); - Semaphore_Destroy(&proc->zombieProcSemaphore); + CV_Destroy(&proc->zombieProcCV); + Mutex_Destroy(&proc->zombieProcLock); PMap_DestroyAS(proc->space); // XXX: We need to promote zombie processes to our parent @@ -154,21 +156,17 @@ Process_Wait(Process *proc, uint64_t pid) // XXX: Need to verify pid exists! + Mutex_Lock(&proc->zombieProcLock); while (1) { - Semaphore_Acquire(&proc->zombieProcSemaphore); - // XXX: Forced exit check! - - Spinlock_Lock(&proc->lock); p = TAILQ_FIRST(&proc->zombieProc); - if (pid == 0 || p->pid == pid) { + if (p != NULL && (pid == 0 || p->pid == pid)) { TAILQ_REMOVE(&proc->zombieProc, p, siblingList); - Spinlock_Unlock(&proc->lock); break; } - Spinlock_Unlock(&proc->lock); - Semaphore_Release(&proc->zombieProcSemaphore); + CV_Wait(&proc->zombieProcCV, &proc->zombieProcLock); } + Mutex_Unlock(&proc->zombieProcLock); status = (p->pid << 16) | p->exitCode; diff --git a/sys/kern/sched.c b/sys/kern/sched.c index 7ba9b21..6218f7b 100644 --- a/sys/kern/sched.c +++ b/sys/kern/sched.c @@ -84,11 +84,13 @@ Sched_SetZombie(Thread *thr) if (proc->threads == 1) { // All processes have parents except 'init' and 'kernel' ASSERT(proc->parent != NULL); - Spinlock_Lock(&proc->parent->lock); + Mutex_Lock(&proc->parent->zombieProcLock); + Spinlock_Lock(&proc->parent->lock); // Guards child list TAILQ_REMOVE(&proc->parent->childrenList, proc, siblingList); TAILQ_INSERT_TAIL(&proc->parent->zombieProc, proc, siblingList); - Semaphore_Release(&proc->parent->zombieProcSemaphore); Spinlock_Unlock(&proc->parent->lock); + CV_Signal(&proc->parent->zombieProcCV); + Mutex_Unlock(&proc->parent->zombieProcLock); } Spinlock_Unlock(&schedLock);