Stream error: handle invalid (6) while trying to release a suspended stream

In this example, I get an exception when calling AThread.Free.

 program Project44; {$APPTYPE CONSOLE} uses SysUtils, Classes, Windows; type TMyException = class(Exception); var AThread: TThread; begin AThread := TThread.Create(True); try AThread.FreeOnTerminate := True; //I want to do some things here before starting the thread //During the setup phase some exception might occur, this exception is for simulating purpouses raise TMyException.Create('exception'); except AThread.Free; //Another exception here end; end. 

I have two questions:

  • How do I free an AThread instance of TThread in this example?

  • I do not understand why TThread.Destroy calls Resume before destroying itself. What is the meaning of this?

+8
exception delphi delphi-2009
source share
2 answers

You cannot set FreeOnTerminate to True and call Free on the thread instance. You have to do one or the other, but not both. In this case, your code destroys the stream twice. You should never destroy an object twice and, of course, when the destructor runs a second time, errors occur.

What happens is that nothing happens from the moment the thread is paused until you explicitly release the thread. When you do this, the destructor will resume the thread, waiting for it to complete. This causes Free called again because you set FreeOnTerminate to True . This second call to Free closes the handle. Then you go back to the proc thread and call ExitThread . This does not work because the stream handle is closed.

As Martin points out in a comment, you should not create TThread directly, since the TThread.Execute method is abstract. In addition, you should not use Resume , which is deprecated. Use Start to start the execution of a suspended thread.

Personally, I do not like to use FreeOnTerminate . Using this function destroys a thread in another thread from which it was created. Usually you use it when you want to forget about the instance link. This leaves you unsure whether the thread was destroyed at the end of the process or even it ends and is freed at the end of the process.

If you must use FreeOnTerminate , you need to make sure that you do not call Free after setting FreeOnTerminate to True . Therefore, the obvious solution is to set FreeOnTerminate to True just before calling Start , and then forget about the thread instance. If you have any exceptions, before you are ready to start, you can safely free the thread, since you FreeOnTerminate will still be False at this point.

 Thread := TMyThread.Create(True); Try //initialise thread object Except Thread.Free; raise; End; Thread.FreeOnTerminate := True; Thread.Start; Thread := nil; 

A more elegant approach would be to move all initialization to the TMyThread constructor. Then the code will look like this:

 Thread := TMyThread.Create(True); Thread.FreeOnTerminate := True; Thread.Start; Thread := nil; 
+15
source share

In your case, the situation is very complicated.

Firstly, you are not actually releasing a suspended thread; the thread resumes in the destructor:

  begin Terminate; if FCreateSuspended then Resume; WaitFor; end; 

Since Terminate is called before Resume , the Execute method never starts, and the thread terminates immediately after resuming:

  try if not Thread.Terminated then try Thread.Execute; except Thread.FFatalException := AcquireExceptionObject; end; finally Result := Thread.FReturnValue; FreeThread := Thread.FFreeOnTerminate; Thread.DoTerminate; Thread.FFinished := True; SignalSyncEvent; if FreeThread then Thread.Free; 

Now let's look at the last line - you call the destructor ( Thread.Free ) from the destructor itself! Fantastic mistake!


To answer your questions:

  • You simply cannot use FreeOnTerminate:= True in your code;
  • You should ask Embarcadero why TThread is designed this way; my guess is that some code ( DoTerminate method) should be executed in the context of the thread while the thread is DoTerminate .

You can send a function request to QC: add FFreeOnTerminate:= False to the TThread.Destroy implementation:

 destructor TThread.Destroy; begin FFreeOnTerminate:= False; // everything else is the same .. end; 

This should prevent the recursive descriptor call and make your code valid.

+5
source share

All Articles