X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=test%2Fgeekos_test_vm%2Fsrc%2Fgeekos%2Fsynch.c;fp=test%2Fgeekos_test_vm%2Fsrc%2Fgeekos%2Fsynch.c;h=86e7191efd075ba696adf097d7f4891cc183bb89;hp=0000000000000000000000000000000000000000;hb=a70930549d1b741704dd7af4e6bb0e89f6f8a519;hpb=afb634a80f946634454a5d067a92aa600227bd93 diff --git a/test/geekos_test_vm/src/geekos/synch.c b/test/geekos_test_vm/src/geekos/synch.c new file mode 100644 index 0000000..86e7191 --- /dev/null +++ b/test/geekos_test_vm/src/geekos/synch.c @@ -0,0 +1,206 @@ +/* + * Synchronization primitives + * Copyright (c) 2001,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include + +/* + * NOTES: + * - The GeekOS mutex and condition variable APIs are based on those + * in pthreads. + * - Unlike disabling interrupts, mutexes offer NO protection against + * concurrent execution of interrupt handlers. Mutexes and + * condition variables should only be used from kernel threads, + * with interrupts enabled. + */ + +/* ---------------------------------------------------------------------- + * Private functions + * ---------------------------------------------------------------------- */ + +/* + * The mutex is currently locked. + * Atomically reenable preemption and wait in the + * mutex's wait queue. + */ +static void Mutex_Wait(struct Mutex *mutex) +{ + KASSERT(mutex->state == MUTEX_LOCKED); + KASSERT(g_preemptionDisabled); + + Disable_Interrupts(); + g_preemptionDisabled = false; + Wait(&mutex->waitQueue); + g_preemptionDisabled = true; + Enable_Interrupts(); +} + +/* + * Lock given mutex. + * Preemption must be disabled. + */ +static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex) +{ + KASSERT(g_preemptionDisabled); + + /* Make sure we're not already holding the mutex */ + KASSERT(!IS_HELD(mutex)); + + /* Wait until the mutex is in an unlocked state */ + while (mutex->state == MUTEX_LOCKED) { + Mutex_Wait(mutex); + } + + /* Now it's ours! */ + mutex->state = MUTEX_LOCKED; + mutex->owner = g_currentThread; +} + +/* + * Unlock given mutex. + * Preemption must be disabled. + */ +static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex) +{ + KASSERT(g_preemptionDisabled); + + /* Make sure mutex was actually acquired by this thread. */ + KASSERT(IS_HELD(mutex)); + + /* Unlock the mutex. */ + mutex->state = MUTEX_UNLOCKED; + mutex->owner = 0; + + /* + * If there are threads waiting to acquire the mutex, + * wake one of them up. Note that it is legal to inspect + * the queue with interrupts enabled because preemption + * is disabled, and therefore we know that no thread can + * concurrently add itself to the queue. + */ + if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) { + Disable_Interrupts(); + Wake_Up_One(&mutex->waitQueue); + Enable_Interrupts(); + } +} + +/* ---------------------------------------------------------------------- + * Public functions + * ---------------------------------------------------------------------- */ + +/* + * Initialize given mutex. + */ +void Mutex_Init(struct Mutex* mutex) +{ + mutex->state = MUTEX_UNLOCKED; + mutex->owner = 0; + Clear_Thread_Queue(&mutex->waitQueue); +} + +/* + * Lock given mutex. + */ +void Mutex_Lock(struct Mutex* mutex) +{ + KASSERT(Interrupts_Enabled()); + + g_preemptionDisabled = true; + Mutex_Lock_Imp(mutex); + g_preemptionDisabled = false; +} + +/* + * Unlock given mutex. + */ +void Mutex_Unlock(struct Mutex* mutex) +{ + KASSERT(Interrupts_Enabled()); + + g_preemptionDisabled = true; + Mutex_Unlock_Imp(mutex); + g_preemptionDisabled = false; +} + +/* + * Initialize given condition. + */ +void Cond_Init(struct Condition* cond) +{ + Clear_Thread_Queue(&cond->waitQueue); +} + +/* + * Wait on given condition (protected by given mutex). + */ +void Cond_Wait(struct Condition* cond, struct Mutex* mutex) +{ + KASSERT(Interrupts_Enabled()); + + /* Ensure mutex is held. */ + KASSERT(IS_HELD(mutex)); + + /* Turn off scheduling. */ + g_preemptionDisabled = true; + + /* + * Release the mutex, but leave preemption disabled. + * No other threads will be able to run before this thread + * is able to wait. Therefore, this thread will not + * miss the eventual notification on the condition. + */ + Mutex_Unlock_Imp(mutex); + + /* + * Atomically reenable preemption and wait in the condition wait queue. + * Other threads can run while this thread is waiting, + * and eventually one of them will call Cond_Signal() or Cond_Broadcast() + * to wake up this thread. + * On wakeup, disable preemption again. + */ + Disable_Interrupts(); + g_preemptionDisabled = false; + Wait(&cond->waitQueue); + g_preemptionDisabled = true; + Enable_Interrupts(); + + /* Reacquire the mutex. */ + Mutex_Lock_Imp(mutex); + + /* Turn scheduling back on. */ + g_preemptionDisabled = false; +} + +/* + * Wake up one thread waiting on the given condition. + * The mutex guarding the condition should be held! + */ +void Cond_Signal(struct Condition* cond) +{ + KASSERT(Interrupts_Enabled()); + Disable_Interrupts(); /* prevent scheduling */ + Wake_Up_One(&cond->waitQueue); + Enable_Interrupts(); /* resume scheduling */ +} + +/* + * Wake up all threads waiting on the given condition. + * The mutex guarding the condition should be held! + */ +void Cond_Broadcast(struct Condition* cond) +{ + KASSERT(Interrupts_Enabled()); + Disable_Interrupts(); /* prevent scheduling */ + Wake_Up(&cond->waitQueue); + Enable_Interrupts(); /* resume scheduling */ +}