mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-11-03 14:29:26 +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;
|
||||
|
||||
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
|
||||
Start : Int64;
|
||||
TimeA,TimeB,Elapsed : Int64;
|
||||
Stage : StageEnum;
|
||||
StageIteration,TimeToSleep : uint32;
|
||||
|
||||
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}
|
||||
Start:=GetTickCount64;
|
||||
TimeA:=-1;
|
||||
Stage:=Spin;
|
||||
Int32(StageIteration):=-1;
|
||||
Repeat
|
||||
Result:=TryEnter;
|
||||
if not Result then
|
||||
Sleep(2);
|
||||
until Result or ((GetTickCount64-Start)>aTimeout);
|
||||
if Result or (aTimeout=0) then
|
||||
break;
|
||||
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}
|
||||
end;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user