Recent

Author Topic: A quirk in TTHREAD.Terminate (FPC 3.2.2)  (Read 2193 times)

jamie

  • Hero Member
  • *****
  • Posts: 7660
A quirk in TTHREAD.Terminate (FPC 3.2.2)
« on: February 28, 2026, 09:48:29 pm »
I was testing something today and noticed there is a quirk in terminating a Thread that has the FreeOnTermate set to True.

I create the thread.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   TheThread := TMyThread.Create(True);
  4.   TheThread.FreeOnTerminate :=True;
  5.   TheThread.AMethodToExec := @ShowTime;
  6. end;                      
  7.  

I have a button on the form "START"

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   theThread.Start;
  4. end;  
  5.  

and when the form closes
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. begin
  3.   TheThread.Terminate;
  4. end;    
  5.  

This all works great when I START the thread.

However, If I don't start the thread there is an issue and that is, when calling Terminate, it creates memory leaks detected via HeapTrc.

On top of that, I can't seem to devise a way to determine if a thread is actually running before calling Terminate where I can instead just free it.
             
Shouldn't "Terminate" be able to determine if the thread is running and just simply FREE if not?

Jamie
The only true wisdom is knowing you know nothing

cdbc

  • Hero Member
  • *****
  • Posts: 2725
    • http://www.cdbc.dk
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #1 on: February 28, 2026, 09:53:29 pm »
Hi
Have you checked 'somethread.Finished'?!?

edit: Otherwise I'd say the sequence is like this:
· Terminate;
· Start;
· Waitfor;
· Free;
The reason ofc. being that you created the thread 'suspended', I don't think you can just free it...

Regards Benny
« Last Edit: February 28, 2026, 10:00:51 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Xenno

  • Jr. Member
  • **
  • Posts: 87
    • BS Programs
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #2 on: February 28, 2026, 10:04:26 pm »
Terminate method doesn't terminate the thread. It only sets Terminated property to True. So, if the thread was not started, memory is allocated but not running, so we have to call Free.
Lazarus 4.0, Windows 10, https://www.youtube.com/@bsprograms

jamie

  • Hero Member
  • *****
  • Posts: 7660
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #3 on: February 28, 2026, 10:09:14 pm »
If I don't use the TerminateOnFree then I can simply call the FREE however, I then need to stop the thread in that case.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. begin
  3.   TheThread.Terminate;
  4.   TheThread.Free;
  5.  
  6. end;                                      
  7.  
  8.  

That works either way when not using the FreeOnTerminate settings.

So, I conclude there is a quirk using the FreeOnTerminate settings on a thread that never gets started, also, if you use FREE on that setting, it faults out.

 So, there are issues when using the FreeOnTerminate.

Jamie

The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 7660
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #4 on: February 28, 2026, 10:09:59 pm »
Terminate method doesn't terminate the thread. It only sets Terminated property to True. So, if the thread was not started, memory is allocated but not running, so we have to call Free.

Please read closer

Jamie
The only true wisdom is knowing you know nothing

Xenno

  • Jr. Member
  • **
  • Posts: 87
    • BS Programs
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #5 on: February 28, 2026, 10:23:04 pm »
Okay. I am sorry. I admit I've never created a thread which might be not started. 
Lazarus 4.0, Windows 10, https://www.youtube.com/@bsprograms

LeP

  • Full Member
  • ***
  • Posts: 228
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #6 on: March 01, 2026, 12:40:28 pm »
This is not an issue, it's by design.

FreeOnTerminate "works" only if a thread is in running STATE. The Thread SHOULD ALWAYS BE in running state or in waiting state for this to works.

If the thread is in a Waiting state, when the sync object is destroyed (like when you exit the application) the thread will be signaled about that and can free itself.
But this doesn't work if it is in a suspended state. Before exit application (or do other thing with the thread) if the thread is suspended you have to "resume" it ... ALWAYS.

For this and for other questions (see the topic about Thread.Suspend, now locked  :( ) that I never use the thread in suspend state.
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

jamie

  • Hero Member
  • *****
  • Posts: 7660
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #7 on: March 01, 2026, 02:19:53 pm »
This is not an issue, it's by design.

FreeOnTerminate "works" only if a thread is in running STATE. The Thread SHOULD ALWAYS BE in running state or in waiting state for this to works.

If the thread is in a Waiting state, when the sync object is destroyed (like when you exit the application) the thread will be signaled about that and can free itself.
But this doesn't work if it is in a suspended state. Before exit application (or do other thing with the thread) if the thread is suspended you have to "resume" it ... ALWAYS.

For this and for other questions (see the topic about Thread.Suspend, now locked  :( ) that I never use the thread in suspend state.

I can't see how you think this is not an issue, it is an issue If I want to save a step in code land, I don't always have a thread started but it's there and it needs to be freed when closing the app.

 If I am using the FreeOnTerminate feature, I would expect that calling "terminate" would test for the current state of a running thread and simple set the TERMINATE flag to true, wait for it to terminate and then Free it, or if the thread is already in the STOP mode, then simple free it.

  It's just an extra step that got overloaded apparently.

I'll give up on using that feature because it's not reliable and has holes in it.

 As for the Dead locking a thread, I've only done that just to test the theory, in practice, at least my practice, I've never coded a thread that got locked at least in a production state. If it got locked it was due to bad coding practices on my end.

Jamie

The only true wisdom is knowing you know nothing

LeP

  • Full Member
  • ***
  • Posts: 228
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #8 on: March 01, 2026, 02:34:27 pm »
I know what you mean. But that is the rule.
I think debugging should be treated as a separate topic. It has slightly different rules than normal execution.

As for bad practice, not using the thread according to the specific rules... that's also bad practice.

If you want to use the Thread, those are the rules.
As a workaround, you can always derive or create a different object that works exactly as you want.
And even submit it to FPC.

This is what FPC says:

"FreeOnTerminate, when set to True, indicates that the thread instance will be freed automatically as soon as the thread stops executing."

And if the thread doesn't start, it won't ever stop either.

It works as designed.

N.B.: And this is always about the thread in suspend state ... I think this state should never have existed.
« Last Edit: March 01, 2026, 02:38:50 pm by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

jamie

  • Hero Member
  • *****
  • Posts: 7660
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #9 on: March 01, 2026, 02:42:57 pm »
Its a bad design and one that should be looked at.

In any case, I'll just call FREE ether way since Delphi states calling free will set the Terminate Flag = true. and wait for it.

So, I guess if that fails then there is a compatibility issue. more on that later.


Jamie
The only true wisdom is knowing you know nothing

LeP

  • Full Member
  • ***
  • Posts: 228
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #10 on: March 01, 2026, 03:17:51 pm »
In any case, I'll just call FREE ether way since Delphi states calling free will set the Terminate Flag = true. and wait for it.
I can confirm that Delphi call Terminate when the Thread is destroyed (I mean free call or other means), in suspended state too, and waitfor it.

The "waitfor" it's a new for me ... I always used waitfor in my code ... that's good to know.
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12770
  • FPC developer.
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #11 on: March 01, 2026, 03:44:16 pm »
Its a bad design and one that should be looked at.

In any case, I'll just call FREE ether way since Delphi states calling free will set the Terminate Flag = true. and wait for it.

So, I guess if that fails then there is a compatibility issue. more on that later.

You can't since that would be a race condition. Even if you do

Code: Pascal  [Select][+][-]
  1.    if thread.running then
  2.      thread.terminate;
  3.  

the thread could finish and deallocate between the thread.running and thread.terminate check, and you would execute on a stale reference with unused OS handles and unpredictable results.

So combining freeonterminate and terminate is inherently not  safe.

jamie

  • Hero Member
  • *****
  • Posts: 7660
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #12 on: March 01, 2026, 06:02:13 pm »
Its a bad design and one that should be looked at.

In any case, I'll just call FREE ether way since Delphi states calling free will set the Terminate Flag = true. and wait for it.

So, I guess if that fails then there is a compatibility issue. more on that later.

You can't since that would be a race condition. Even if you do

Code: Pascal  [Select][+][-]
  1.    if thread.running then
  2.      thread.terminate;
  3.  

the thread could finish and deallocate between the thread.running and thread.terminate check, and you would execute on a stale reference with unused OS handles and unpredictable results.

So combining freeonterminate and terminate is inherently not  safe.

The Terminate check point if the code is properly written, should be done in a non-volatile state of the loop, otherwise the loop will not terminate unless done via a hard exit, which isn't good.

The Terminated state needs to be checked in the Threads user code so the proper place for that is in the loop where it makes sense so that all is cleaned up.

The user code can clean up any of the handles that are in the thread before it exit's the Execution code.

I suppose a suspended thread being terminated may not fair to well, but the state is basically the same, it would have to be a hard exit because the terminate state still needs to be tested.

But the point is, if the thread never got started in the first place, freeing it shouldn't be an issue even for the FreeOnTermate if Terminate is called.

I don't know, I've never experienced such oddities using threads, I think it's an understanding of what's going on under the hood.

Jamie


The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 18952
  • Glad to be alive.
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #13 on: March 01, 2026, 06:05:48 pm »
@LeP
Waitfor can cause the same race condition as Marco described: it is not an improvement in combination with calling terminate on a thread from another thread.
It IS an improvement if a thread itself knows it is done. That is about the same as per PascalDragon's comment above in the sense that the thread itself needs a mechanism to know it  should finish and that either takes extra code or the thread itself knows it has finished its job (designed as finite, not infinite).
Recovered from removal of tumor in tongue following tongue reconstruction with a part from my leg.

LeP

  • Full Member
  • ***
  • Posts: 228
Re: A quirk in TTHREAD.Terminate (FPC 3.2.2)
« Reply #14 on: March 01, 2026, 09:56:56 pm »
@LeP
Waitfor can cause the same race condition as Marco described: it is not an improvement in combination with calling terminate on a thread from another thread.
It IS an improvement if a thread itself knows it is done. That is about the same as per PascalDragon's comment above in the sense that the thread itself needs a mechanism to know it  should finish and that either takes extra code or the thread itself knows it has finished its job (designed as finite, not infinite).
I know. But I was referring to the fact that waitfor is internal to the thread's "terminate" routine (ThreadShutdown in Delphi).

Regarding FreeOnTerminate, as stated in the documentation, if a thread has a FreeOnterminate property set to True, it cannot be accessed under any circumstances after creation (or at startup if the creation state is suspended).
So there can't be any problem: the thread shouldn't be accessed (in fact, it MUST NOT be accessed) because it can "disappear" at any time after creation or startup.

You can't use Free, Terminate, WaitFor, or any other property. Only the thread's user code can decide whether and how to exit, and if the rules are followed, there shouldn't be any problems.
Is the thread blocking? That's a programming error, not a design error.

And if freeonterminate is FALSE, in Delphi the THREAD can always be terminated with a FREE, without any further action, even in suspended state. Except when the thread is in a waiting state (as far as I know) and that can be the issue (if one don't use the right way).

As for states, it's not possible to correctly manage states from user code/internal class code/operating system code anyway.

Three different entities can manage the same object in a non-cooperative manner: rules must be found and applied, there are no alternatives.

And the very fact of needing to know the FreeOnTerminate property is a contradiction: how can one know this property if, when that is true, one cannot access the Thread (externally)?
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

 

TinyPortal © 2005-2018