/* * 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 */ }