|
@@ -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;
|
|
|
|