unit uMyTimer;

interface

uses
  System.Classes, System.SyncObjs, System.SysUtils;

type
  TTimerExec = procedure(Sender: TObject; const txt: string; o: Boolean) of object;

  TMyTimerThread = class(TThread)
  private
    FEvent: TEvent;
    FCriticalSection: TCriticalSection;
    FInterval: Integer;
    FText: string;
    FFlagO: Boolean;
    FEnabled: Boolean;
    FOnTimerExec: TTimerExec;
    procedure SyncTimerEvent;
  protected
    procedure Execute; override;
  public
    constructor Create(AIntervalMinutes: Integer; const AText: string; AFlagO: Boolean);
    destructor Destroy; override;
    procedure StartT;
    procedure StopT;
    procedure TerminateAndDestroy;
    procedure Update(AIntervalMinutes: Integer; const AText: string; AFlagO: Boolean);
    property OnTimerExec: TTimerExec read FOnTimerExec write FOnTimerExec;
  end;

implementation

{ TMyTimerThread }

constructor TMyTimerThread.Create(AIntervalMinutes: Integer; const AText: string; AFlagO: Boolean);
begin
  inherited Create(True);
  FreeOnTerminate := False;
  FEvent := TEvent.Create(nil, True, False, '');
  FCriticalSection := TCriticalSection.Create;
  FInterval := AIntervalMinutes * 60 * 1000;
  FText := AText;
  FFlagO := AFlagO;
  FEnabled := False;
end;

destructor TMyTimerThread.Destroy;
begin
  StopT;
  Terminate;
  FEvent.SetEvent;
  if not Suspended then
    WaitFor;
  FreeAndNil(FEvent);
  FreeAndNil(FCriticalSection);
  inherited;
end;

procedure TMyTimerThread.Execute;
var
  WaitResult: TWaitResult;
  LocalInterval: Integer;
begin
  while not Terminated do
  begin
    FCriticalSection.Enter;
    try
      if FEnabled then
        LocalInterval := FInterval
      else
        LocalInterval := INFINITE;
    finally
      FCriticalSection.Leave;
    end;

    WaitResult := FEvent.WaitFor(LocalInterval);

    FCriticalSection.Enter;
    try
      if FEnabled and (WaitResult = wrTimeout) then
      begin
        if Assigned(FOnTimerExec) then
          SyncTimerEvent;
      end;
    finally
      FCriticalSection.Leave;
    end;

    FEvent.ResetEvent;
  end;
end;

procedure TMyTimerThread.StartT;
begin
  FCriticalSection.Enter;
  try
    FEnabled := True;
    Suspended:=false;
  finally
    FCriticalSection.Leave;
  end;
  if Suspended then
    Start;
  FEvent.SetEvent;
end;

procedure TMyTimerThread.StopT;
begin
  FCriticalSection.Enter;

  try
    FEnabled := False;
    Suspended:=true;
  finally
    FCriticalSection.Leave;
  end;
  FEvent.SetEvent;
end;

procedure TMyTimerThread.SyncTimerEvent;
var
  LText: string;
  LFlag: Boolean;
begin
  FCriticalSection.Enter;
  try
    LText := FText;
    LFlag := FFlagO;
  finally
    FCriticalSection.Leave;
  end;
  if Assigned(FOnTimerExec) then
    FOnTimerExec(Self, LText, LFlag);
end;

procedure TMyTimerThread.TerminateAndDestroy;
begin
StopT;
  Terminate;
  Free;
end;

procedure TMyTimerThread.Update(AIntervalMinutes: Integer; const AText: string; AFlagO: Boolean);
begin
  FCriticalSection.Enter;
  try
    FInterval := AIntervalMinutes * 60 * 1000;
    FText := AText;
    FFlagO := AFlagO;
  finally
    FCriticalSection.Leave;
  end;
  FEvent.SetEvent;
end;

end.
