Componentes.Terceros.jvcl/official/3.39/run/JvThreadTimer.pas
2010-01-18 16:55:50 +00:00

355 lines
8.9 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvThreadTimer.PAS, released on 2001-02-28.
The Initial Developer of the Original Code is S?stien Buysse [sbuysse att buypin dott com]
Portions created by S?stien Buysse are Copyright (C) 2001 S?stien Buysse.
All Rights Reserved.
Contributor(s):
Michael Beck [mbeck att bigfoot dott com].
Peter Thrnqvist
Ivo Bauer
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.delphi-jedi.org
Known Issues:
History:
2003-07-24 (p3)
* Changed Active->Enabled and Delay->Interval to make property names match TTimer
* Changed implementation so that setting Enabled := false, frees the thread instead
of suspending it. This makes it possible to restart the timer interval.
2003-07-25 (ivobauer)
* Rewritten almost everything.
-----------------------------------------------------------------------------}
// $Id: JvThreadTimer.pas 12501 2009-09-14 20:59:58Z ahuser $
unit JvThreadTimer;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
QWindows,
{$ENDIF UNIX}
SysUtils, Classes,
JvTypes, JvComponentBase;
type
TJvThreadTimer = class(TJvComponent)
private
FEnabled: Boolean;
FInterval: Cardinal;
FKeepAlive: Boolean;
FOnTimer: TNotifyEvent;
FPriority: TThreadPriority;
FStreamedEnabled: Boolean;
FThread: TThread;
function GetThread: TThread;
procedure SetEnabled(const Value: Boolean);
procedure SetInterval(const Value: Cardinal);
procedure SetOnTimer(const Value: TNotifyEvent);
procedure SetPriority(const Value: TThreadPriority);
procedure SetKeepAlive(const Value: Boolean);
protected
procedure DoOnTimer;
procedure Loaded; override;
procedure StopTimer;
procedure UpdateTimer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Thread: TThread read GetThread;
published
// (p3) renamed Active->Enabled, Delay->Interval to make it compatible with TTimer
property Enabled: Boolean read FEnabled write SetEnabled default False;
property Interval: Cardinal read FInterval write SetInterval default 1000;
property KeepAlive: Boolean read FKeepAlive write SetKeepAlive default False;
property OnTimer: TNotifyEvent read FOnTimer write SetOnTimer;
property Priority: TThreadPriority read FPriority write SetPriority
{$IFDEF MSWINDOWS} default tpNormal {$ENDIF};
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvThreadTimer.pas $';
Revision: '$Revision: 12501 $';
Date: '$Date: 2009-09-14 22:59:58 +0200 (lun., 14 sept. 2009) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
Messages,
JvJCLUtils;
type
TJvTimerThread = class(TThread)
private
FEvent: THandle;
FHasBeenSuspended: Boolean;
FInterval: Cardinal;
FTimer: TJvThreadTimer;
FPriority: TThreadPriority;
protected
procedure DoSuspend;
procedure Execute; override;
public
constructor Create(ATimer: TJvThreadTimer);
destructor Destroy; override;
procedure Stop;
property Interval: Cardinal read FInterval;
property Timer: TJvThreadTimer read FTimer;
end;
function SubtractMin0(const Big, Small: Cardinal): Cardinal;
begin
if Big <= Small then
Result := 0
else
Result := Big - Small;
end;
//=== { TJvTimerThread } =====================================================
constructor TJvTimerThread.Create(ATimer: TJvThreadTimer);
begin
inherited Create(False);
FreeOnTerminate := True;
{ Manually reset = false; Initial State = false }
FEvent := CreateEvent(nil, False, False, nil);
if FEvent = 0 then
RaiseLastOSError;
FInterval := ATimer.FInterval;
FTimer := ATimer;
FPriority := ATimer.Priority; // setting the priority is deferred to Execute()
end;
destructor TJvTimerThread.Destroy;
begin
Stop;
inherited Destroy;
if FEvent <> 0 then
CloseHandle(FEvent);
end;
procedure TJvTimerThread.DoSuspend;
begin
FHasBeenSuspended := True;
Suspended := True;
end;
procedure TJvTimerThread.Execute;
var
Offset, TickCount: Cardinal;
begin
Priority := FPriority;
if WaitForSingleObject(FEvent, Interval) <> WAIT_TIMEOUT then
Exit;
while not Terminated do
begin
FHasBeenSuspended := False;
TickCount := GetTickCount;
if not Terminated then
Synchronize(FTimer.DoOnTimer);
// Determine how much time it took to execute OnTimer event handler. Take a care
// of wrapping the value returned by GetTickCount API around zero if Windows is
// run continuously for more than 49.7 days.
if FHasBeenSuspended then
Offset := 0
else
begin
Offset := GetTickCount;
if Offset >= TickCount then
Dec(Offset, TickCount)
else
Inc(Offset, High(Cardinal) - TickCount);
end;
// Make sure Offset is less than or equal to FInterval.
// (rb) Ensure it's atomic, because of KeepAlive
if WaitForSingleObject(FEvent, SubtractMin0(Interval, Offset)) <> WAIT_TIMEOUT then
Exit;
end;
end;
procedure TJvTimerThread.Stop;
begin
Terminate;
SetEvent(FEvent);
if Suspended then
Suspended := False;
Sleep(0);
end;
//=== { TJvThreadTimer } =====================================================
constructor TJvThreadTimer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FInterval := 1000;
{$IFDEF MSWINDOWS}
FPriority := tpNormal;
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
FPriority := 0;
{$ENDIF UNIX}
end;
destructor TJvThreadTimer.Destroy;
begin
StopTimer;
inherited Destroy;
end;
procedure TJvThreadTimer.DoOnTimer;
begin
if csDestroying in ComponentState then
Exit;
try
if Assigned(FOnTimer) then
FOnTimer(Self);
except
if Assigned(ApplicationHandleException) then
ApplicationHandleException(Self);
end;
end;
function TJvThreadTimer.GetThread: TThread;
begin
Result := FThread;
end;
procedure TJvThreadTimer.Loaded;
begin
inherited Loaded;
SetEnabled(FStreamedEnabled);
end;
procedure TJvThreadTimer.SetEnabled(const Value: Boolean);
begin
if csLoading in ComponentState then
FStreamedEnabled := Value
else
if FEnabled <> Value then
begin
FEnabled := Value;
UpdateTimer;
end;
end;
procedure TJvThreadTimer.SetInterval(const Value: Cardinal);
begin
if FInterval <> Value then
begin
FInterval := Value;
UpdateTimer;
end;
end;
procedure TJvThreadTimer.SetKeepAlive(const Value: Boolean);
begin
if FKeepAlive <> Value then
begin
StopTimer;
FKeepAlive := Value;
UpdateTimer;
end;
end;
procedure TJvThreadTimer.SetOnTimer(const Value: TNotifyEvent);
begin
if @FOnTimer <> @Value then
begin
FOnTimer := Value;
UpdateTimer;
end;
end;
procedure TJvThreadTimer.SetPriority(const Value: TThreadPriority);
begin
if FPriority <> Value then
begin
FPriority := Value;
if FThread <> nil then
FThread.Priority := FPriority;
end;
end;
procedure TJvThreadTimer.StopTimer;
begin
if FThread is TJvTimerThread then
TJvTimerThread(FThread).Stop;
FThread := nil;
end;
procedure TJvThreadTimer.UpdateTimer;
var
DoEnable: Boolean;
begin
if ComponentState * [csDesigning, csLoading] <> [] then
Exit;
DoEnable := FEnabled and Assigned(FOnTimer) and (FInterval > 0);
if not KeepAlive then
StopTimer;
if DoEnable then
begin
if FThread is TJvTimerThread then
TJvTimerThread(FThread).FInterval := FInterval
else
FThread := TJvTimerThread.Create(Self);
if FThread.Suspended then
FThread.Suspended := False;
end
else
if FThread is TJvTimerThread then
begin
if not FThread.Suspended then
TJvTimerThread(FThread).DoSuspend;
TJvTimerThread(FThread).FInterval := FInterval;
end;
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.