5050// / - Added methods Enter: T and Leave to Locked<T>.
5151// / - Added methods BeginRead, TryBeginRead, EndRead, BeginWrite,
5252// / TryBeginWrite, EndWrite to Locked<T> (Delphi 11+ only).
53+ // / - Locked<T>.Access/Release now implement locking with a
54+ // / SRW lock (when available) in 'write' access mode. This is funcionally
55+ // / identical to the old implementation (critical section).
5356// / 1.27d: 2023-11-28
5457// / - Fixed hints & warnings.
5558// / - Locked<T>.Initialize without the 'factory' parameter could return
@@ -384,10 +387,9 @@ TOmniResourceCount = class abstract(TInterfacedObject, IOmniResourceCount, IOm
384387 { $IFDEF OTL_HasLightweightMREW}
385388 TLightweightMREWEx = record
386389 private
387- FRWLock : TLightweightMREW;
388- FReadLockCount : TOmniAlignedInt32;
389- FWriteLockCount : TOmniAlignedInt32;
390- FLockOwner : TThreadID;
390+ FRWLock : TLightweightMREW;
391+ FWriteLockCount: TOmniAlignedInt32;
392+ FLockOwner : TThreadID;
391393 private
392394 function GetLockOwner : TThreadID; inline;
393395 procedure SetLockOwner (value : TThreadID); inline;
@@ -404,7 +406,6 @@ TLightweightMREWEx = record
404406 function TryBeginWrite (timeout: cardinal): boolean; overload;
405407 { $IFEND LINUX or ANDROID}
406408 procedure EndWrite ;
407- function IsLocked : boolean; inline;
408409 end ; { TLightweightMREWEx }
409410
410411 ILightweightMREWEx = interface
@@ -418,7 +419,6 @@ TLightweightMREWEx = record
418419 function TryBeginWrite (timeout: cardinal): boolean; overload;
419420 { $IFEND LINUX or ANDROID}
420421 procedure EndWrite ;
421- function IsLocked : boolean;
422422 end ; { ILightweightMREWEx }
423423
424424 TLightweightMREWExImpl = class (TInterfacedObject, ILightweightMREWEx)
@@ -435,7 +435,6 @@ TLightweightMREWExImpl = class(TInterfacedObject, ILightweightMREWEx)
435435 function TryBeginWrite (timeout: cardinal): boolean; overload;
436436 { $IFEND LINUX or ANDROID}
437437 procedure EndWrite ;
438- function IsLocked : boolean;
439438 end ; { TLightweightMREWEx }
440439 { $ENDIF OTL_HasLightweightMREW}
441440
@@ -457,11 +456,16 @@ Atomic<I; T:constructor> = class
457456 Locked<T> = record
458457 strict private // keep those aligned!
459458 FLock : { $IFDEF OTL_HasLightweightMREW} ILightweightMREWEx{ $ELSE} TOmniCS{ $ENDIF} ;
459+ { $IFDEF DEBUG}
460+ FLockCount: IOmniCounter;
461+ { $ENDIF DEBUG}
460462 FValue : T;
461463 strict private
462464 FInitialized: boolean;
463465 FLifecycle : IInterface;
464466 FOwnsObject : boolean;
467+ procedure AssertLocked ; inline;
468+ procedure AssertNotLocked ; inline;
465469 procedure Clear ; inline;
466470 function GetValue : T; inline;
467471 procedure SetValue (const value : T); inline;
@@ -1655,40 +1659,35 @@ class function Atomic<I,T>.Initialize(var storage: I): I;
16551659
16561660function TLightweightMREWEx.GetLockOwner : TThreadID;
16571661begin
1658- { $IFDEF CPUX64 }
1659- Result := TThreadID(TInterlocked.Read(Int64( FLockOwner)) );
1662+ { $IFDEF MSWINDOWS }
1663+ Result := InterlockedCompareExchange(integer( FLockOwner), 0 , 0 );
16601664 { $ELSE}
1661- Result := TThreadID(TInterlocked.Read(Integer(FLockOwner)));
1662- { $ENDIF}
1663- MemoryBarrier;
1665+ Result := TInterlocked.Read(FLockOwner);
1666+ { $ENDIF ~MSWINDOWS}
16641667end ; { TLightweightMREWEx.GetLockOwner }
16651668
16661669procedure TLightweightMREWEx.SetLockOwner (value : TThreadID);
16671670begin
1668- MemoryBarrier;
1669- { $IFDEF CPUX64}
1670- TInterlocked.Exchange(Int64(FLockOwner), Int64(value ));
1671- { $ELSE}
1672- TInterlocked.Exchange(Integer(FLockOwner), Integer(value ));
1673- { $ENDIF}
1671+ Assert(CAS32(FLockOwner, value , FLockOwner), ' Failed to set new lock owner - check for race conditions!' );
16741672end ; { TLightweightMREWEx.SetLockOwner }
16751673
16761674class operator TLightweightMREWEx.Initialize(out dest: TLightweightMREWEx);
16771675begin
16781676 Dest.SetLockOwner(0 );
1679- Dest.FReadLockCount.Value := 0 ;
16801677 Dest.FWriteLockCount.Value := 0 ;
16811678end ; { TLightweightMREWEx.Initialize }
16821679
16831680procedure TLightweightMREWEx.BeginRead ;
16841681begin
16851682 FRWLock.BeginRead;
1686- FReadLockCount.Increment;
16871683end ; { TLightweightMREWEx.BeginRead }
16881684
16891685procedure TLightweightMREWEx.BeginWrite ;
16901686begin
16911687 if GetLockOwner = TThread.Current.ThreadID then
1688+ // We are already an owner so no need for locking.
1689+ // If another thread executes BeginWrite at this moment, it would enter
1690+ // the 'else' part below and block in the call to FRWLock.BeginWrite.
16921691 FWriteLockCount.Increment
16931692 else begin
16941693 FRWLock.BeginWrite;
@@ -1699,7 +1698,6 @@ procedure TLightweightMREWEx.BeginWrite;
16991698
17001699procedure TLightweightMREWEx.EndRead ;
17011700begin
1702- FReadLockCount.Decrement;
17031701 FRWLock.EndRead;
17041702end ; { TLightweightMREWEx.EndRead }
17051703
@@ -1714,16 +1712,9 @@ procedure TLightweightMREWEx.EndWrite;
17141712 end ;
17151713end ; { TLightweightMREWEx.EndWrite }
17161714
1717- function TLightweightMREWEx.IsLocked : boolean;
1718- begin
1719- Result := (FReadLockCount.Value > 0 ) or (FWriteLockCount.Value > 0 );
1720- end ; { TLightweightMREWEx.IsLocked }
1721-
17221715function TLightweightMREWEx.TryBeginRead : boolean;
17231716begin
17241717 Result := FRWLock.TryBeginRead;
1725- if Result then
1726- FReadLockCount.Increment;
17271718end ; { TLightweightMREWEx.TryBeginRead }
17281719
17291720{ $IF defined(LINUX) or defined(ANDROID)}
@@ -1772,17 +1763,19 @@ function TLightweightMREWEx.TryBeginWrite(timeout: cardinal): boolean;
17721763
17731764constructor Locked<T>.Create(const value : T; ownsObject: boolean);
17741765begin
1766+ { $IFDEF OTL_HasLightweightMREW}
1767+ FLock := TLightweightMREWExImpl.Create;
1768+ { $ELSE ~OTL_HasLightweightMREW}
1769+ FLock.Initialize;
1770+ { $ENDIF ~OTL_HasLightweightMREW}
1771+ FLockCount := CreateCounter;
1772+
17751773 Clear;
17761774 FValue := value ;
17771775 if ownsObject and (PTypeInfo(TypeInfo(T))^.Kind = tkClass) then
17781776 FLifecycle := CreateAutoDestroyObject(TObject(PPointer(@value )^));
17791777
17801778 FInitialized := true;
1781- { $IFDEF OTL_HasLightweightMREW}
1782- FLock := TLightweightMREWExImpl.Create;
1783- { $ELSE ~OTL_HasLightweightMREW}
1784- FLock.Initialize;
1785- { $ENDIF ~OTL_HasLightweightMREW}
17861779end ; { Locked<T>.Create }
17871780
17881781class operator Locked<T>.Implicit(const value : Locked<T>): T;
@@ -1802,24 +1795,56 @@ procedure Locked<T>.Acquire;
18021795 { $ELSE ~OTL_HasLightweightMREW}
18031796 FLock.Acquire;
18041797 { $ENDIF ~OTL_HasLightweightMREW}
1798+ { $IFDEF DEBUG}
1799+ FLockCount.Increment;
1800+ { $ENDIF DEBUG}
18051801end ; { Locked<T>.Acquire }
18061802
1803+ procedure Locked <T>.AssertLocked;
1804+ begin
1805+ // This is just a debugging helper. It catches most bad cases of accessing
1806+ // Locked<T>.Value while Locked<T> is not locked. It may fail (not detect a problem)
1807+ // in multithreading code where one thread may lock the Locked<T> and
1808+ // then another thread tries to access Locked<T>.Value.
1809+ { $IFDEF DEBUG}
1810+ Assert(FLockCount.Value > 0 , ' Locked<T> is not locked!' );
1811+ { $ENDIF DEBUG}
1812+ end ; { Locked<T>.AssertLocked }
1813+
1814+ procedure Locked <T>.AssertNotLocked;
1815+ begin
1816+ // This is just a debugging helper. It catches probles in a single-threaded
1817+ // code. It may fail (not detect a problem) in a multithreaded code when
1818+ // one thread executes this test, another thread then locks the the value
1819+ // and first thread resumes execution.
1820+ { $IFDEF DEBUG}
1821+ Assert(FLockCount.Value = 0 , ' Locked<T> is locked!' );
1822+ { $ENDIF DEBUG}
1823+ end ; { Locked<T>.AssertNotLocked }
1824+
18071825{ $IFDEF OTL_HasLightweightMREW}
18081826function Locked <T>.BeginRead: T;
18091827begin
18101828 FLock.BeginRead;
1829+ { $IFDEF DEBUG}
1830+ FLockCount.Increment;
1831+ { $ENDIF DEBUG}
18111832 Result := FValue;
18121833end ; { Locked<T>.BeginRead }
18131834
18141835function Locked <T>.BeginWrite: T;
18151836begin
18161837 FLock.BeginWrite;
1838+ { $IFDEF DEBUG}
1839+ FLockCount.Increment;
1840+ { $ENDIF DEBUG}
18171841 Result := FValue;
18181842end ; { Locked<T>.BeginWrite }
18191843{ $ENDIF OTL_HasLightweightMREW}
18201844
18211845procedure Locked <T>.Clear;
18221846begin
1847+ AssertNotLocked;
18231848 FLifecycle := nil ;
18241849 FInitialized := false;
18251850 FValue := Default(T);
@@ -1830,11 +1855,17 @@ procedure Locked<T>.Clear;
18301855procedure Locked <T>.EndRead;
18311856begin
18321857 FLock.EndRead;
1858+ { $IFDEF DEBUG}
1859+ FLockCount.Decrement;
1860+ { $ENDIF DEBUG}
18331861end ; { Locked<T>.EndRead }
18341862
18351863procedure Locked <T>.EndWrite;
18361864begin
18371865 FLock.EndWrite;
1866+ { $IFDEF DEBUG}
1867+ FLockCount.Decrement;
1868+ { $ENDIF DEBUG}
18381869end ; { Locked<T>.EndWrite }
18391870{ $ENDIF OTL_HasLightweightMREW}
18401871
@@ -1862,21 +1893,13 @@ procedure Locked<T>.Free;
18621893
18631894function Locked <T>.GetValue: T;
18641895begin
1865- { $IFDEF OTL_HasLightweightMREW}
1866- Assert(FLock.IsLocked, ' Locked<T>.GetValue: Not locked' );
1867- { $ELSE ~OTL_HasLightweightMREW}
1868- Assert(FLock.LockCount > 0 , ' Locked<T>.GetValue: Not locked' );
1869- { $ENDIF ~OTL_HasLightweightMREW}
1896+ AssertLocked;
18701897 Result := FValue;
18711898end ; { Locked<T>.GetValue }
18721899
18731900procedure Locked <T>.SetValue(const value : T);
18741901begin
1875- { $IFDEF OTL_HasLightweightMREW}
1876- Assert(FLock.IsLocked, ' Locked<T>.SetValue: Not locked' );
1877- { $ELSE ~OTL_HasLightweightMREW}
1878- Assert(FLock.LockCount > 0 , ' Locked<T>.SetValue: Not locked' );
1879- { $ENDIF ~OTL_HasLightweightMREW}
1902+ AssertLocked;
18801903 FValue := value ;
18811904end ; { Locked<T>.SetValue }
18821905
@@ -1888,6 +1911,8 @@ function Locked<T>.Initialize(factory: TFactory): T;
18881911 { $ELSE ~OTL_HasLightweightMREW}
18891912 FLock.Initialize;
18901913 { $ENDIF ~OTL_HasLightweightMREW}
1914+ FLockCount := CreateCounter;
1915+
18911916 Acquire;
18921917 try
18931918 if not FInitialized then begin
@@ -1944,6 +1969,9 @@ procedure Locked<T>.Leave;
19441969 { $ELSE ~OTL_HasLightweightMREW}
19451970 FLock.Release;
19461971 { $ENDIF ~OTL_HasLightweightMREW}
1972+ { $IFDEF DEBUG}
1973+ FLockCount.Decrement;
1974+ { $ENDIF DEBUG}
19471975end ; { Locked<T>.Leave }
19481976
19491977procedure Locked <T>.Locked(proc: TProc);
@@ -1971,22 +1999,38 @@ procedure Locked<T>.Release;
19711999function Locked <T>.TryBeginRead: boolean;
19722000begin
19732001 Result := FLock.TryBeginRead;
2002+ { $IFDEF DEBUG}
2003+ if Result then
2004+ FLockCount.Increment;
2005+ { $ENDIF DEBUG}
19742006end ; { Locked<T>.TryBeginRead}
19752007
19762008function Locked <T>.TryBeginWrite: boolean;
19772009begin
19782010 Result := FLock.TryBeginWrite;
2011+ { $IFDEF DEBUG}
2012+ if Result then
2013+ FLockCount.Increment;
2014+ { $ENDIF DEBUG}
19792015end ; { Locked<T>.TryBeginWrite }
19802016
19812017{ $IF defined(LINUX) or defined(ANDROID)}
19822018function Locked <T>.TryBeginRead(timeout: cardinal): boolean;
19832019begin
19842020 Result := FLock.TryBeginRead(timeout);
2021+ { $IFDEF DEBUG}
2022+ if Result then
2023+ FLockCount.Increment;
2024+ { $ENDIF DEBUG}
19852025end ; { Locked<T>.TryBeginRead }
19862026
19872027function Locked <T>.TryBeginWrite(timeout: cardinal): boolean; overload; inline;
19882028begin
19892029 Result := FLock.TryBeginWrite(timeout);
2030+ { $IFDEF DEBUG}
2031+ if Result then
2032+ FLockCount.Increment;
2033+ { $ENDIF DEBUG}
19902034end ; { Locked<T>.TryBeginWrite }
19912035{ $IFEND LINUX or ANDROID}
19922036{ $ENDIF OTL_HasLightweightMREW}
@@ -3006,11 +3050,6 @@ procedure TLightweightMREWExImpl.EndWrite;
30063050 FLock.EndWrite;
30073051end ; { TLightweightMREWExImpl.EndWrite }
30083052
3009- function TLightweightMREWExImpl.IsLocked : boolean;
3010- begin
3011- Result := FLock.IsLocked;
3012- end ; { TLightweightMREWExImpl.IsLocked }
3013-
30143053function TLightweightMREWExImpl.TryBeginRead : boolean;
30153054begin
30163055 Result := FLock.TryBeginRead;
0 commit comments