View Issue Details

IDProjectCategoryView StatusLast Update
0034530FPCDocumentationpublic2019-01-19 16:58
ReportercordylusAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
Product Version3.0.4Product Build 
Target Version3.2.0Fixed in Version3.3.1 
Summary0034530: Thread.WaitFor hangs the program if the thread has been freed already
DescriptionIf FreeOnTerminate is enabled, the following pattern may work one time and hang the other, depending on whether the thread has terminated instantly or not:
AThread.Terminate;
AThread.WaitFor;

That might be wrong use, but the hang makes it hard to debug. Should be some error instead. Delphi (I checked 7 and 2000) gives an error.
Steps To ReproduceAn example with random reproducibility is attached, a dozen of clicks may be required to reproduce. Comment out all TMyThread.Execute function body to make it reproduce every time.
TagsNo tags attached.
Fixed in Revision1538
FPCOldBugId
FPCTarget
Attached Files

Activities

cordylus

2018-11-08 20:36

reporter  

waitfor_hang.zip (4,247 bytes)

cordylus

2018-11-08 20:42

reporter   ~0111868

OS: Windows XP

Jonas Maebe

2018-11-08 20:55

manager   ~0111869

It is not possible to give any reliable results in this scenario. The reason is that after the tthread instance has been freed, its memory can be reused and overwritten by other data. You are getting very lucky in Delphi that you get an error in your use case, but I can't imagine that even Delphi can guarantee that this will happen.

cordylus

2018-11-08 21:11

reporter   ~0111870

Last edited: 2018-11-08 21:15

View 2 revisions

Shouldn't it error when it tries to use freed data? Or maybe it's possible to check whether the identifier that is being used to wait for is invalid?

Jonas Maebe

2018-11-08 21:20

manager   ~0111871

Memory that is freed still belongs to the application, and can be reused for other class instances. There is no way to detect that the memory has been freed from within the class instance.

Please ask further questions on the mailing list (http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal) or forum (http://forum.lazarus.freepascal.org/index.php/board,62.0.html)

cordylus

2018-11-08 21:57

reporter   ~0111874

I'm pretty sure that the hang is caused by the not implemented properly error handling in TThread.WaitFor. There is an explicit infinite loop that expects certain successful behavior of MsgWaitForMultipleObjects. If only it'd check for error return values instead of trying again indefinitely, that might fix the problem. Please, take a closer look at the problem. If you decide not to fix it, so be it, I won't reopen again.

rd0x

2018-11-08 22:30

reporter   ~0111875

I have had a similiar issue which is maybe related to this one:

writeln('Start');
thread1 := TThread.ExecuteInThread(@Proc, nil);
// some more threads executing same Proc function
Writeln('Main procedure done');
thread1.WaitFor;
// some more WaitFor, one for each thread
Writeln('End');

Sometimes it didn't showed End msg and I needed to stop my app with control+c

cordylus

2018-11-08 23:36

reporter   ~0111876

The equivalent Delphi code checks for WAIT_FAILED result and raises an exception with GetLastError data in this case.

Sven Barth

2018-12-25 20:49

manager   ~0112874

This *can not* be reliably solved, because the instance of the thread no longer exists if FreeOnTerminate is set and the thread has terminated.
That it works in Delphi is *pure chance* and the following code shows a deadlock on my system with Delphi:

=== code begin ===

type
  TTestThread = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure TTestThread.Execute;
begin

end;

var
  thd, thd2: TTestThread;
begin
  thd := TTestThread.Create(True);
  thd.FreeOnTerminate := True;
  thd.Start;
  Sleep(1000);
  thd2 := TTestThread.Create(True);
  Writeln(IntToHex(NativeInt(thd), SizeOf(NativeInt) * 2));
  Writeln(IntToHex(NativeInt(thd2), SizeOf(NativeInt) * 2));
  try
    thd.WaitFor;
  except
    on e: Exception do
      Writeln('Exception: ', e.Message);
  end;

=== code end ===

The instance of TTestThread assigned to thd2 will be done in the same memory location as thd and as the thread is suspended thd.WaitFor will wait for the new thread instance thus resulting in a deadlock.

In short: DO NOT USE ANY THREAD MEMBER AFTER THE THREAD IS STARTED AND FreeOnTerminate IS SET TO TRUE!
(@Michael: maybe we should add that to TThread's documentation?)

Michael Van Canneyt

2019-01-19 16:58

administrator   ~0113484

Documented.

Issue History

Date Modified Username Field Change
2018-11-08 20:36 cordylus New Issue
2018-11-08 20:36 cordylus File Added: waitfor_hang.zip
2018-11-08 20:42 cordylus Note Added: 0111868
2018-11-08 20:55 Jonas Maebe Note Added: 0111869
2018-11-08 20:55 Jonas Maebe Status new => resolved
2018-11-08 20:55 Jonas Maebe Resolution open => not fixable
2018-11-08 20:55 Jonas Maebe Assigned To => Jonas Maebe
2018-11-08 21:11 cordylus Note Added: 0111870
2018-11-08 21:11 cordylus Status resolved => feedback
2018-11-08 21:11 cordylus Resolution not fixable => reopened
2018-11-08 21:15 cordylus Note Edited: 0111870 View Revisions
2018-11-08 21:20 Jonas Maebe Note Added: 0111871
2018-11-08 21:20 Jonas Maebe Status feedback => resolved
2018-11-08 21:20 Jonas Maebe Resolution reopened => not fixable
2018-11-08 21:57 cordylus Note Added: 0111874
2018-11-08 21:57 cordylus Status resolved => feedback
2018-11-08 21:57 cordylus Resolution not fixable => reopened
2018-11-08 22:00 Jonas Maebe Assigned To Jonas Maebe =>
2018-11-08 22:00 Jonas Maebe Status feedback => new
2018-11-08 22:30 rd0x Note Added: 0111875
2018-11-08 23:36 cordylus Note Added: 0111876
2018-12-25 20:49 Sven Barth Note Added: 0112874
2018-12-25 20:50 Sven Barth Assigned To => Michael Van Canneyt
2018-12-25 20:50 Sven Barth Status new => assigned
2018-12-25 20:50 Sven Barth Category RTL => Documentation
2019-01-19 16:58 Michael Van Canneyt Fixed in Revision => 1538
2019-01-19 16:58 Michael Van Canneyt Note Added: 0113484
2019-01-19 16:58 Michael Van Canneyt Status assigned => resolved
2019-01-19 16:58 Michael Van Canneyt Fixed in Version => 3.3.1
2019-01-19 16:58 Michael Van Canneyt Resolution reopened => fixed
2019-01-19 16:58 Michael Van Canneyt Target Version => 3.2.0