mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-11 16:29:28 +01:00
Use exponential backoff in timeouted ‘TMonitor.Enter’ and explain why it’s still a bad solution.
This commit is contained in:
parent
d1432b7302
commit
c2176d27ea
@ -206,17 +206,57 @@ end;
|
|||||||
|
|
||||||
function TMonitorData.Enter(aTimeout: Cardinal): Boolean;
|
function TMonitorData.Enter(aTimeout: Cardinal): Boolean;
|
||||||
|
|
||||||
|
type
|
||||||
|
StageEnum = (Spin, ThreadSwitch, SleepA, SleepB, SleepC, SleepD, SleepE, SleepF);
|
||||||
|
|
||||||
|
const
|
||||||
|
SleepTime: array[SleepA .. High(StageEnum)] of uint8 = (2, 4, 8, 16, 30, 50);
|
||||||
|
StageIterations: array[StageEnum] of uint8 = (40, 40, 8, 8, 8, 8, 8, 8);
|
||||||
|
|
||||||
var
|
var
|
||||||
Start : Int64;
|
TimeA,TimeB,Elapsed : Int64;
|
||||||
|
Stage : StageEnum;
|
||||||
|
StageIteration,TimeToSleep : uint32;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
// Should preferably use an event raised on Leave somehow.
|
||||||
|
// And this event should preferably not exist until someone actually uses timeouted Enter, ant not be raised until there are outstanding timeouted Enters.
|
||||||
|
// Sounds complex, so until then, spin-wait + exponentially wait.
|
||||||
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadId,' Begin Enter(',aTimeout,')');{$ENDIF}
|
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadId,' Begin Enter(',aTimeout,')');{$ENDIF}
|
||||||
Start:=GetTickCount64;
|
TimeA:=-1;
|
||||||
|
Stage:=Spin;
|
||||||
|
Int32(StageIteration):=-1;
|
||||||
Repeat
|
Repeat
|
||||||
Result:=TryEnter;
|
Result:=TryEnter;
|
||||||
if not Result then
|
if Result or (aTimeout=0) then
|
||||||
Sleep(2);
|
break;
|
||||||
until Result or ((GetTickCount64-Start)>aTimeout);
|
if TimeA=-1 then
|
||||||
|
TimeA:=GetTickCount64; // Avoid GetTickCount64 call if first TryEnter succeeds. -1 is a possible timestamp, but nothing particularly bad will happen.
|
||||||
|
Inc(Int32(StageIteration));
|
||||||
|
if StageIteration>=StageIterations[Stage] then
|
||||||
|
begin
|
||||||
|
if Stage<High(Stage) then
|
||||||
|
Inc(Stage);
|
||||||
|
StageIteration:=0;
|
||||||
|
end;
|
||||||
|
case Stage of
|
||||||
|
Spin: ;
|
||||||
|
ThreadSwitch: System.ThreadSwitch;
|
||||||
|
SleepA .. High(StageEnum):
|
||||||
|
begin
|
||||||
|
TimeToSleep:=SleepTime[Stage];
|
||||||
|
if aTimeout<TimeToSleep then
|
||||||
|
TimeToSleep:=aTimeout;
|
||||||
|
SysUtils.Sleep(TimeToSleep);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
TimeB:=GetTickCount64;
|
||||||
|
Elapsed:=TimeB-TimeA;
|
||||||
|
TimeA:=TimeB; // Sum of Elapseds will always be exactly <current time> - <start time>.
|
||||||
|
if Elapsed>=aTimeout then
|
||||||
|
break;
|
||||||
|
aTimeout:=aTimeout-Elapsed;
|
||||||
|
until false;
|
||||||
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadId,' End Enter(',aTimeout,'), Result: ',Result);{$ENDIF}
|
{$IFDEF DEBUG_MONITOR}Writeln(StdErr,GetTickCount64,': Thread ',GetCurrentThreadId,' End Enter(',aTimeout,'), Result: ',Result);{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user