Recent

Author Topic: Improvement of TFPTimerThread.Execute  (Read 443 times)

lagprogramming

  • Sr. Member
  • ****
  • Posts: 406
Improvement of TFPTimerThread.Execute
« on: April 18, 2024, 12:53:29 pm »
packages/fcl-base/src/fptimer.pp has
Code: Pascal  [Select][+][-]
  1. {$ifdef Has_EventWait}
  2. procedure TFPTimerThread.Execute;
  3. var
  4.   WakeTime, StartTime: TDateTime;
  5.   WakeInterval: Integer;
  6.   Counter: int64; { use Int64 to avoid overflow with Counter*fInterval (~49 days)}
  7.   AInterval: int64;
  8.   Diff: Extended;
  9.  
  10. Const
  11.   wrSignaled = 0;
  12.   wrTimeout  = 1;
  13.   wrAbandoned= 2;
  14.   wrError    = 3;
  15.  
  16. begin
  17.   WakeInterval := MaxInt;
  18.   Counter := 1;
  19.   AInterval := fInterval;
  20.   FStartTime := Now;
  21.   while not Terminated do
  22.     begin
  23.     if GetWakeTime(AInterval,Counter,WakeInterval,WakeTime) then
  24.       Continue;
  25.     if not Terminated then
  26.       case BasicEventWaitFor(WakeInterval,fWaitEvent) of
  27.       wrTimeout:
  28.         begin
  29.         if Terminated then
  30.           Break
  31.         else
  32.           begin
  33.           try
  34.             if not Timer.UseTimerThread then
  35.               // If terminate is called while here, then the Synchronize will be
  36.               // queued while the stoptimer is being processed.
  37.               // StopTimer cannot wait until thread completion as this would deadlock
  38.               Synchronize(@Timer.Timer)  // Call user event
  39.             else
  40.               Timer.Timer;
  41.           except
  42.             // Trap errors to prevent this thread from terminating
  43.           end;
  44.           Inc(Counter);                // Next interval
  45.           end;
  46.         end;
  47.       wrSignaled:
  48.         begin
  49.         if Terminated then
  50.           Break
  51.         else
  52.           begin                      // Interval has changed
  53.           Counter := 1;              // Restart timer without creating new thread
  54.           AInterval := fInterval;
  55.           FStartTime := Now;
  56.           end;
  57.         end;
  58.       else
  59.         Break;
  60.       end
  61.     end;
  62.   BasicEventDestroy(fWaitEvent);
  63. end;
  64.  
  65. {$ELSE Has_EventWait}
  66.  
  67. procedure TFPTimerThread.Execute;
  68.  
  69. var
  70.   WakeTime, StartTime: TDateTime;
  71.   WakeInterval: Integer;
  72.   Counter: int64; { use Int64 to avoid overflow with Counter*fInterval (~49 days)}
  73.   AInterval: int64;
  74.   Diff: Extended;
  75.   S,Last: Cardinal;
  76.   RecheckTimeCounter: integer;
  77.  
  78. const
  79.   cSleepTime = 500;           // 0.5 second, better than every 5 milliseconds
  80.   cRecheckTimeCount = 120;    // Recheck clock every minute, as the sleep loop can loose time
  81.  
  82. begin
  83.   WakeInterval := MaxInt;
  84.   Counter := 1;
  85.   AInterval := fInterval;
  86.   FStartTime := Now;
  87.   while not Terminated do
  88.     begin
  89.     if GetWakeTime(AInterval,Counter,WakeInterval,WakeTime) then
  90.       Continue;
  91.     if not Terminated then
  92.       begin
  93.       RecheckTimeCounter := cRecheckTimeCount;
  94.       s := cSleepTime;
  95.       repeat
  96.         if s > WakeInterval then
  97.           s := WakeInterval;
  98.         sleep(s);
  99.         if fSignaled then            // Terminated or interval has changed
  100.           begin
  101.           if not Terminated then
  102.             begin
  103.             fSignaled := False;
  104.             Counter := 1;            // Restart timer
  105.             AInterval := fInterval;
  106.             StartTime := Now;
  107.             end;
  108.           break;                     // Need to break out of sleep loop
  109.           end;
  110.  
  111.         dec(WakeInterval,s);         // Update total wait time
  112.         dec(RecheckTimeCounter);     // Do we need to recheck current time
  113.         if (RecheckTimeCounter < 0) and (WakeInterval > 0) then
  114.           begin
  115.           Diff := (WakeTime - Now);
  116.           WakeInterval := Trunc(Diff * cMilliSecs);
  117.           RecheckTimeCounter := cRecheckTimeCount;
  118.           s := cSleepTime;
  119.           end;
  120.       until (WakeInterval<=0) or Terminated;
  121.       if WakeInterval <= 0 then
  122.         try
  123.           inc(Counter);
  124.           if not Timer.UseTimerThread then
  125.             // If terminate is called while here, then the Synchronize will be
  126.             // queued while the stoptimer is being processed.
  127.             // StopTimer cannot wait until thread completion as this would deadlock
  128.             Synchronize(@Timer.Timer)  // Call user event
  129.           else
  130.             Timer.Timer;
  131.         except
  132.           // Trap errors to prevent this thread from terminating
  133.         end;
  134.       end
  135.     end;
  136. end;
  137. {$ENDIF Has_EventWait}





Variable "Last" is declared but never used, which means it can be removed.
Looking at variable StartTime I've noticed that when Has_EventWait is defined, the variable can be removed because it's not used at all. But then I've looked at the content of the procedure when Has_EventWait is not defined. It has just a simple assignment: StartTime := Now;. When Has_EventWait is defined the equivalent line is FStartTime := Now;. It doesn't look right. Looks like a bug, a typo. Most likely should have been "FStartTime := Now;" instead of "StartTime := Now;".
Here is a patch.

AlexTP

  • Hero Member
  • *****
  • Posts: 2413
    • UVviewsoft

 

TinyPortal © 2005-2018