For example from thread provided by callback from CreateTimerQueueTimer in executable or dll? It is significant to have same thread id as main thread.
procedure TMyMainClass.ExecuteMe(someparam: paramtype);
begin
{something}
end;
and
procedure TimerCallback(pvContext: pointer; fTimerOrWaitFired: boolean); stdcall;
begin
{do what ?}
end;
Final update:
All this stuff (TThread.Synchronize, TThread.Queue, PostThreadMessage etc) works through messages. So be sure host application of your dll processing messages while waiting for callback.
To execute code in the main thread, without access to a TThread instance, call the class methods TThread.Synchronize or TThread.Queue.
If you happen to be using an old Delphi compiler that does not have those methods, then SendMessage or PostMessage with a user defined message are the simplest solution.
Related
When I run this code:
FMyThread := TThread.createAnonymousThread(
procedure
begin
while not FMyThread.CheckTerminated do begin
sleep(750);
TThread.synchronize(nil,
procedure
begin
if FMyThread.CheckTerminated then exit;
....
end);
end;
end);
FMyThread.start;
I have the exception
Cannot call CheckTerminated on an externally created thread
Why ?
If you look at the implementation of CheckTerminated, you will notice it's not a method but a static class method.
CheckTerminated indicates if the currently executing thread has its Terminated flag set to TRUE. When you call FMyThread.CheckTerminated inside TThread.synchronize, , it doesn't check FMyThread.Terminated, it tries to check the state of "GetCurrentThreadId", which in this case is your process' main thread.
Since the main thread isn't created by a TThread, the RTL creates a TExternalThread (See class function TThread.GetCurrentThread: TThread)
In other words, if you have a TThread object and want to check whether it's terminated or not, you should call Terminated, not CheckTerminated.
Since AnonymousThread are by default freed on terminate, you need to make sure you clear any reference to your thread in a OnTerminate handler.
Under firemonkey, When i want to execute some code after the current "cycle", I do like this :
TThread.createAnonymousThread(
procedure
begin
TThread.queue(nil,
procedure
begin
domycode
end);
end).start;
because if we are in the mainThread, then TThread.queue will execute the code immediatly. I m curious if their is not another way to do this than using a thread ?
In 10.2 Tokyo, a new TThread.ForceQueue() method was added to address RSP-15427 (Add an option to let TThread.Queue() run asynchronously when called by the main UI thread):
TThread.ForceQueue(nil,
procedure
begin
domycode
end
);
No thread is needed.
Prior to Tokyo, you would have to re-write the code if you don't want to use an anonymous thread to call TThread.Queue(). For instance, you could post yourself a delayed message with PostMessage() or PostThreadMessage(), and then do the work in the message handler. Or use the TApplication(Events).OnIdle event, like GolezTrol suggested.
I am using CreateAnonymousThread for a worker task, and when I started with it I used Synchronize within the entire declaration as per documented examples, e.g:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here before worker stuff
NotifyBeforeWorkerStuff;
end);
// Do worker stuff
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
// Here after worker stuff
NotifyAfterWorkerStuff;
end);
end);
FExecutionThread.Start;
end;
end;
As you see, from within this thread I launch event notifications to various parts of my app including VCL forms (NotifyBeforeWorkerStuff etc).
Later, I saw that I could move Synchronize() more locally to each VCL form close to the point that actually required it for updating (non-safe) VCL controls:
procedure TSomeVCLForm.ReceiveNotification;
begin
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
Label1.Caption := GetSomeStringFunction;
end);
end;
The worker thread then becomes simpler as long as I live with notifications being from either main or worker threads:
procedure Txxx.RunWorker;
begin
FExecutionThread := TThread.CreateAnonymousThread(procedure ()
begin
NotifyBeforeWorkerStuff;
// Do worker stuff
NotifyAfterWorkerStuff;
end);
FExecutionThread.Start;
end;
I have several questions about whether this is correct:
My notifications may be from the worker thread but also from the main thread (e.g derived from a button press). So, when 'ReceiveNotification' on a VCL form is called from the main thread, is it allowed to call TThread.Synchronize as above? The the XE8 docs imply not, but checking System.Classes this looks ok and it works fine.
Within 'ReceiveNotification' when Label1.Caption fetches the string from GetSomeStringFunction, is it correct that there is absolutely no need for locking within that function even when the call is from a worker thread?
Thanks for any advice.
The documentation says:
Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.
I think that documentation is simply wrong. The implementation in XE8 checks whether or not the current thread is the main thread. If it is then the method is executed directly.
No locking is required in ReceiveNotification because the call to GetSomeStringFunction is always performed on the main thread.
I have a problem that code inside my dll is acting different compared to the same code within a normal application. After some debugging I found that the thread's OnTerminate is never called within the dll.
type
TTest = class
private
public
procedure threadStart();
procedure threadEnd(Sender: TObject);
procedure lines(value: String);
end;
procedure TTest.threadStart();
var aThread : TThread;
begin
aThread :=
TThread.CreateAnonymousThread(
procedure
begin
lines('start')
end
);
aThread.FreeOnTerminate := True;
aThread.OnTerminate := self.threadEnd;
aThread.Start;
end;
procedure TTest.threadEnd;
begin
lines('end')
end;
procedure TTest.lines(value: String);
var MyText: TStringlist;
begin
MyText:= TStringlist.create;
MyText.Add(value);
MyText.SaveToFile('.\filename.txt');
MyText.Free
end;
If I run this code from a normal VLC Delphi Application, I get end in the text file.
If I run the same code from a dll (loading it either static or dynamic into a VLC Application), I get start in the text file.
My question: Why? Or better asked, how can I let my dll act the same way as my VLC. Current version I'm using is XE7.
The TThread.OnTerminate event is triggered in the context of the main UI thread via a call to TThread.Synchronize(), which stores requests in a queue that the main UI thread checks periodically, executing pending requests when available.
If the DLL and EXE are compiled with Runtime Packages enabled, they share a single copy of the RTL (and thus require you to deploy rtl.bpl with your app). When the EXE checks the RTL's Synchronize() queue, it will see pending requests from both EXE and DLL.
However, if they are not sharing a single RTL, then they will be compiled with separate copies of the RTL that are not linked to each other. By default, there is nothing in the EXE that checks and processes pending requests from the DLL's Synchronize() queue, only from the EXE's Synchronize() queue. To address that, you have to export a function from the DLL that calls the CheckSynchronize() function of the DLL's RTL, and then make the EXE call that exported DLL function periodically, such as in a timer.
Otherwise, the other way to get around this problem is to bypass the Synchronize() call that triggers the OnTerminate event, by overriding the thread's virtual DoTerminate() method (which you cannot do with TThread.CreateAnonymousThread()). You can have DoTerminate() call OnTerminate directly, or just do what you need inside of DoTerminate() itself. But either way, you have to make sure this code is thread safe, as DoTerminate() runs in the context of the worker thread.
I created a class that opens a COM port and handles overlapped read and write operations. It contains two independent threads - one that reads and one that writes data. Both of them call OnXXX procedures (eg OnRead or OnWrite) notifying about finished read or write operation.
The following is a short example of the idea how the threads work:
TOnWrite = procedure (Text: string);
TWritingThread = class(TThread)
strict private
FOnWrite: TOnWrite;
FWriteQueue: array of string;
FSerialPort: TAsyncSerialPort;
protected
procedure Execute; override;
public
procedure Enqueue(Text: string);
{...}
end;
TAsyncSerialPort = class
private
FCommPort: THandle;
FWritingThread: TWritingThread;
FLock: TCriticalSection;
{...}
public
procedure Open();
procedure Write(Text: string);
procedure Close();
{...}
end;
var
AsyncSerialPort: TAsyncSerialPort;
implementation
{$R *.dfm}
procedure OnWrite(Text: string);
begin
{...}
if {...} then
AsyncSerialPort.Write('something');
{...}
end;
{ TAsyncSerialPort }
procedure TAsyncSerialPort.Close;
begin
FLock.Enter;
try
FWritingThread.Terminate;
if FWritingThread.Suspended then
FWritingThread.Resume;
FWritingThread.WaitFor;
FreeAndNil(FWritingThread);
CloseHandle(FCommPort);
FCommPort := 0;
finally
FLock.Leave;
end;
end;
procedure TAsyncSerialPort.Open;
begin
FLock.Enter;
try
{open comm port}
{create writing thread}
finally
FLock.Leave;
end;
end;
procedure TAsyncSerialPort.Write(Text: string);
begin
FLock.Enter;
try
{add Text to the FWritingThread's queue}
FWritingThread.Enqueue(Text);
finally
FLock.Leave;
end;
end;
{ TWritingThread }
procedure TWritingThread.Execute;
begin
while not Terminated do
begin
{GetMessage() - wait for a message informing about a new value in the queue}
{pop a value from the queue}
{write the value}
{call OnWrite method}
end;
end;
When you look at the Close() procedure, you will see that it enters the critical section, terminates the writing thread and then waits for it to finish.
Because of the fact that the writing thread can enqueue a new value to be written when it calls the OnWrite method, it will try to enter the same critical section when calling the Write() procedure of the TAsyncSerialPort class.
And here we've got a deadlock. The thread that called the Close() method entered the critical section and then waits for the writing thread to be closed, while at the same time that thread waits for the critical section to be freed.
I've been thinking for quite a long time and I didn't manage to find a solution to that problem. The thing is that I would like to be sure that no reading/writing thread is alive when the Close() method is left, which means that I cannot just set the Terminated flag of those threads and leave.
How can I solve the problem? Maybe I should change my approach to handling serial port asynchronously?
Thanks for your advice in advance.
Mariusz.
--------- EDIT ----------
How about such a solution?
procedure TAsyncSerialPort.Close;
var
lThread: TThread;
begin
FLock.Enter;
try
lThread := FWritingThread;
if Assigned(lThread) then
begin
lThread.Terminate;
if lThread.Suspended then
lThread.Resume;
FWritingThread := nil;
end;
if FCommPort <> 0 then
begin
CloseHandle(FCommPort);
FCommPort := 0;
end;
finally
FLock.Leave;
end;
if Assigned(lThread) then
begin
lThread.WaitFor;
lThread.Free;
end;
end;
If my thinking is correct, this should eliminate the deadlock problem. Unfortunately, however, I close the comm port handle before the writing thread is closed. This means that when it calls any method that takes the comm port handle as one of its arguments (eg Write, Read, WaitCommEvent) an exception should be raised in that thread. Can I be sure that if I catch that exception in that thread it will not affect the work of the whole application? This question may sound stupid, but I think some exceptions may cause the OS to close the application that caused it, right? Do I have to worry about that in this case?
Yes, you should probably reconsider your approach. Asynchronous operations are available exactly to eliminate the need for threads. If you use threads, then use synchronous (blocking) calls. If you use asynchronous operations, then handle everything in one thread - not necessarily the main thread, but it doesn't make sense IMO to do the sending and receiving in different threads.
There are of course ways around your synchronization problem, but I'd rather change the design.
You can take the lock out of the Close. By the time it returns from the WaitFor, the thread body has noticed it has been terminated, completed the last loop, and ended.
If you don't feel happy doing this, then you could move setting the lock just before the FreeAndNil. This explicitly lets the thread shutdown mechanisms work before you apply the lock (so it won't have to compete with anything for the lock)
EDIT:
(1) If you also want to close the comms handle do it after the loop in the Execute, or in the thread's destructor.
(2) Sorry, but your edited solution is a terrible mess. Terminate and Waitfor will do everything you need, perfectly safely.
The main problem seems to be that you place the entire content of Close in a critical section. I'm almost sure (but you'll have to check the docs) that TThread.Terminate and TThread.WaitFor are safe to call from outside the section. By pulling that part outside the critical section you will solve the deadlock.