TThread.Create(CreateSuspended = False) construction has a race condition.
Original Reporter info from Mantis: jmd
-
Reporter name: Jared Davison
Original Reporter info from Mantis: jmd
- Reporter name: Jared Davison
Description:
Thread may get scheduled for execution before the TThread constructor has completed. This behaviour is not Delphi compatible.
This bug is common to all FPC versions, platforms and operating systems.
I have produced a patch for Unix (Tested on Linux & Mac), and will do a Windows one. Other OS & platform I don't have so cant both fix & test - sorry. Can someone please help with that?
Steps to reproduce:
Some unit testing is required for TThread constructor.
See also attached code fpc zip file for full compilable code.
interface
TThreadChild = class(TThread)
private
FThreadState: Integer;
public
constructor CreateRace(const ForceFail: Boolean);
procedure Execute; override;
end;
implementation
var
ATestFailed : Boolean = False;
procedure Log(const S: string);
begin
writeln(S);
end;
constructor TThreadChild.CreateRace(const ForceFail: Boolean);
begin
FThreadState := 1;
inherited Create(False {not suspended}); { the bug is that the inherited call will actually cause execute to be run before the rest of the constructor - serious problem as the thread state may not be initialised properly }
if ForceFail then
Sleep(1000); { This will force the issue. -
it may not be easily reproducable depending on your OS, CPU thread scheduling.
I discovered this on my OSX macbook but my collegue never had the problem on his computer OSX mac mini}
FThreadState := 2; { this is the final state that we should see in the execute, if we get a 1 in the TThreadChild.Execute, then we know that the execute won the race with the constructor }
Sleep(200);
end;
procedure TThreadChild.Execute;
var
ThreadState: Integer;
begin
ThreadState := FThreadState;
if ThreadState = 1 then
Log(Format('ThreadState = %d - constructor race condition occured (should be 2)', [FThreadState])) { we should get the Value 2 here every time, not 1. }
else
if ThreadSTate = 2 then
begin
Log(Format('ThreadState = %d - constructor race condition did not occur (should be 2)', [FThreadState]));
ATestFailed := True;
end;
end;
Additional information:
Here is an example run of the program to illustrate the problem
user@ubuntu:/tthread$ fpc threadtester.lpr/code/medicalobjects/local tests/fpc/tthread$ ./threadtester
Free Pascal Compiler version 2.2.4-3 [2009/06/04] for i386
Copyright (c) 1993-2008 by Florian Klaempfl
Target OS: Linux for i386
Compiling threadtester.lpr
Compiling uthreadtester.pas
Linking threadtester
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
187 lines compiled, 0.1 sec
jared@ubuntu:
Running unforced failure tests
------------------------------
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
ThreadState = 2 - constructor race condition did not occur (should be 2)
Test Complete
Running forced failure tests
----------------------------
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
ThreadState = 1 - constructor race condition occured (should be 2)
Test Complete
There was 1 or more failures
Mantis conversion info:
- Mantis ID: 16884
- Fixed in version: 2.6.0
- Fixed in revision: 16091 (#a6687670)
- Monitored by: » jmd (Jared Davison)