X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=geekos%2Fsrc%2Fgeekos%2Fsynch.c;fp=geekos%2Fsrc%2Fgeekos%2Fsynch.c;h=5c5a75b17ac22b3bda37b5226eba5122c6a0bb6a;hp=0000000000000000000000000000000000000000;hb=ddc16b0737cf58f7aa90a69c6652cdf4090aec51;hpb=626595465a2c6987606a6bc697df65130ad8c2d3 diff --git a/geekos/src/geekos/synch.c b/geekos/src/geekos/synch.c new file mode 100644 index 0000000..5c5a75b --- /dev/null +++ b/geekos/src/geekos/synch.c @@ -0,0 +1,303 @@ +/* + * 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 +#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; +} + +/* + * Destroy Mutex + */ +void Mutex_Destroy(struct Mutex* mutex) +{ + + +} + +/* + * Condition Destroy + */ +void Cond_Destroy(struct Condition* cond) +{ + + +} + +/* + * 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; +} + + + +struct timeout_data { + int pid; + int timed_out; + struct Thread_Queue * waitQueue; +}; + + +static void timeout_cb(int id, void * arg) { + struct timeout_data * to_state = (struct timeout_data *)arg; + + to_state->timed_out = 1; + Wake_Up_Thread(to_state->waitQueue, to_state->pid); + +} + + +/* + * Wait on given condition (protected by given mutex). + */ +int Cond_Wait_Timeout(struct Condition* cond, struct Mutex* mutex, uint_t ms) +{ + struct timeout_data to_state; + struct Kernel_Thread * self = Get_Current(); + + to_state.pid = self->pid; + to_state.timed_out = 0; + to_state.waitQueue = &cond->waitQueue; + + 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); + + + Start_Timer_MSecs(ms, timeout_cb, &to_state); + + /* + * 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(); + + if (to_state.timed_out == 0) { + /* Reacquire the mutex. */ + Mutex_Lock_Imp(mutex); + } + + /* Turn scheduling back on. */ + g_preemptionDisabled = false; + + + return to_state.timed_out; +} + +/* + * 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 */ +}