Recent

Author Topic: Random freeze with fpTimer  (Read 21478 times)

balazsszekely

  • Guest
Re: Random freeze with fpTimer
« Reply #15 on: February 07, 2015, 08:42:32 pm »
@Tharon

Try this (it's not a visual component, but it should work on windows, linux and osx):

Code: [Select]
unit uThreadTimer;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, lclintf;

type
  TOnTimer = procedure(Sender: TObject) of object;

  { TThreadTimer }

  TThreadTimer = class(TThread)
  private
    FTime: QWORD;
    FInterval: Cardinal;
    FOnTimer: TOnTimer;
    FEnabled: Boolean;
    procedure DoOnTimer;
  protected
    procedure Execute; override;
  public
    property OnTimer: TOnTimer read FOnTimer write FOnTimer;
    property Interval: Cardinal read FInterval write FInterval;
    property Enabled: Boolean read FEnabled write FEnabled;
    procedure StopTimer;
    procedure StartTimer;
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
  end;


implementation

constructor TThreadTimer.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  FInterval := 1000;
  FreeOnTerminate := True;
  FEnabled := False;
end;

destructor TThreadTimer.Destroy;
begin
  //
  inherited Destroy;
end;

procedure TThreadTimer.DoOnTimer;
begin
  if Assigned(FOnTimer) then
    FOnTimer(Self);
end;

procedure TThreadTimer.Execute;
begin
  while not Terminated do
  begin
    Sleep(1);
    if (GetTickCount64 - FTime > FInterval) and (FEnabled) then
    begin
      FTime := GetTickCount64;
      Synchronize(@DoOnTimer);
    end;
  end;
end;

procedure TThreadTimer.StopTimer;
begin
  FEnabled := False;
end;

procedure TThreadTimer.StartTimer;
begin
  FTime := GetTickCount64;
  FEnabled := True;
  if Self.Suspended then
    Start;
end;

end.

Don't forget to add the following lines to project source:
Code: [Select]
program project1;

{$mode objfpc}{$H+}

{$DEFINE UseCThreads}  //add this line
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}  //add this line
  cthreads,    //add this line
  {$ENDIF}{$ENDIF} //add this line 
  //...

Main form:
Code: [Select]
uses uThreadTimer, lclintf;

var
  ThreadTimer: TThreadTimer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThreadTimer := TThreadTimer.Create(True);
  ThreadTimer.Interval := 1000;
  ThreadTimer.OnTimer := @OnTimer;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if ThreadTimer.Enabled then
    ThreadTimer.StopTimer;
  ThreadTimer.Terminate;
end;

procedure TForm1.bStartStopClick(Sender: TObject);
begin
  if ThreadTimer.Enabled then
    ThreadTimer.StopTimer
  else
    ThreadTimer.StartTimer;
end;

procedure TForm1.OnTimer(Sender: TObject);
begin
  Label1.Caption := IntToStr(GetTickCount64())
end;


Please run a few test and tell me if it's working as it should.
« Last Edit: February 07, 2015, 08:54:46 pm by GetMem »

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #16 on: February 13, 2015, 02:54:11 pm »
@Getmem.
Your code have even more problem than fptimer. Always crashes (sigsegv) when freed.

balazsszekely

  • Guest
Re: Random freeze with fpTimer
« Reply #17 on: February 13, 2015, 03:53:32 pm »
Quote
Your code have even more problem than fptimer. Always crashes (sigsegv) when freed.

Why do you want to free it? Take a look at the constructor:
Code: [Select]
constructor TThreadTimer.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  FInterval := 1000;
  FreeOnTerminate := True; //<<--- FreeOnTerminate it's set to true
  FEnabled := False;
end;

All you have to do is to call Terminate:
Code: [Select]
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if ThreadTimer.Enabled then
    ThreadTimer.StopTimer;
  ThreadTimer.Terminate; //<--this will free the timer
end;

I test it extensively and it's working just fine(both windows and linux). Not a single sigsegv, not even on osx.

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #18 on: February 14, 2015, 01:49:37 am »
Ok, i missed that part.

But i don't like to have to remember different behaviour of what i use. And i usually always free everything i create.

Is the FreeOnTerminate := True really necessary or can i remove that line and manually free the timer ?
« Last Edit: February 14, 2015, 02:04:57 am by Tharon »

balazsszekely

  • Guest
Re: Random freeze with fpTimer
« Reply #19 on: February 14, 2015, 05:10:23 am »
Quote
Is the FreeOnTerminate := True really necessary or can i remove that line and manually free the timer ?
In this particular case you should really stick to FreeOnTerminate := True, otherwise bad things start to happen(see fpTimer and the previous posts)

Quote
But i don't like to have to remember different behaviour of what i use. And i usually always free everything i create.
Instead of ThreadTimer.Free  you call ThreadTimer.Terminate. In my opinion it's not so hard to remember. It's not like you have to solve a differential equation  :D

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #20 on: February 15, 2015, 02:34:02 pm »
Thank You GetMem, i'll use it if i fail with my tests with fpTimer.

For now, i have to admit that engkin's fix seems to works after all. The problem was within my code.

In the timer cycle my program read messages from another application, one of them is an halt signal that stop the program and close the form.

Here is the problem : closing the form occours inside the timer, so it freezes since i'm basically freeing the timer inside itself.

I have to find a workaround to let the program close outside the timer after the halt signal has been received.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Random freeze with fpTimer
« Reply #21 on: February 15, 2015, 03:09:02 pm »
... engkin's fix seems to works after all.

Thanks for reporting again.  :) In this case I might reactivate the 2010 bug report with a new patch if needed.

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #22 on: February 15, 2015, 04:59:18 pm »
Engkin, the issue was (or is) really random, so is pretty difficult to uncover it.

I discovered the logic error in my code only because it happened every time the form was closed by remote (but i usually don't do it, this is why i got confused).

After a workaround (i'm using a global flag and a TTimer to check it and close the form) i didn't have problems with it anymore. If i encounter another freeze issue i will report here :)

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Random freeze with fpTimer
« Reply #23 on: February 15, 2015, 05:35:25 pm »
Yes, you are right and I better wait for a while before posting anything to the bug report. Thank you.  :)

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #24 on: March 02, 2015, 12:32:35 pm »
@GetMem

There is an issue with your timer.

If created with the suspended flag true and never started it will generate a memory leak once terminated.
« Last Edit: March 02, 2015, 12:36:14 pm by Tharon »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Random freeze with fpTimer
« Reply #25 on: March 02, 2015, 11:01:19 pm »
If you are on *nix, you DO import cthreads as first unit of your main program (.lpr) ?

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #26 on: March 03, 2015, 03:50:33 pm »
I'm on windows.

balazsszekely

  • Guest
Re: Random freeze with fpTimer
« Reply #27 on: March 03, 2015, 07:25:55 pm »
Quote
@Tharon
There is an issue with your timer.
If created with the suspended flag true and never started it will generate a memory leak once terminated.

1. Create the timer only when you need it:
Code: [Select]
var
  ThreadTimer: TThreadTimer = nil;

//...

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if ThreadTimer <> nil then
  begin
    if ThreadTimer.Enabled then
      ThreadTimer.StopTimer;
    ThreadTimer.Terminate;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if ThreadTimer = nil then
  begin
    ThreadTimer := TThreadTimer.Create(True);
    ThreadTimer.Interval := 1000;
    ThreadTimer.OnTimer := @OnTimer;
  end;
  if ThreadTimer.Enabled then
    ThreadTimer.StopTimer
  else
    ThreadTimer.StartTimer;
end;                                   

2. Start the timer after you created:
Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin
  ThreadTimer := TThreadTimer.Create(True);
  ThreadTimer.Interval := 1000;
  ThreadTimer.OnTimer := @OnTimer;
  ThreadTimer.Start;//add this line
end; 
On timer won't be called because FEnabled is false. I don't have time to test it, but both solution should work.

« Last Edit: March 03, 2015, 08:36:21 pm by GetMem »

Tharon

  • Jr. Member
  • **
  • Posts: 61
Re: Random freeze with fpTimer
« Reply #28 on: March 04, 2015, 05:26:26 pm »
Your timer need a lot of workarounds and things to rember in order to work as it should, don't you think ? :D

Also, it seems to fire randomly at least once after it has been stopped, but i need more tests on it.

balazsszekely

  • Guest
Re: Random freeze with fpTimer
« Reply #29 on: March 04, 2015, 05:52:38 pm »
Code: [Select]
@Tharon
Your timer need a lot of workarounds and things to rember in order to work as it should, don't you think ?
I coded in 15-20 min, when I saw this thread. I'm not saying it's perfect, you can further improve it.

 

TinyPortal © 2005-2018