GXS.AsyncTimer.pas 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.AsyncTimer;
  5. (*
  6. Asynchronous timer component (actual 1 ms resolution).
  7. This component is based on ThreadedTimer by Carlos Barbosa.
  8. *)
  9. interface
  10. {.$I Stage.Defines.inc}
  11. uses
  12. System.Classes,
  13. System.SysUtils,
  14. System.SyncObjs,
  15. Stage.Utils;
  16. const
  17. cDEFAULT_TIMER_INTERVAL = 1000;
  18. type
  19. (* Asynchronous timer component (actual 1 ms resolution, if CPU fast enough).
  20. Keep in mind timer resolution is obtained in-between events, but
  21. events are not triggered every x ms. For instance if you set the interval to
  22. 5 ms, and your Timer event takes 1 ms to complete, Timer events will actually
  23. be triggered every 5+1=6 ms (that's why it's "asynchronous").
  24. This component is based on ThreadedTimer by Carlos Barbosa. *)
  25. TgxAsyncTimer = class(TComponent)
  26. private
  27. FEnabled: Boolean;
  28. FOnTimer: TNotifyEvent;
  29. FTimerThread: TThread;
  30. FMutex: TCriticalSection;
  31. protected
  32. procedure SetEnabled(Value: Boolean);
  33. function GetInterval: Word;
  34. procedure SetInterval(Value: Word);
  35. function GetThreadPriority: TThreadPriority;
  36. procedure SetThreadPriority(Value: TThreadPriority);
  37. procedure DoTimer;
  38. public
  39. constructor Create(AOwner: TComponent); override;
  40. destructor Destroy; override;
  41. published
  42. property Enabled: Boolean read FEnabled write SetEnabled default False;
  43. property Interval: Word read GetInterval write SetInterval
  44. default cDEFAULT_TIMER_INTERVAL;
  45. property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
  46. property ThreadPriority: TThreadPriority read GetThreadPriority
  47. write SetThreadPriority default tpTimeCritical;
  48. end;
  49. procedure Register;
  50. implementation // ------------------------------------------------------------
  51. type
  52. TTimerThread = class(TThread)
  53. private
  54. FOwner: TgxAsyncTimer;
  55. FInterval: Word;
  56. protected
  57. procedure Execute; override;
  58. public
  59. constructor Create(CreateSuspended: Boolean); virtual;
  60. end;
  61. constructor TTimerThread.Create(CreateSuspended: Boolean);
  62. begin
  63. inherited Create(CreateSuspended);
  64. end;
  65. procedure TTimerThread.Execute;
  66. var
  67. lastTick, nextTick, curTick, perfFreq: Int64;
  68. begin
  69. QueryPerformanceFrequency(perfFreq);
  70. QueryPerformanceCounter(lastTick);
  71. nextTick := lastTick + (FInterval * perfFreq) div 1000;
  72. while not Terminated do
  73. begin
  74. FOwner.FMutex.Acquire;
  75. FOwner.FMutex.Release;
  76. while not Terminated do
  77. begin
  78. QueryPerformanceCounter(lastTick);
  79. if lastTick >= nextTick then
  80. break;
  81. Sleep(1);
  82. end;
  83. if not Terminated then
  84. begin
  85. // if time elapsed run user-event
  86. Synchronize(FOwner.DoTimer);
  87. QueryPerformanceCounter(curTick);
  88. nextTick := lastTick + (FInterval * perfFreq) div 1000;
  89. if nextTick <= curTick then
  90. begin
  91. // CPU too slow... delay to avoid monopolizing what's left
  92. nextTick := curTick + (FInterval * perfFreq) div 1000;
  93. end;
  94. end;
  95. end;
  96. end;
  97. //-----------------------------------------
  98. // TgxAsyncTimer
  99. //-----------------------------------------
  100. constructor TgxAsyncTimer.Create(AOwner: TComponent);
  101. begin
  102. inherited Create(AOwner);
  103. // create timer thread
  104. FMutex := TCriticalSection.Create;
  105. FMutex.Acquire;
  106. FTimerThread := TTimerThread.Create(False);
  107. with TTimerThread(FTimerThread) do
  108. begin
  109. FOwner := Self;
  110. FreeOnTerminate := False;
  111. Priority := tpTimeCritical;
  112. FInterval := cDEFAULT_TIMER_INTERVAL;
  113. end;
  114. end;
  115. destructor TgxAsyncTimer.Destroy;
  116. begin
  117. Enabled := False;
  118. FTimerThread.Terminate;
  119. FMutex.Release;
  120. CheckSynchronize;
  121. // wait & free
  122. FTimerThread.WaitFor;
  123. FTimerThread.Free;
  124. FMutex.Free;
  125. inherited Destroy;
  126. end;
  127. procedure TgxAsyncTimer.DoTimer;
  128. begin
  129. if Enabled and Assigned(FOnTimer) then
  130. FOnTimer(Self);
  131. end;
  132. procedure TgxAsyncTimer.SetEnabled(Value: Boolean);
  133. begin
  134. if Value <> FEnabled then
  135. begin
  136. FEnabled := Value;
  137. if FEnabled then
  138. begin
  139. // When enabled resume thread
  140. if TTimerThread(FTimerThread).FInterval > 0 then
  141. FMutex.Release;
  142. end
  143. else
  144. FMutex.Acquire;
  145. end;
  146. end;
  147. function TgxAsyncTimer.GetInterval: Word;
  148. begin
  149. Result := TTimerThread(FTimerThread).FInterval;
  150. end;
  151. procedure TgxAsyncTimer.SetInterval(Value: Word);
  152. begin
  153. if Value <> TTimerThread(FTimerThread).FInterval then
  154. begin
  155. TTimerThread(FTimerThread).FInterval := Value;
  156. end;
  157. end;
  158. function TgxAsyncTimer.GetThreadPriority: TThreadPriority;
  159. begin
  160. Result := FTimerThread.Priority;
  161. end;
  162. procedure TgxAsyncTimer.SetThreadPriority(Value: TThreadPriority);
  163. begin
  164. FTimerThread.Priority := Value;
  165. end;
  166. procedure Register;
  167. begin
  168. RegisterComponents('GLXEngine', [TgxAsyncTimer]);
  169. end;
  170. end.