1

Take a look at this (pseudo) code

procedure TestASync;
begin
  var lSomeIntf:=TSomeImplementor.Create as ISomeIntf;
  parallel.ASync(
    procedure
    begin
       sleep(1000); // allow the main thread to finish
       MyThreadedProc(lSomeIntf);
    end
  );
  sleep(100); // will finish before sub-thread
end;

This produces a race condition as I found out the hard way. Because TestASync is already finished before the anonymous method gets the chance of calling MyThreadedPorc, MyThreadedProc is called with a nil interface. (Which is already a lot better then some random value)

Q1: Would this also be the case for simple, non ref-counted variables (like an integer, double etc) - I suspect strongly they may get changed/return random values since they are located on the stack.

Q2: How do I get around this in a simple, clean way?

I have been fiddling with the IOmniTask interface and other methods to start an unmonitored background thread stuff like that, but all of these appear to clutter up my code and make the source hard to understand.

Maybe something like this? :

procedure TestGenericASync;
begin
  var lSomeIntf:=TSomeImplementor.Create as ISomeIntf;
  parallel.ASync<ISomeIntf>(lSomeIntf,
    procedure (const aSomeIntf:ISomeIntf)
    begin
       MyThreadedProc(aSomeIntf);
    end
  );
end;


H.Hasenack
  • 1,094
  • 8
  • 27
  • 1
    Your original code should work. It is possible that there is a problem with capturing inline variable or with directly using that variable as parameter. I would try removing inline variable declaration and use regular local variable and if that does not work then declaring local variable inside Async proc and assigning lSomeIntf to that before using it as parameter. Cannot test at the moment to see what is really going on. – Dalija Prasnikar May 15 '20 at 11:32
  • Also assigning it to a local variable in the anonymous procedure before calling MyThreadedProc has no effect. Set a breakpoint on the begin of the anonymous proicedure, and lSOmeIntf is already nil. (Probably depends on CPU, speed and randomness. – H.Hasenack May 15 '20 at 11:52
  • @DalijaPrasnikar Is correct. The anon method is implemented by a class which takes a reference to the interface (capture) and that should keep it alive. – David Heffernan May 15 '20 at 12:18
  • 2
    There is a bug with capturing inline variables https://quality.embarcadero.com/browse/RSP-26666 removing inline variable declaration should work (just checked) and if does not then we need to see [mcve] – Dalija Prasnikar May 15 '20 at 12:20
  • @Dalija, yes that did the trick. I'll update the Q to avoid the race condition and provide an answer with updated code. – H.Hasenack May 15 '20 at 12:35
  • 1
    There is no race condition. Variable is captured (which means additionally increasing reference count) before TestAsync is finished and local variable reference count is decreased. If you have some issues they are not due to the capture and the code you have presented here. – Dalija Prasnikar May 15 '20 at 12:39

1 Answers1

1

As Dalija suggested, you have to work around a Delphi bug (RSP-26666) with inline variable declarations. These inline variable declarations are not captured properly by anonymous methods.

The workaround is not to use an inline variable. (or to upgrade to RS 10.4 Sydney.)

procedure TestASync;
var lSomeIntf:ISomeIntf; // declaring here works around the bug
begin
  lSomeIntf:=TSomeImplementor.Create as ISomeIntf;
  parallel.ASync(
    procedure
    begin
       sleep(1000); // allow the main thread to finish
       MyThreadedProc(lSomeIntf);
    end
  );
  sleep(100); // will finish before sub-thread
end;
H.Hasenack
  • 1,094
  • 8
  • 27
  • It seems this bug has been fixed in RS10.4 Sydney – H.Hasenack May 27 '20 at 13:31
  • And in 10.4 sydney a new , similar bug has been created. The referenc count is increased by the capture, but never decreased by the sub-thread when the interface goes out of scope. – H.Hasenack Jun 11 '20 at 19:23
  • I have put the closely related interface release bug here: https://quality.embarcadero.com/browse/RSP-29564 please vote this bug up! – H.Hasenack Jun 11 '20 at 19:40