Hi,
Has anyone ever tested threads on linux? A dead simple test program (attached) didn't work for me. It always just got "killed".
After recompiling the RTL with debugging and using ddd, I was able to figure out what the problem is:
Line 80 of tthread.inc in linux says:
FpSigAction(SIGCHLD, @Act, @OldAct);and really should be
FpSigAction(SIGCHLD, Act, OldAct);or maybe even better
FpSigAction(SIGCHLD, Act, nil);Noting that it still doesn't work correctly (the thread executes even though I create it suspended!), I went on. Further debugging reveals that the thread executes before the constructor is able to stop the new thread. I got around this by doing
while Thread.FHandle = 0 do fpsleep(1); // necessary to avoid race
if Thread.FSuspended then Thread.suspend();in ThreadProc() as the first thing and removing the suspend() call in TThread.Create.
Note however, that this still doesn't work as expected with the test program.
Another thing to note is that the flags to clone() should probably include CLONE_PARENT if threads are created from within threads.
Another problem I noticed is that IsMultiThread is set to true _very_ late, even after the thread is really created. This can possibly lead to heap corruption, because the thread could start another thread and then execute memory management routines in those 2 threads before IsMultiThread is set to true. It is highly unlikely though.
And, something I have to take blame for, FFatalException shouldn't be set to nil after the clone() call, but rather before it, because the thread could already have executed, gotten an exception, and set FFatalException by the time FFatalException is set to nil again.
TThread.Suspend should also be rewritten to
procedure TThread.Suspend;
begin
FSuspended := true; // put before Kill() call
Kill(FHandle, SIGSTOP);
end;
so that a thread can suspend itself properly.Since suspending threads is non-portable (afaik the current implementation actually violates posix because if a thread is sent SIGSTOP then the whole process should be suspended), I think it would be better to change the implementation as follows:
- if CreateSuspended is true, do not actually clone()
- in resume, clone() if the thread was created suspended [if you do this, implementing above change with calling suspend in ThreadProc is meaningless and should not be done]
One could also add in a portable way threads suspending themselves, by having them wait for an event in suspend() and signalling that event in resume(). This would be done if FThreadID = CurrentThreadID, otherwise try the non-portable way of sending SIGSTOP. In resume, check a flag (how the thread was suspended) and then either signal the event or sent SIGCONT. Then at least suspending from inside the thread would be posix-safe. Maybe suspend() should be marked as deprecated.
This way at least the semantics of CreateSuspended could be kept, while using suspend() itself would be rather dangerous for portable code.
But I do think that using BeginThread and such functions would be far better.
I'd be willing to implement this, but I only have access to a Linux 2.6 (with NPTL) system (my own) and some Linux 2.4 (with LinuxThreads) systems (at university).
johannes
