GLAsyncTimer.pas 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLAsyncTimer;
  5. (*
  6. Asynchronous timer component (actual 1 ms resolution).
  7. This component is based on ThreadedTimer by Carlos Barbosa.
  8. *)
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. System.Classes,
  13. System.SysUtils,
  14. System.SyncObjs,
  15. GLCrossPlatform;
  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. TGLAsyncTimer = 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. // ------------------------------------------------------------------
  50. implementation
  51. // ------------------------------------------------------------------
  52. type
  53. TTimerThread = class(TThread)
  54. private
  55. FOwner: TGLAsyncTimer;
  56. FInterval: Word;
  57. protected
  58. procedure Execute; override;
  59. public
  60. constructor Create(CreateSuspended: Boolean); virtual;
  61. end;
  62. constructor TTimerThread.Create(CreateSuspended: Boolean);
  63. begin
  64. inherited Create(CreateSuspended);
  65. end;
  66. procedure TTimerThread.Execute;
  67. var
  68. lastTick, nextTick, curTick, perfFreq: Int64;
  69. begin
  70. QueryPerformanceFrequency(perfFreq);
  71. QueryPerformanceCounter(lastTick);
  72. nextTick := lastTick + (FInterval * perfFreq) div 1000;
  73. while not Terminated do
  74. begin
  75. FOwner.FMutex.Acquire;
  76. FOwner.FMutex.Release;
  77. while not Terminated do
  78. begin
  79. QueryPerformanceCounter(lastTick);
  80. if lastTick >= nextTick then
  81. break;
  82. Sleep(1);
  83. end;
  84. if not Terminated then
  85. begin
  86. // if time elapsed run user-event
  87. Synchronize(FOwner.DoTimer);
  88. QueryPerformanceCounter(curTick);
  89. nextTick := lastTick + (FInterval * perfFreq) div 1000;
  90. if nextTick <= curTick then
  91. begin
  92. // CPU too slow... delay to avoid monopolizing what's left
  93. nextTick := curTick + (FInterval * perfFreq) div 1000;
  94. end;
  95. end;
  96. end;
  97. end;
  98. //-----------------------------------------
  99. { TGLAsyncTimer }
  100. //-----------------------------------------
  101. constructor TGLAsyncTimer.Create(AOwner: TComponent);
  102. begin
  103. inherited Create(AOwner);
  104. // create timer thread
  105. FMutex := TCriticalSection.Create;
  106. FMutex.Acquire;
  107. FTimerThread := TTimerThread.Create(False);
  108. with TTimerThread(FTimerThread) do
  109. begin
  110. FOwner := Self;
  111. FreeOnTerminate := False;
  112. Priority := tpTimeCritical;
  113. FInterval := cDEFAULT_TIMER_INTERVAL;
  114. end;
  115. end;
  116. destructor TGLAsyncTimer.Destroy;
  117. begin
  118. Enabled := False;
  119. FTimerThread.Terminate;
  120. FMutex.Release;
  121. CheckSynchronize;
  122. // wait & free
  123. FTimerThread.WaitFor;
  124. FTimerThread.Free;
  125. FMutex.Free;
  126. inherited Destroy;
  127. end;
  128. procedure TGLAsyncTimer.DoTimer;
  129. begin
  130. if Enabled and Assigned(FOnTimer) then
  131. FOnTimer(Self);
  132. end;
  133. procedure TGLAsyncTimer.SetEnabled(Value: Boolean);
  134. begin
  135. if Value <> FEnabled then
  136. begin
  137. FEnabled := Value;
  138. if FEnabled then
  139. begin
  140. // When enabled resume thread
  141. if TTimerThread(FTimerThread).FInterval > 0 then
  142. FMutex.Release;
  143. end
  144. else
  145. FMutex.Acquire;
  146. end;
  147. end;
  148. function TGLAsyncTimer.GetInterval: Word;
  149. begin
  150. Result := TTimerThread(FTimerThread).FInterval;
  151. end;
  152. procedure TGLAsyncTimer.SetInterval(Value: Word);
  153. begin
  154. if Value <> TTimerThread(FTimerThread).FInterval then
  155. begin
  156. TTimerThread(FTimerThread).FInterval := Value;
  157. end;
  158. end;
  159. function TGLAsyncTimer.GetThreadPriority: TThreadPriority;
  160. begin
  161. Result := FTimerThread.Priority;
  162. end;
  163. procedure TGLAsyncTimer.SetThreadPriority(Value: TThreadPriority);
  164. begin
  165. FTimerThread.Priority := Value;
  166. end;
  167. //-----------------------------------------------------------
  168. initialization
  169. //-----------------------------------------------------------
  170. RegisterClass(TGLAsyncTimer);
  171. end.