Faster TMultiReadExclusiveWriteSynchronizer? Faster TMultiReadExclusiveWriteSynchronizer? multithreading multithreading

Faster TMultiReadExclusiveWriteSynchronizer?


TOmniMREW from OmniThreadLibrary claims to be faster and more lightweight:

OTL is an excellent threading lib, BTW.

Sample Code

TOmniReaderWriterLock = class(TInterfacedObject, IReaderWriterLock)private   omrewReference: Integer;public   { IReaderWriterLock }   procedure BeginRead;   procedure EndRead;   procedure BeginWrite;   procedure EndWrite;end;{ TOmniReaderWriterLock }procedure TOmniReaderWriterLock.BeginRead;var  currentReference: Integer;begin    //Wait on writer to reset write flag so Reference.Bit0 must be 0 than increase Reference    repeat        currentReference := Integer(omrewReference) and not 1;    until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(Integer(currentReference) + 2), Pointer(currentReference)));end;procedure TOmniReaderWriterLock.EndRead;begin    //Decrease omrewReference    InterlockedExchangeAdd(@omrewReference, -2);end;procedure TOmniReaderWriterLock.BeginWrite;var    currentReference: integer;begin    //Wait on writer to reset write flag so omrewReference.Bit0 must be 0 then set omrewReference.Bit0    repeat        currentReference := omrewReference and (not 1);    until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(currentReference+1), Pointer(currentReference)));    //Now wait on all readers    repeat    until omrewReference = 1;end;procedure TOmniReaderWriterLock.EndWrite;begin    omrewReference := 0;end;


In the end i used a compromise solution. The Omni reader-writer lock uses "slim" principles (spinning bit-manipulations). Like Window's own, it doesn't support lock escalation. I've tested it, and it doesn't seem to lockup crash or deadlock.

In the end i used a fallback situation. The most generic of generic interfaces to support "read-write" concepts:

IReaderWriterLock = interface   ['{6C4150D0-7B13-446D-9D8E-866B66723320}']   procedure BeginRead;   procedure EndRead;   procedure BeginWrite;   procedure EndWrite;end;

And then we decide at runtime which implementation to use. If we're on Windows Vista or new, use Window's own SlimReaderWriter, otherwise fallback to Omni version:

TReaderWriterLock = class(TObject)public   class function Create: IReaderWriterLock;end;class function TReaderWriterLock.Create: IReaderWriterLock;begin   if Win32MajorVersion >= 6 then //SRWLocks were introduced with Vista/Server 2008 (Windows version 6.0)   begin      //Use the Windows built-in Slim ReaderWriter lock      Result := TSlimReaderWriterLock.Create;   end   else   begin      //XP and earlier fallback to Omni equivalent      Result := TOmniReaderWriterLock.Create;   end;end;

Note: Any code is released into the public domain. No attribution required.


The Delphi TMultiReadExclusiveWriteSynchronizer is very sophisticated - it can be acquired recursively and you can update from Read to Write.

This comes with a cost, which in this case means managing a bucket of shared state per thread. As the Windows thread-local mechanics (accessible via threadvar) is too simplistic for this (not able cope with several MREWS instances) it is done in a rather inefficient way – see the RTL or JCL sources – the implementations are quite similar, sharing bad performance and update-deadlock risk.

First ensure you really need the MREWS functionality – I assume, according to proportional size of locking overhead to the workload, you will be much better off with a TCriticalSection.

If you really-really need it, go with the Delphi implementation and watch out for the possible hidden unlock in BeginWrite – see it's documentation and return value meaning.

It is possible to implement a Vista-like SRW using the Interlocked functions or inline assembly, but it's not worth the effort in most cases.