mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-13 23:59:10 +02:00
* allow tthread-based threads to only start executing once the constructor
has finished running (based on patch by Jared Davison, mantis #16884) git-svn-id: trunk@15599 -
This commit is contained in:
parent
b18a4617bb
commit
d7cdd9afba
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -10543,6 +10543,7 @@ tests/webtbs/tw16820.pp svneol=native#text/plain
|
|||||||
tests/webtbs/tw16861.pp svneol=native#text/plain
|
tests/webtbs/tw16861.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw16863.pp svneol=native#text/plain
|
tests/webtbs/tw16863.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw16874.pp svneol=native#text/plain
|
tests/webtbs/tw16874.pp svneol=native#text/plain
|
||||||
|
tests/webtbs/tw16884.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw16901.pp svneol=native#text/plain
|
tests/webtbs/tw16901.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw16949a.pp svneol=native#text/plain
|
tests/webtbs/tw16949a.pp svneol=native#text/plain
|
||||||
tests/webtbs/tw16949b.pp svneol=native#text/plain
|
tests/webtbs/tw16949b.pp svneol=native#text/plain
|
||||||
|
@ -124,7 +124,8 @@ end;
|
|||||||
procedure TThread.AfterConstruction;
|
procedure TThread.AfterConstruction;
|
||||||
begin
|
begin
|
||||||
inherited AfterConstruction;
|
inherited AfterConstruction;
|
||||||
// Resume;
|
if not FInitialSuspended then
|
||||||
|
Resume;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1490,6 +1490,7 @@ type
|
|||||||
FSem: Pointer;
|
FSem: Pointer;
|
||||||
FInitialSuspended: boolean;
|
FInitialSuspended: boolean;
|
||||||
FSuspendedExternal: boolean;
|
FSuspendedExternal: boolean;
|
||||||
|
FSuspendedInternal: longbool;
|
||||||
FThreadReaped: boolean;
|
FThreadReaped: boolean;
|
||||||
{$endif}
|
{$endif}
|
||||||
{$ifdef netwlibc}
|
{$ifdef netwlibc}
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
|
|
||||||
{ ok, so this is a hack, but it works nicely. Just never use
|
{ ok, so this is a hack, but it works nicely. Just never use
|
||||||
a multiline argument with WRITE_DEBUG! }
|
a multiline argument with WRITE_DEBUG! }
|
||||||
|
|
||||||
|
{.$DEFINE DEBUG_MT}
|
||||||
{$MACRO ON}
|
{$MACRO ON}
|
||||||
{$IFDEF DEBUG_MT}
|
{$IFDEF DEBUG_MT}
|
||||||
{$define WRITE_DEBUG := writeln} // actually write something
|
{$define WRITE_DEBUG := writeln} // actually write something
|
||||||
@ -120,7 +122,10 @@ begin
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
WRITE_DEBUG('going into LThread.Execute');
|
LThread.FSuspendedInternal := true;
|
||||||
|
WRITE_DEBUG('waiting for SuspendedInternal - ', LThread.ClassName);
|
||||||
|
CurrentTM.SemaphoreWait(LThread.FSem);
|
||||||
|
WRITE_DEBUG('going into LThread.Execute - ', LThread.ClassName);
|
||||||
LThread.Execute;
|
LThread.Execute;
|
||||||
end;
|
end;
|
||||||
except
|
except
|
||||||
@ -180,6 +185,7 @@ begin
|
|||||||
FThreadReaped := false;
|
FThreadReaped := false;
|
||||||
FInitialSuspended := CreateSuspended;
|
FInitialSuspended := CreateSuspended;
|
||||||
FFatalException := nil;
|
FFatalException := nil;
|
||||||
|
FSuspendedInternal := not CreateSuspended;
|
||||||
WRITE_DEBUG('creating thread, self = ',longint(self));
|
WRITE_DEBUG('creating thread, self = ',longint(self));
|
||||||
FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID, StackSize);
|
FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID, StackSize);
|
||||||
if FHandle = TThreadID(0) then
|
if FHandle = TThreadID(0) then
|
||||||
@ -221,7 +227,7 @@ begin
|
|||||||
if not FThreadReaped then
|
if not FThreadReaped then
|
||||||
begin
|
begin
|
||||||
Terminate;
|
Terminate;
|
||||||
if (FInitialSuspended) then
|
if (FSuspendedInternal or FInitialSuspended) then
|
||||||
Resume;
|
Resume;
|
||||||
WaitFor;
|
WaitFor;
|
||||||
end;
|
end;
|
||||||
@ -263,7 +269,12 @@ end;
|
|||||||
|
|
||||||
procedure TThread.Resume;
|
procedure TThread.Resume;
|
||||||
begin
|
begin
|
||||||
if (not FSuspendedExternal) then
|
if FSuspendedInternal and (InterLockedExchange(longint(FSuspendedInternal),ord(false)) = longint(longbool(true))) then
|
||||||
|
begin
|
||||||
|
WRITE_DEBUG('resuming thread after TThread construction',ptruint(self));
|
||||||
|
CurrentTM.SemaphorePost(FSem);
|
||||||
|
end
|
||||||
|
else if (not FSuspendedExternal) then
|
||||||
begin
|
begin
|
||||||
if FSuspended and
|
if FSuspended and
|
||||||
{ don't compare with ord(true) or ord(longbool(true)), }
|
{ don't compare with ord(true) or ord(longbool(true)), }
|
||||||
|
71
tests/webtbs/tw16884.pp
Normal file
71
tests/webtbs/tw16884.pp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{$mode objfpc}
|
||||||
|
|
||||||
|
uses
|
||||||
|
{$ifdef unix}
|
||||||
|
cthreads,
|
||||||
|
{$endif}
|
||||||
|
sysutils, classes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TThreadChild = class(TThread)
|
||||||
|
private
|
||||||
|
FThreadState: Integer;
|
||||||
|
public
|
||||||
|
constructor CreateRace(const ForceFail: Boolean);
|
||||||
|
procedure Execute; override;
|
||||||
|
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;
|
||||||
|
|
||||||
|
var
|
||||||
|
ATestFailed: boolean;
|
||||||
|
|
||||||
|
procedure TThreadChild.Execute;
|
||||||
|
var
|
||||||
|
ThreadState: Integer;
|
||||||
|
begin
|
||||||
|
ThreadState := FThreadState;
|
||||||
|
|
||||||
|
if ThreadState = 1 then
|
||||||
|
begin
|
||||||
|
writeln(Format('ThreadState = %d - constructor race condition occured (should be 2)', [FThreadState])); { we should get the Value 2 here every time, not 1. }
|
||||||
|
ATestFailed := True;
|
||||||
|
readwritebarrier;
|
||||||
|
end
|
||||||
|
else if ThreadSTate = 2 then
|
||||||
|
begin
|
||||||
|
writeln(Format('ThreadState = %d - constructor race condition did not occur (should be 2)', [FThreadState]));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
t1, t2, t3: tthread;
|
||||||
|
begin
|
||||||
|
ATestFailed:=false;
|
||||||
|
t1:=TThreadChild.createrace(true);
|
||||||
|
t2:=TThreadChild.createrace(true);
|
||||||
|
t3:=TThreadChild.createrace(true);
|
||||||
|
t1.waitfor;
|
||||||
|
t1.free;
|
||||||
|
t2.waitfor;
|
||||||
|
t2.free;
|
||||||
|
t3.waitfor;
|
||||||
|
t3.free;
|
||||||
|
readwritebarrier;
|
||||||
|
if ATestFailed then
|
||||||
|
halt(1);
|
||||||
|
end.
|
Loading…
Reference in New Issue
Block a user