Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / geekos / synch.c
diff --git a/geekos/src/geekos/synch.c b/geekos/src/geekos/synch.c
new file mode 100644 (file)
index 0000000..5c5a75b
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Synchronization primitives
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kthread.h>
+#include <geekos/int.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/synch.h>
+#include <geekos/timer.h>
+
+#include <geekos/debug.h>
+
+/*
+ * 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 */
+}