Custom rwlock data structure

This commit is contained in:
2024-04-13 22:49:49 +05:30
parent 861976fc69
commit 3e79c064a6
5 changed files with 421 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
# CMakeList.txt : CMake project for Custom_read_write_lock, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
project ("Custom_read_write_lock")
# Add source to this project's executable.
add_executable (Custom_read_write_lock "Custom_read_write_lock.c" "Custom_read_write_lock.h")
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET Custom_read_write_lock PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.

View File

@@ -0,0 +1,101 @@
{
"version": 3,
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "x64-debug",
"displayName": "x64 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x64-release",
"displayName": "x64 Release",
"inherits": "x64-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "x86-debug",
"displayName": "x86 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x86-release",
"displayName": "x86 Release",
"inherits": "x86-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "linux-debug",
"displayName": "Linux Debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"vendor": {
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"sourceDir": "$env{HOME}/.vs/$ms{projectDirName}"
}
}
},
{
"name": "macos-debug",
"displayName": "macOS Debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"vendor": {
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"sourceDir": "$env{HOME}/.vs/$ms{projectDirName}"
}
}
}
]
}

View File

@@ -0,0 +1,243 @@
#include "Custom_read_write_lock.h"
void
rw_lock_init(rwlock_t* rw_lock) {
pthread_mutex_init(&rw_lock->stateMutex, NULL);
pthread_cond_init(&rw_lock->cv, NULL);
rw_lock->n_reader_waiting = 0;
rw_lock->n_writer_waiting = 0;
rw_lock->is_locked_by_reader = false;
rw_lock->is_locked_by_writer = false;
rw_lock->writer_thread = 0;
}
void
rw_lock_rd_lock(rwlock_t* rw_lock) {
pthread_mutex_lock(&rw_lock->stateMutex);
/* Case 1 : rw_lock is Unlocked */
if (rw_lock->is_locked_by_reader == false &&
rw_lock->is_locked_by_writer == false) {
assert(rw_lock->n_locks == 0);
assert(!rw_lock->is_locked_by_reader);
assert(!rw_lock->is_locked_by_writer);
assert(!rw_lock->writer_thread);
rw_lock->n_locks++;
rw_lock->is_locked_by_reader = true;
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
/* Case 2 : rw_lock is locked by reader(s) thread already ( could be same as this thread (recursive))*/
if (rw_lock->is_locked_by_reader) {
assert(rw_lock->is_locked_by_writer == false);
assert(rw_lock->n_locks);
assert(!rw_lock->writer_thread);
rw_lock->n_locks++;
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
/* Case 3 : rw_lock is locked by a writer thread */
while (rw_lock->is_locked_by_writer) {
assert(rw_lock->n_locks);
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->writer_thread);
rw_lock->n_reader_waiting++;
pthread_cond_wait(&rw_lock->cv, &rw_lock->stateMutex);
rw_lock->n_reader_waiting--;
}
if (rw_lock->n_locks == 0) {
/* First reader enter C.S */
rw_lock->is_locked_by_reader = true;
}
rw_lock->n_locks++;
assert(rw_lock->is_locked_by_writer == false);
assert(!rw_lock->writer_thread);
pthread_mutex_unlock(&rw_lock->stateMutex);
}
void
rw_lock_wr_lock(rwlock_t* rw_lock) {
pthread_mutex_lock(&rw_lock->stateMutex);
/* Case 1: rw_lock is Unlocked and free */
if (rw_lock->is_locked_by_reader == false &&
rw_lock->is_locked_by_writer == false) {
assert(rw_lock->n_locks == 0);
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->is_locked_by_writer == false);
assert(rw_lock->writer_thread);
rw_lock->n_locks++;
rw_lock->is_locked_by_writer = true;
rw_lock->writer_thread = pthread_self();
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
/* Case 2 : if same thread tries to obtain write lock again,
Implement recursive property.*/
if (rw_lock->is_locked_by_reader &&
rw_lock->writer_thread == pthread_self()) {
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->n_locks);
rw_lock->n_locks++;
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
/* Case 3 : If some other thread tries to obtain a lock on already
locked mutex*/
while (rw_lock->is_locked_by_reader ||
rw_lock->is_locked_by_writer) {
/* Sanity Check */
if (rw_lock->is_locked_by_reader) {
assert(rw_lock->is_locked_by_writer == false);
assert(rw_lock->writer_thread == 0);
assert(rw_lock->n_locks);
}
else {
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->writer_thread);
assert(rw_lock->n_locks);
}
rw_lock->n_writer_waiting++;
pthread_cond_wait(&rw_lock->cv, &rw_lock->stateMutex);
rw_lock->n_writer_waiting--;
}
/* Lock is Available now */
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->is_locked_by_writer == false);
assert(rw_lock->n_locks == 0);
assert(!rw_lock->writer_thread);
rw_lock->is_locked_by_writer = true;
rw_lock->n_locks++;
rw_lock->writer_thread = pthread_self();
pthread_mutex_unlock(&rw_lock->stateMutex);
}
void
rw_lock_unlock(rwlock_t* rw_lock) {
pthread_mutex_lock(&rw_lock->stateMutex);
/* Case 1 : Attempt to unlock the unlocked lock */
assert(rw_lock->n_locks);
/* Case 2 : Writer thread unlocking the rw_lock */
if (rw_lock->is_locked_by_writer) {
/* Only the owner of the rw_lock must attempt to unlock the rw_lock */
assert(rw_lock->writer_thread == pthread_self());
assert(rw_lock->is_locked_by_reader == false);
rw_lock->n_locks--;
if (rw_lock->n_locks) {
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
if (rw_lock->n_reader_waiting ||
rw_lock->n_writer_waiting) {
pthread_cond_broadcast(&rw_lock->cv);
}
rw_lock->is_locked_by_writer = false;
rw_lock->writer_thread = 0;
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
/* Case 3 : Reader Thread trying to unlock the rw_lock */
/* Note : Case 3 cannot be moved before Case 2. I leave it to you
for reasoning.. */
/* Ans to above ^ : Only one reader thread is being unlocked by case 3
however, it is broadcasting the message to both reader or writer thread
and there may be some reader thread that still may be owing the rwlock,
writer thread may not be aware of it and cause problem.
So, Case 2 ensures that the rwlock is not being hold by any writer thread
as you can check it out with the condition of case.*/
/* There is minor design flaw in our implementation. Not actually flaw, but
a room to mis-use the rw_locks which is a trade off for simplicity. Here Simplicity is
that rw_lock implementation do not keep track of "who" all reader threads have
obtain the read lock on rw_lock. Here is an example, how rw_lock implementation
could be erroneously used by the developer, yet rw_lock implementation would not
report a bug ( assert () ).
Suppose, threads T1, T2 and T3 owns read locks on rw_lock. and Now, some
Thread T4 which do not own any type of lock on rw_lock invoke rw_lock_unlock( )
API. The API would still honor the unlock request, since our rw_lock do not keep
track of who all "reader" threads owns the rw_lock and treat T4 as some reader thread
trying to unlock the rw_lock*/
else if (rw_lock->is_locked_by_reader) {
assert(rw_lock->is_locked_by_writer == false);
assert(rw_lock->writer_thread == 0);
rw_lock->n_locks--;
if (rw_lock->n_locks) {
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
if (rw_lock->n_reader_waiting ||
rw_lock->n_writer_waiting) {
pthread_cond_broadcast(&rw_lock->cv);
}
rw_lock->is_locked_by_reader = false;
pthread_mutex_unlock(&rw_lock->stateMutex);
return;
}
assert(0);
}
void
rw_lock_destroy(rwlock_t* rw_lock) {
assert(rw_lock->n_locks == 0);
assert(rw_lock->n_reader_waiting == 0);
assert(rw_lock->n_writer_waiting == 0);
assert(rw_lock->is_locked_by_reader == false);
assert(rw_lock->is_locked_by_writer == false);
assert(!rw_lock->writer_thread);
pthread_mutex_destroy(&rw_lock->stateMutex);
pthread_cond_destroy(&rw_lock->cv);
}

View File

@@ -0,0 +1,55 @@
#ifndef __RW_LOCK_H
#define __RW_LOCK_H
#include <pthread.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct rwlock_{
/* A Mutex to manipulate/inspect the state of rwlock
in a mutually exclusive manner */
pthread_mutex_t stateMutex;
/* CV to block the threads if rwlock is not available*/
pthread_cond_t cv;
/* count of number of concurrent thread existing inside C.S*/
uint16_t n_locks;
/* No of reader thread waiting for the lock grant*/
uint16_t n_reader_waiting;
/* No of writer thread waiting for the lock grant*/
uint16_t n_writer_waiting;
/* Is locked currently occupied by Reader threads */
bool is_locked_by_reader;
/* Is locked currently occupied by Writer threads*/
bool is_locked_by_writer;
/* Thread handle of the writer thread currently holding the
lock. It is 0 if lock is not being held by any writer thread*/
pthread_t writer_thread;
}rwlock_t;
void
rw_lock_init(rwlock_t* rw_lock);
void
rw_lock_rd_lock(rwlock_t* rw_lock);
void
rw_lock_wr_lock(rwlock_t* rw_lock);
void
rw_lock_unlock(rwlock_t* rw_lock);
void
rw_lock_destroy(rwlock_t* rw_lock);
#endif

View File

@@ -9,7 +9,7 @@ static int n_w = 0;
pthread_mutex_t state_check_mutex; pthread_mutex_t state_check_mutex;
static void static void
cs_static_check() { cs_status_check() {
pthread_mutex_lock(&state_check_mutex); pthread_mutex_lock(&state_check_mutex);
assert(n_r >= 0 && n_w >= 0); /* cannot be negative */ assert(n_r >= 0 && n_w >= 0); /* cannot be negative */