- DelphiTools - https://www.delphitools.info -

TMonitor woes

Primoz Gabrijelcic [1] recently reported a possible bug with TMonitor [2], in the more advanced side of TMonitor.

However, when experimenting with it for DWS, I bumped on issues in the basic usage scenarios too, and reverted to using critical sections. It seems that as of Delphi XE, short of a patch, TMonitor is just a waste of 4 bytes per object instance.

One of the basic usage of TMonitor I’m referring to would be to replace a critical section:

TMonitor.Enter(someObject);
try
   // protected code
finally
   TMonitor.Exit(someObject);
end;

However, TMonitor is having a problem with the above, and you can quickly run into situations where everything gets serialized, even when there is no need to. Let’s look at a minimal thread:

type
   TMyThread = class(TThread)
      Count : Integer;
      Obj : TObject;
      procedure Execute; override;
   end;

procedure TMyThread.Execute;
begin
   while not Terminated do begin
      System.TMonitor.Enter(Obj);
      try
         Dec(Count); // or do something else
         if Count<=0 then Break;
      finally
         System.TMonitor.Exit(Obj);
      end;
   end;
end;

Assuming you create two instances of the above thread class, which are working on two different “Obj” instances, the two threads should be able to run in parallel, as they don’t operate on the same memory structures at all, right?
Well, if you use plain old critical sections, they will, but if you use TMonitor like in the above code, they won’t, they’ll just run serialized, and all but the first thread will suffer from severe contention, which hints that a race condition [3] is hiding somewhere in TMonitor’s code…