Note that there are some explanatory texts on larger screens.

plurals
  1. PODeadlock when closing thread
    primarykey
    data
    text
    <p>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. </p> <p>The following is a short example of the idea how the threads work:</p> <pre><code> 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; </code></pre> <p>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. </p> <p>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.</p> <p>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.</p> <p>How can I solve the problem? Maybe I should change my approach to handling serial port asynchronously?</p> <p>Thanks for your advice in advance.</p> <p>Mariusz.</p> <p>--------- EDIT ----------<br/> How about such a solution?</p> <pre><code>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 &lt;&gt; 0 then begin CloseHandle(FCommPort); FCommPort := 0; end; finally FLock.Leave; end; if Assigned(lThread) then begin lThread.WaitFor; lThread.Free; end; end; </code></pre> <p>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?</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload