Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is TExternalThread? "Cannot terminate externally created thread" when terminating a thread-based timer

This happens half of the time when closing my application in which I have placed a TLMDHiTimer on my form in design time, Enabled set to true. In my OnFormClose event, I call MyLMDHiTimer.Enabled := false. When this is called, I sometimes (about half of the time) get this exception.

I debugged and stepped into the call and found that it is line 246 in LMDTimer.pas that gives this error.

FThread.Terminate;

I am using the latest version of LMDTools. I did a complete reinstall of LMD tools before the weekend and have removed and re-added the component to the form properly as well.

From what I've found, this has something to do with TExternalThread, but there's no documentation on it from Embarcadero and I haven't found anything referencing it within the LMDTools source code.

Using fully updated RAD Studio 2010, Delphi 2010.

What really upsets me here is that there's no documentation whatsoever. Google yeilds one result that actually talks about it, in which someone says that the error is caused by trying to terminate a TExternalThread. But looking at the source-code for this LMDHiTimer, not once does it aim to do anything but create a regular TThread. The one google result I could find, Thread: Cannot terminate an externally created thread? on Embarcadero mentions using GetCurrentThread() and GetCurrentThreadId() to get the data necessary to hook on to an existing thread, but the TLMDHiTimer does no such thing. It just creates its own TThread descendant with its own Create() constructor (overridden of course, and calls inherited at the start of the constructor)

So... What the heck is this TExternalThread? Has anyone else run into this kind of exception? And perhaps figured out a solution or workaround? I've asked almost the exact same question to LMDTools' own support, but it can't hurt to ask in multiple places.

Thank you in advance for any assistance.

like image 620
Michael Stahre Avatar asked Feb 08 '10 13:02

Michael Stahre


1 Answers

TExternalThread wraps a thread that the Delphi RTL didn't create. It might represent a thread belonging to the OS thread pool, or maybe a thread created by another DLL in your program. Since the thread is executing code that doesn't belong to the associated TExternalThread class, the Terminate method has no way to notify the thread that you want it to stop.

A Delphi TThread object would set its Terminated property to True, and the Execute method that got overridden would be expected to check that property periodically, but since this thread is non-Delphi code, there is no Execute method, and any Terminated property only came into existence after the thread code was already written someplace else (not by overriding Execute).

The newsgroup thread suggests what's probably happening in your case:

... you have corrupted memory that causes the TThread.FExternalThread member to become a non-zero value.

It might be due to a bug in the component library, or it could be due to a bug in your own code. You can use the debugger's data breakpoints to try to find out. Set a breakpoint in the timer's thread's constructor. When your program pauses there, use the "Add Breakpoint" command on the Run menu to add a data breakpoint using the address of the new object's FExternalThread field. Thereafter, if that field's value changes, the debugger will pause and show you what changed it. (The data breakpoint will get reset each time you run the program because the IDE assumes the object won't get allocated at the same address each time.)

like image 148
Rob Kennedy Avatar answered Sep 20 '22 12:09

Rob Kennedy