Why i get an exception argument out of range?

  

Can someone can explain me why i receive sometime an Exception “Argument out of range” under the ios simulator when i execute the code below? on android i never get any error. I use Delphi berlin.

the functions where the error appear :

{**********************************************************************}
procedure Twin_WorkerThreadPool.Enqueue(const Value: Twin_WorkerThread);
begin
Tmonitor.Enter(fPool);
try
fPool.Add(Value);
fSignal.SetEvent;
finally
Tmonitor.Exit(fPool);
end;
end;

{********************************************************}
function Twin_WorkerThreadPool.Dequeue: Twin_WorkerThread;
begin
Tmonitor.Enter(self); // << only one thread can execute the code below
try

Tmonitor.Enter(fPool);
try
if Fpool.Count > 0 then begin
result := fPool[Fpool.Count – 1];
fPool.Delete(Fpool.Count – 1);
exit;
end;
fSignal.ResetEvent;
finally
Tmonitor.Exit(fPool);
end;

fSignal.WaitFor(Infinite);

Tmonitor.Enter(fPool);
try
result := fPool[Fpool.Count – 1]; // << exception argument out of range ? but how it’s possible ?
fPool.Delete(Fpool.Count – 1);
finally
Tmonitor.Exit(fPool);
end;

finally
Tmonitor.exit(self);
end;
end;

Below the full source code :

{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
Twin_WorkerThreadPool = class(TObject)
private
fPool: TObjectList<Twin_WorkerThread>;
fSignal: Tevent;
public
procedure Enqueue(const Value: Twin_WorkerThread);
function Dequeue: Twin_WorkerThread;
end;

{***********************************}
constructor Twin_WorkerThread.Create;
begin
FProc := nil;
FProcReadySignal := TEvent.Create(nil, false{ManualReset}, false, ”);
FProcFinishedSignal := TEvent.Create(nil, false{ManualReset}, false, ”);
inherited Create(False); // see http://www.gerixsoft.com/blog/delphi/fixing-symbol-resume-deprecated-warning-delphi-2010
end;

{***********************************}
destructor Twin_WorkerThread.Destroy;
begin
Terminate;
FProcReadySignal.setevent;
WaitFor;
FProcReadySignal.Free;
FProcFinishedSignal.Free;
inherited;
end;

{**********************************}
procedure Twin_WorkerThread.Execute;
begin
while True do begin
try

//wait the signal
FProcReadySignal.WaitFor(INFINITE);

//if terminated then exit
if Terminated then Break;

//execute fProc
if assigned(FProc) then FProc();

//signal the proc is finished
FProcFinishedSignal.SetEvent;

except
//hide the exception
end;
end;
end;

{**********************************************************}
procedure Twin_WorkerThread.ExecuteProc(const AProc: TProc);
begin
fProc := AProc;
FProcFinishedSignal.ResetEvent;
FProcReadySignal.SetEvent;
end;

{*****************************************************************}
procedure Twin_WorkerThread.ExecuteAndWaitProc(const AProc: TProc);
begin
fProc := AProc;
FProcFinishedSignal.ResetEvent;
FProcReadySignal.SetEvent;
FProcFinishedSignal.WaitFor(INFINITE);
end;

{********************************************************************}
constructor Twin_WorkerThreadPool.Create(const aThreadCount: integer);
var i: integer;
begin
fPool := TObjectList<Twin_WorkerThread>.create(false{aOwnObjects});
fSignal := TEvent.Create(nil, false{ManualReset}, false, ”);
for I := 0 to aThreadCount – 1 do
fPool.Add(Twin_WorkerThread.Create)
end;

{***************************************}
destructor Twin_WorkerThreadPool.Destroy;
var i: integer;
begin
for I := 0 to fPool.Count – 1 do begin
fPool[i].disposeOf;
fPool[i] := nil;
end;
fPool.Free;
fSignal.Free;
inherited Destroy;
end;

{*********************************************************************}
procedure Twin_WorkerThreadPool.ExecuteAndWaitProc(const AProc: TProc);
var aThread: Twin_WorkerThread;
begin
aThread := Dequeue;
try
aThread.ExecuteAndWaitProc(aProc);
finally
Enqueue(aThread);
end;
end;

NOTE:

Just to explain a little better, remember it’s only on ios it’s not work, and if i add a sleep(1000) after the fSignal.resetEvent then it’s work :

Tmonitor.Enter(fPool);
try
if Fpool.Count > 0 then begin
result := fPool[Fpool.Count – 1];
fPool.Delete(Fpool.Count – 1);
exit;
end;
fSignal.ResetEvent;

sleep(1000);

finally
Tmonitor.Exit(fPool);
end;

fSignal.WaitFor(Infinite);

so it’s look like that the signal is not set to OFF just after doing fSignal.ResetEvent;

i m affraid it’s a bug in TEvent or Tmonitor 🙁

Comments are closed.