123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.AsyncTimer;
- (*
- Asynchronous timer component (actual 1 ms resolution).
- This component is based on ThreadedTimer by Carlos Barbosa.
- *)
- interface
- {.$I Stage.Defines.inc}
- uses
- System.Classes,
- System.SysUtils,
- System.SyncObjs,
- Stage.Utils;
- const
- cDEFAULT_TIMER_INTERVAL = 1000;
- type
- (* Asynchronous timer component (actual 1 ms resolution, if CPU fast enough).
- Keep in mind timer resolution is obtained in-between events, but
- events are not triggered every x ms. For instance if you set the interval to
- 5 ms, and your Timer event takes 1 ms to complete, Timer events will actually
- be triggered every 5+1=6 ms (that's why it's "asynchronous").
- This component is based on ThreadedTimer by Carlos Barbosa. *)
- TgxAsyncTimer = class(TComponent)
- private
- FEnabled: Boolean;
- FOnTimer: TNotifyEvent;
- FTimerThread: TThread;
- FMutex: TCriticalSection;
- protected
- procedure SetEnabled(Value: Boolean);
- function GetInterval: Word;
- procedure SetInterval(Value: Word);
- function GetThreadPriority: TThreadPriority;
- procedure SetThreadPriority(Value: TThreadPriority);
- procedure DoTimer;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- published
- property Enabled: Boolean read FEnabled write SetEnabled default False;
- property Interval: Word read GetInterval write SetInterval
- default cDEFAULT_TIMER_INTERVAL;
- property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
- property ThreadPriority: TThreadPriority read GetThreadPriority
- write SetThreadPriority default tpTimeCritical;
- end;
- procedure Register;
- implementation // ------------------------------------------------------------
- type
- TTimerThread = class(TThread)
- private
- FOwner: TgxAsyncTimer;
- FInterval: Word;
- protected
- procedure Execute; override;
- public
- constructor Create(CreateSuspended: Boolean); virtual;
- end;
- constructor TTimerThread.Create(CreateSuspended: Boolean);
- begin
- inherited Create(CreateSuspended);
- end;
- procedure TTimerThread.Execute;
- var
- lastTick, nextTick, curTick, perfFreq: Int64;
- begin
- QueryPerformanceFrequency(perfFreq);
- QueryPerformanceCounter(lastTick);
- nextTick := lastTick + (FInterval * perfFreq) div 1000;
- while not Terminated do
- begin
- FOwner.FMutex.Acquire;
- FOwner.FMutex.Release;
- while not Terminated do
- begin
- QueryPerformanceCounter(lastTick);
- if lastTick >= nextTick then
- break;
- Sleep(1);
- end;
- if not Terminated then
- begin
- // if time elapsed run user-event
- Synchronize(FOwner.DoTimer);
- QueryPerformanceCounter(curTick);
- nextTick := lastTick + (FInterval * perfFreq) div 1000;
- if nextTick <= curTick then
- begin
- // CPU too slow... delay to avoid monopolizing what's left
- nextTick := curTick + (FInterval * perfFreq) div 1000;
- end;
- end;
- end;
- end;
- //-----------------------------------------
- // TgxAsyncTimer
- //-----------------------------------------
- constructor TgxAsyncTimer.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- // create timer thread
- FMutex := TCriticalSection.Create;
- FMutex.Acquire;
- FTimerThread := TTimerThread.Create(False);
- with TTimerThread(FTimerThread) do
- begin
- FOwner := Self;
- FreeOnTerminate := False;
- Priority := tpTimeCritical;
- FInterval := cDEFAULT_TIMER_INTERVAL;
- end;
- end;
- destructor TgxAsyncTimer.Destroy;
- begin
- Enabled := False;
- FTimerThread.Terminate;
- FMutex.Release;
- CheckSynchronize;
- // wait & free
- FTimerThread.WaitFor;
- FTimerThread.Free;
- FMutex.Free;
- inherited Destroy;
- end;
- procedure TgxAsyncTimer.DoTimer;
- begin
- if Enabled and Assigned(FOnTimer) then
- FOnTimer(Self);
- end;
- procedure TgxAsyncTimer.SetEnabled(Value: Boolean);
- begin
- if Value <> FEnabled then
- begin
- FEnabled := Value;
- if FEnabled then
- begin
- // When enabled resume thread
- if TTimerThread(FTimerThread).FInterval > 0 then
- FMutex.Release;
- end
- else
- FMutex.Acquire;
- end;
- end;
- function TgxAsyncTimer.GetInterval: Word;
- begin
- Result := TTimerThread(FTimerThread).FInterval;
- end;
- procedure TgxAsyncTimer.SetInterval(Value: Word);
- begin
- if Value <> TTimerThread(FTimerThread).FInterval then
- begin
- TTimerThread(FTimerThread).FInterval := Value;
- end;
- end;
- function TgxAsyncTimer.GetThreadPriority: TThreadPriority;
- begin
- Result := FTimerThread.Priority;
- end;
- procedure TgxAsyncTimer.SetThreadPriority(Value: TThreadPriority);
- begin
- FTimerThread.Priority := Value;
- end;
- procedure Register;
- begin
- RegisterComponents('GLXEngine', [TgxAsyncTimer]);
- end;
- end.
|