[fpc-devel]threads in linux

Johannes Berg johannes at sipsolutions.de
Thu Nov 13 19:43:23 CET 2003


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
-- 
http://www.sipsolutions.de/
GnuPG key: http://www.sipsolutions.de/keys/JohannesBerg.asc
  Key-ID: 9AB78CA5 Johannes Berg <johannes at sipsolutions.de>
  Fingerprint = AD02 0176 4E29 C137 1DF6 08D2 FC44 CF86 9AB7 8CA5
-------------- next part --------------
program thread;

{$IFDEF FPC}
  {$THREADING ON}
  {$MODE OBJFPC}
uses
  Unix,BaseUnix,
{$ELSE}
uses
{$ENDIF}
  SysUtils, Classes;

{$IFNDEF FPC}
procedure fpsleep(const atime: longint);
begin
  sleep(atime);
end;
{$ENDIF}

type
  TMyThread = class(TThread)
    procedure Execute; override;
  end;

procedure TMyThread.Execute;
begin
  while not terminated do begin
    writeln('hallo');
    fpsleep(1);
  end;
end;

var
  m: TMyThread;
begin
  writeln('starting');
//  fpkill(fpgetpid(),SIGSTOP);
  m := TMyThread.Create(true);
  fpsleep(1000);
  m.terminate;
  m.free;
end.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 194 bytes
Desc: This is a digitally signed message part
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20031113/9ec254cb/attachment.sig>


More information about the fpc-devel mailing list