I am developing a C++ kernel, and I've got the need for manipulating huge data structures before the task-scheduler runs - it means in a non-preemptive environment.
For this, I have developed a read-write serializer struct whose job is to allow readers to concurrently access the data. It's worthy to note here that any writer will gain exclusive control over the data and constrain any read attempts until all pending writes are complete. That will allow software to get the complete data and eliminate race conditions - in cases where the write operation didn't complete, due to locking before the reader came in.
I want to know how efficient this implementation is and if any improvements can be made. Thank You in advance.
NOTE: Spinlock
is a primitive lock that is used by SpinLock
and SpinUnlock
functions. It is used in the code.
struct ReadWriteSerializer
{
unsigned long onlineReaders;
Spinlock criticalSection;
void enterAsReader()
{
SpinLock(&criticalSection);
++(onlineReaders);
SpinUnlock(&criticalSection);
}
void enterAsWriter()
{
SpinLock(&criticalSection);
while(onlineReaders)
asm volatile("nop");
}
void exitAsReader()
{
--(onlineReaders);
__mfence // macro declared otherwhere - asm volatile("mfence");
}
void exitAsWriter()
{
SpinUnlock(&criticalSection);
}
};
Explanation of how this lock works:
When software attempts to read/write, it will access ReadWriteSerializer::enterAsReader
or ReadWriteSerializer::enterAsWriter
. On entering as a reader, the code will immediately increment the total number of concurrent readers (onlineReaders
) and then unlock the serializer. That means other read/write software can come and access the serializer.
But when a writer enters, it says nop :-), no more readers entering until I finish my work. It waits until all other readers exit the lock (by decrementing onlineReaders
). Then it does its work. On exit, it will free the lock for others to come.
Spinlock
implementation for review also. \$\endgroup\$