--- /dev/null
+/*
+ * Acess2 Kernel
+ *
+ * rwmutex.c
+ * - Reader-Writer Mutex (Multiple Readers, One Writer)
+ */
+#ifndef _RWLOCK_H
+#define _RWLOCK_H
+
+#include <acess.h>
+
+typedef struct sRWLock tRWLock;
+
+struct sRWLock
+{
+ tShortSpinlock Protector; //!< Protector for the lock strucure
+ const char *Name; //!< Human-readable name
+ int Level; // Number of readers active
+ struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock)
+ struct sThread *ReaderWaiting; //!< Waiting threads (readers)
+ struct sThread *ReaderWaitingLast; //!< Waiting threads
+
+ struct sThread *WriterWaiting; //!< Waiting threads (writers)
+ struct sThread *WriterWaitingLast; //!< Waiting threads
+};
+
+/**
+ * \brief Acquire a heavy mutex
+ * \param Mutex Mutex to acquire
+ * \return zero on success, -1 if terminated
+ *
+ * This type of mutex checks if the mutex is avaliable, and acquires it
+ * if it is. Otherwise, the current thread is added to the mutex's wait
+ * queue and the thread suspends. When the holder of the mutex completes,
+ * the oldest thread (top thread) on the queue is given the lock and
+ * restarted.
+ */
+extern int RWLock_AcquireRead(tRWLock *Lock);
+
+extern int RWLock_AcquireWrite(tRWLock *LOck);
+
+/**
+ * \brief Release a held mutex
+ * \param Mutex Mutex to release
+ * \note Releasing a non-held mutex has no effect
+ */
+extern void RWLock_Release(tRWLock *Lock);
+
+#endif
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * rwlock.c
+ * - Reader-Writer Lockes
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <rwlock.h>
+
+// === PROTOTYPES ===
+//
+// Acquire as a reader (see rwlock.h for documentation)
+//
+int RWLock_AcquireRead(tRWLock *Lock)
+{
+ tThread *us;
+ // Get protector
+ SHORTLOCK( &Lock->Protector );
+
+ // Check if the lock is already held by a writer
+ if( Lock->Owner )
+ {
+ SHORTLOCK( &glThreadListLock );
+
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_RWLOCKSLEEP;
+ us->WaitPointer = Lock;
+
+ // - Add to waiting
+ if(Lock->ReaderWaiting)
+ Lock->ReaderWaitingLast->Next = us;
+ else
+ Lock->ReaderWaiting = us;
+ Lock->ReaderWaitingLast = us;
+
+ #if DEBUG_TRACE_STATE
+ Log("%p (%i %s) waiting on rwlock %p",
+ us, us->TID, us->ThreadName, Lock);
+ #endif
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Lock->Protector );
+ while(us->Status == THREAD_STAT_RWLOCKSLEEP) Threads_Yield();
+ // We're only woken when we get the lock
+ // TODO: Handle when this isn't the case
+ us->WaitPointer = NULL;
+ }
+ // Ooh, no problems then!
+ else
+ {
+ Lock->Level++;
+ SHORTREL( & Lock->Protector );
+ }
+
+ return 0;
+}
+
+int RWLock_AcquireWrite(tRWLock *Lock)
+{
+ tThread *us;
+
+ SHORTLOCK(&Lock->Protector);
+ if( Lock->Owner || Lock->Level != 0 )
+ {
+ SHORTLOCK(&glThreadListLock);
+
+ us = Threads_RemActive();
+ us->Next = NULL;
+ us->Status = THREAD_STAT_RWLOCKSLEEP;
+ us->WaitPointer = Lock;
+
+ if( Lock->WriterWaiting )
+ Lock->WriterWaitingLast->Next = us;
+ else
+ Lock->WriterWaiting = us;
+ Lock->WriterWaitingLast = us;
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Lock->Protector );
+
+ while(us->Status == THREAD_STAT_RWLOCKSLEEP) Threads_Yield();
+ us->WaitPointer = NULL;
+ }
+ else
+ {
+ // Nothing else is using the lock, nice :)
+ Lock->Owner = Proc_GetCurThread();
+ SHORTREL(&Lock->Protector);
+ }
+ return 0;
+}
+
+// Release a mutex
+void RWLock_Release(tRWLock *Lock)
+{
+ SHORTLOCK( &Lock->Protector );
+
+ if( Lock->Owner == Proc_GetCurThread() )
+ Lock->Level --;
+
+ // Writers first
+ if( Lock->WriterWaiting )
+ {
+ Lock->Owner = Lock->WriterWaiting; // Set owner
+ Lock->WriterWaiting = Lock->WriterWaiting->Next; // Next!
+
+ // Wake new owner
+ if( Lock->Owner->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(Lock->Owner);
+ }
+ else
+ {
+ Lock->Owner = NULL;
+
+ while( Lock->ReaderWaiting ) {
+ Lock->Level ++;
+ Threads_AddActive(Lock->ReaderWaiting);
+ Lock->ReaderWaiting = Lock->ReaderWaiting->Next;
+ }
+ }
+ SHORTREL( &Lock->Protector );
+}
+
+// === EXPORTS ===
+EXPORT(RWLock_AcquireRead);
+EXPORT(RWLock_AcquireWrite);
+EXPORT(RWLock_Release);