From be490e1dd003780a02493a51ebbab1a5225c30d1 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 14 Jul 2012 13:06:10 +0800 Subject: [PATCH] Kernel - Added a Read-Write lock implimentation (untested) --- KernelLand/Kernel/include/rwlock.h | 49 +++++++++++ KernelLand/Kernel/rwlock.c | 132 +++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 KernelLand/Kernel/include/rwlock.h create mode 100644 KernelLand/Kernel/rwlock.c diff --git a/KernelLand/Kernel/include/rwlock.h b/KernelLand/Kernel/include/rwlock.h new file mode 100644 index 00000000..a6f46b58 --- /dev/null +++ b/KernelLand/Kernel/include/rwlock.h @@ -0,0 +1,49 @@ +/* + * Acess2 Kernel + * + * rwmutex.c + * - Reader-Writer Mutex (Multiple Readers, One Writer) + */ +#ifndef _RWLOCK_H +#define _RWLOCK_H + +#include + +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 diff --git a/KernelLand/Kernel/rwlock.c b/KernelLand/Kernel/rwlock.c new file mode 100644 index 00000000..6706bb22 --- /dev/null +++ b/KernelLand/Kernel/rwlock.c @@ -0,0 +1,132 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * rwlock.c + * - Reader-Writer Lockes + */ +#include +#include +#include + +// === 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); -- 2.20.1