Browse Source

+ implementation of cSemaphore* and BasicRTLEvent based on
sem_open/sem_close for Darwin (doesn't have sem_init/sem_destroy)
+ implementation of cSemaphore* based on pipes (for potential future
systems that don't have either sem* routines)
+ test for basicrtlevent
* fixed datarace whereby a TThread could be started, run and exit before
TThread.AfterConstructor had been called (Mantis 6693, all platforms)
* throw EThread exceptions in TThread.create if something during creating
the tthread goes wrong (*nix)
* don't crash in TThread.Destroy if the TThread throws an exception before
it was fully initialised (*nix)
* changed order of operations in TThread.Destroy so it doesn't perform
invalid thread operations in some edge cases (*nix)
* fixed usage of sem_wait/sem_trywait (can be interrupted) in Semaphore
and RTLEvent implementations
* fixed erroneous waiting for threads after they had already exited via
pthread_detach/pthread_exit
* fixed several memory leaks in case of thread intialisation errors
(*nix)
* unified tthread.inc for all Unices

git-svn-id: trunk@5662 -

Jonas Maebe 18 years ago
parent
commit
0c3afc0cf4

+ 2 - 6
.gitattributes

@@ -4425,7 +4425,6 @@ rtl/darwin/sysctlh.inc svneol=native#text/plain
 rtl/darwin/termio.pp svneol=native#text/plain
 rtl/darwin/termio.pp svneol=native#text/plain
 rtl/darwin/termios.inc svneol=native#text/plain
 rtl/darwin/termios.inc svneol=native#text/plain
 rtl/darwin/termiosproc.inc svneol=native#text/plain
 rtl/darwin/termiosproc.inc svneol=native#text/plain
-rtl/darwin/tthread.inc svneol=native#text/plain
 rtl/darwin/unxconst.inc svneol=native#text/plain
 rtl/darwin/unxconst.inc svneol=native#text/plain
 rtl/darwin/unxfunc.inc svneol=native#text/plain
 rtl/darwin/unxfunc.inc svneol=native#text/plain
 rtl/darwin/unxsockh.inc svneol=native#text/plain
 rtl/darwin/unxsockh.inc svneol=native#text/plain
@@ -4485,7 +4484,6 @@ rtl/freebsd/sysnr.inc svneol=native#text/plain
 rtl/freebsd/termio.pp svneol=native#text/plain
 rtl/freebsd/termio.pp svneol=native#text/plain
 rtl/freebsd/termios.inc svneol=native#text/plain
 rtl/freebsd/termios.inc svneol=native#text/plain
 rtl/freebsd/termiosproc.inc svneol=native#text/plain
 rtl/freebsd/termiosproc.inc svneol=native#text/plain
-rtl/freebsd/tthread.inc svneol=native#text/plain
 rtl/freebsd/ucontexth.inc -text svneol=unset#text/plain
 rtl/freebsd/ucontexth.inc -text svneol=unset#text/plain
 rtl/freebsd/unixsock.inc svneol=native#text/plain
 rtl/freebsd/unixsock.inc svneol=native#text/plain
 rtl/freebsd/unxconst.inc svneol=native#text/plain
 rtl/freebsd/unxconst.inc svneol=native#text/plain
@@ -4761,7 +4759,6 @@ rtl/linux/system.pp svneol=native#text/plain
 rtl/linux/termio.pp svneol=native#text/plain
 rtl/linux/termio.pp svneol=native#text/plain
 rtl/linux/termios.inc svneol=native#text/plain
 rtl/linux/termios.inc svneol=native#text/plain
 rtl/linux/termiosproc.inc svneol=native#text/plain
 rtl/linux/termiosproc.inc svneol=native#text/plain
-rtl/linux/tthread.inc svneol=native#text/plain
 rtl/linux/unixsock.inc svneol=native#text/plain
 rtl/linux/unixsock.inc svneol=native#text/plain
 rtl/linux/unxconst.inc svneol=native#text/plain
 rtl/linux/unxconst.inc svneol=native#text/plain
 rtl/linux/unxfunc.inc svneol=native#text/plain
 rtl/linux/unxfunc.inc svneol=native#text/plain
@@ -4897,7 +4894,6 @@ rtl/netbsd/systypes.inc svneol=native#text/plain
 rtl/netbsd/termio.pp svneol=native#text/plain
 rtl/netbsd/termio.pp svneol=native#text/plain
 rtl/netbsd/termios.inc svneol=native#text/plain
 rtl/netbsd/termios.inc svneol=native#text/plain
 rtl/netbsd/termiosproc.inc svneol=native#text/plain
 rtl/netbsd/termiosproc.inc svneol=native#text/plain
-rtl/netbsd/tthread.inc svneol=native#text/plain
 rtl/netbsd/unixsock.inc svneol=native#text/plain
 rtl/netbsd/unixsock.inc svneol=native#text/plain
 rtl/netbsd/unxconst.inc svneol=native#text/plain
 rtl/netbsd/unxconst.inc svneol=native#text/plain
 rtl/netbsd/unxfunc.inc svneol=native#text/plain
 rtl/netbsd/unxfunc.inc svneol=native#text/plain
@@ -5105,7 +5101,6 @@ rtl/openbsd/systypes.inc svneol=native#text/plain
 rtl/openbsd/termio.pp svneol=native#text/plain
 rtl/openbsd/termio.pp svneol=native#text/plain
 rtl/openbsd/termios.inc svneol=native#text/plain
 rtl/openbsd/termios.inc svneol=native#text/plain
 rtl/openbsd/termiosproc.inc svneol=native#text/plain
 rtl/openbsd/termiosproc.inc svneol=native#text/plain
-rtl/openbsd/tthread.inc svneol=native#text/plain
 rtl/openbsd/unixsock.inc svneol=native#text/plain
 rtl/openbsd/unixsock.inc svneol=native#text/plain
 rtl/openbsd/unixsysc.inc svneol=native#text/plain
 rtl/openbsd/unixsysc.inc svneol=native#text/plain
 rtl/openbsd/unxsockh.inc svneol=native#text/plain
 rtl/openbsd/unxsockh.inc svneol=native#text/plain
@@ -5240,7 +5235,6 @@ rtl/solaris/system.pp svneol=native#text/plain
 rtl/solaris/termio.pp svneol=native#text/plain
 rtl/solaris/termio.pp svneol=native#text/plain
 rtl/solaris/termios.inc svneol=native#text/plain
 rtl/solaris/termios.inc svneol=native#text/plain
 rtl/solaris/termiosproc.inc svneol=native#text/plain
 rtl/solaris/termiosproc.inc svneol=native#text/plain
-rtl/solaris/tthread.inc svneol=native#text/plain
 rtl/solaris/unxconst.inc svneol=native#text/plain
 rtl/solaris/unxconst.inc svneol=native#text/plain
 rtl/solaris/unxfunc.inc svneol=native#text/plain
 rtl/solaris/unxfunc.inc svneol=native#text/plain
 rtl/solaris/unxsockh.inc svneol=native#text/plain
 rtl/solaris/unxsockh.inc svneol=native#text/plain
@@ -5337,6 +5331,7 @@ rtl/unix/sysutils.pp svneol=native#text/plain
 rtl/unix/terminfo.pp svneol=native#text/plain
 rtl/unix/terminfo.pp svneol=native#text/plain
 rtl/unix/termiosh.inc svneol=native#text/plain
 rtl/unix/termiosh.inc svneol=native#text/plain
 rtl/unix/timezone.inc svneol=native#text/plain
 rtl/unix/timezone.inc svneol=native#text/plain
+rtl/unix/tthread.inc svneol=native#text/plain
 rtl/unix/ttyname.inc svneol=native#text/plain
 rtl/unix/ttyname.inc svneol=native#text/plain
 rtl/unix/unix.pp svneol=native#text/plain
 rtl/unix/unix.pp svneol=native#text/plain
 rtl/unix/unixtype.pp svneol=native#text/plain
 rtl/unix/unixtype.pp svneol=native#text/plain
@@ -6501,6 +6496,7 @@ tests/test/tarray5.pp svneol=native#text/plain
 tests/test/tarray6.pp svneol=native#text/plain
 tests/test/tarray6.pp svneol=native#text/plain
 tests/test/tasmread.pp svneol=native#text/plain
 tests/test/tasmread.pp svneol=native#text/plain
 tests/test/tasout.pp svneol=native#text/plain
 tests/test/tasout.pp svneol=native#text/plain
+tests/test/tbrtlevt.pp svneol=native#text/plain
 tests/test/tcase1.pp svneol=native#text/plain
 tests/test/tcase1.pp svneol=native#text/plain
 tests/test/tcase2.pp svneol=native#text/plain
 tests/test/tcase2.pp svneol=native#text/plain
 tests/test/tcg1.pp svneol=native#text/plain
 tests/test/tcg1.pp svneol=native#text/plain

+ 5 - 3
rtl/darwin/pthread.inc

@@ -22,6 +22,7 @@
 CONST PTHREAD_EXPLICIT_SCHED       = 0;
 CONST PTHREAD_EXPLICIT_SCHED       = 0;
       PTHREAD_CREATE_DETACHED      = 1;
       PTHREAD_CREATE_DETACHED      = 1;
       PTHREAD_SCOPE_PROCESS        = 0;
       PTHREAD_SCOPE_PROCESS        = 0;
+      SEM_FAILED                   = -1;
 
 
  TYPE
  TYPE
     ppthread_t           = ^pthread_t;
     ppthread_t           = ^pthread_t;
@@ -63,7 +64,9 @@ function  pthread_cond_wait(_para1:Ppthread_cond_t;_para2:Ppthread_mutex_t):cint
 function pthread_kill(__thread:pthread_t; __signo:cint):cint;cdecl;external 'c';
 function pthread_kill(__thread:pthread_t; __signo:cint):cint;cdecl;external 'c';
 
 
 
 
-function sem_init(__sem:Psem_t; __pshared:cint;__value:cuint):cint;cdecl; external 'c' name 'sem_init';
+// not yet implemented in Mac OS X 10.4.8!
+// function sem_init(__sem:Psem_t; __pshared:cint;__value:cuint):cint;cdecl; external 'c' name 'sem_init';
+function sem_open(name: pchar; oflag: cint): Psem_t; cdecl; varargs; external 'c' name 'sem_open';
 function sem_destroy(__sem:Psem_t):cint;cdecl;external 'c' name 'sem_destroy';
 function sem_destroy(__sem:Psem_t):cint;cdecl;external 'c' name 'sem_destroy';
 function sem_close(__sem:Psem_t):cint;cdecl;external 'c'  name 'sem_close';
 function sem_close(__sem:Psem_t):cint;cdecl;external 'c'  name 'sem_close';
 function sem_unlink(__name:Pchar):cint;cdecl;external 'c' name 'sem_unlink';
 function sem_unlink(__name:Pchar):cint;cdecl;external 'c' name 'sem_unlink';
@@ -76,5 +79,4 @@ function pthread_mutexattr_init(_para1:Ppthread_mutexattr_t):cint;cdecl;external
 function pthread_mutexattr_destroy(_para1:Ppthread_mutexattr_t):cint;cdecl;external 'c' name 'pthread_mutexattr_destroy';
 function pthread_mutexattr_destroy(_para1:Ppthread_mutexattr_t):cint;cdecl;external 'c' name 'pthread_mutexattr_destroy';
 function pthread_mutexattr_gettype(_para1:Ppthread_mutexattr_t; _para2:Pcint):cint;cdecl;external 'c' name 'pthread_mutexattr_gettype';
 function pthread_mutexattr_gettype(_para1:Ppthread_mutexattr_t; _para2:Pcint):cint;cdecl;external 'c' name 'pthread_mutexattr_gettype';
 function pthread_mutexattr_settype(_para1:Ppthread_mutexattr_t; _para2:cint):cint;cdecl;external 'c' name 'pthread_mutexattr_settype';
 function pthread_mutexattr_settype(_para1:Ppthread_mutexattr_t; _para2:cint):cint;cdecl;external 'c' name 'pthread_mutexattr_settype';
-function pthread_cond_timedwait(__cond:ppthread_cond_t; __mutex:ppthread_mutex_t; __abstime:ptimespec):cint; cdecl;external 'c' name 'pthread_cond_timedwait'; 
-
+function pthread_cond_timedwait(__cond:ppthread_cond_t; __mutex:ppthread_mutex_t; __abstime:ptimespec):cint; cdecl;external 'c' name 'pthread_cond_timedwait';

+ 0 - 252
rtl/freebsd/tthread.inc

@@ -1,252 +0,0 @@
-{
-   This file is part of the Free Pascal run time library.
-   (c) 2000-2003 by Marco van de Voort
-   member of the Free Pascal development team.
-
-   See the file COPYING.FPC, included in this distribution,
-   for details about the copyright.
-
-   TThread implementation old (1.0) and new (pthreads) style
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY;without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-}
-
-
-{
-  What follows, is a short description on my implementation of TThread.
-  Most information can also be found by reading the source and accompanying
-  comments.
-
-  A thread is created using BeginThread, which in turn calls
-  pthread_create. So the threads here are always posix threads.
-  Posix doesn't define anything for suspending threads as this is
-  inherintly unsafe. Just don't suspend threads at points they cannot
-  control. Therefore, I didn't implement .Suspend() if its called from
-  outside the threads execution flow (except on Linux _without_ NPTL).
-
-  The implementation for .suspend uses a semaphore, which is initialized
-  at thread creation. If the thread tries to suspend itself, we simply
-  let it wait on the semaphore until it is unblocked by someone else
-  who calls .Resume.
-
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
-
-  Johannes Berg <[email protected]>, Sunday, November 16 2003
-}
-
-{ ok, so this is a hack, but it works nicely. Just never use
-  a multiline argument with WRITE_DEBUG! }
-{$MACRO ON}
-{$IFDEF DEBUG_MT}
-{$define WRITE_DEBUG := writeln} // actually write something
-{$ELSE}
-{$define WRITE_DEBUG := //}      // just comment out those lines
-{$ENDIF}
-
-var
-  ThreadsInited: boolean = false;
-  CurrentTM: TThreadManager;
-  
-const
-  // stupid, considering its not even implemented...
-  Priorities: array [TThreadPriority] of Integer =
-   (-20,-19,-10,0,9,18,19);
-
-procedure InitThreads;
-begin
-  if not ThreadsInited then begin
-    GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-  end;
-end;
-
-procedure DoneThreads;
-begin
-  ThreadsInited := false;
-end;
-
-function ThreadFunc(parameter: Pointer): LongInt;
-var
-  LThread: TThread;
-  c: char;
-begin
-  WRITE_DEBUG('ThreadFunc is here...');
-  LThread := TThread(parameter);
-  WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
-  try
-    if LThread.FInitialSuspended then begin
-      CurrentTM.SemaphoreWait(LThread.FSem);
-      if not LThread.FSuspended then begin
-        LThread.FInitialSuspended := false;
-        WRITE_DEBUG('going into LThread.Execute');
-        LThread.Execute;
-      end;
-    end else begin
-      WRITE_DEBUG('going into LThread.Execute');
-      LThread.Execute;
-    end;
-  except
-    on e: exception do begin
-      WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
-      // not sure if we should really do this...
-      // but .Destroy was called, so why not try FreeOnTerminate?
-      if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
-    end;
-  end;
-  WRITE_DEBUG('thread done running');
-  Result := LThread.FReturnValue;
-  WRITE_DEBUG('Result is ',Result);
-  LThread.FFinished := True;
-  LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
-  end;
-  WRITE_DEBUG('thread func calling EndThread');
-  EndThread(Result);
-end;
-
-{ TThread }
-constructor TThread.Create(CreateSuspended: Boolean;
-                           const StackSize: SizeUInt = DefaultStackSize);
-begin
-  // lets just hope that the user doesn't create a thread
-  // via BeginThread and creates the first TThread Object in there!
-  InitThreads;
-  inherited Create;
-  FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
-  FSuspendedExternal := false;
-  FInitialSuspended := CreateSuspended;
-  FFatalException := nil;
-  WRITE_DEBUG('creating thread, self = ',PtrInt(self));
-  FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID,StackSize);
-  WRITE_DEBUG('TThread.Create done');
-end;
-
-
-destructor TThread.Destroy;
-begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
-    raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
-  // if someone calls .Free on a thread with
-  // FreeOnTerminate, then don't crash!
-  FFreeOnTerminate := false;
-  if not FFinished and not FSuspended then begin
-    Terminate;
-    WaitFor;
-  end;
-  if (FInitialSuspended) then begin
-    // thread was created suspended but never woken up.
-    CurrentTM.SemaphorePost(FSem);
-    WaitFor;
-  end;
-  FFatalException.Free;
-  FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
-  inherited Destroy;
-end;
-
-procedure TThread.SetSuspended(Value: Boolean);
-begin
-  if Value <> FSuspended then
-    if Value then
-      Suspend
-    else
-      Resume;
-end;
-
-procedure TThread.Suspend;
-begin
-  if not FSuspended then begin
-    if FThreadID = GetCurrentThreadID then begin
-      FSuspended := true;
-      CurrentTM.SemaphoreWait(FSem);
-    end else begin
-      FSuspendedExternal := true;
-      SuspendThread(FHandle);
-    end;
-  end;
-end;
-
-
-procedure TThread.Resume;
-begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
-    end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
-end;
-
-
-procedure TThread.Terminate;
-begin
-  FTerminated := True;
-end;
-
-function TThread.WaitFor: Integer;
-begin
-  WRITE_DEBUG('waiting for thread ',FHandle);
-  WaitFor := WaitForThreadTerminate(FHandle, 0);
-  WRITE_DEBUG('thread terminated');
-end;
-
-procedure TThread.CallOnTerminate;
-begin
-  // no need to check if FOnTerminate <> nil, because
-  // thats already done in DoTerminate
-  FOnTerminate(self);
-end;
-
-procedure TThread.DoTerminate;
-begin
-  if Assigned(FOnTerminate) then
-    Synchronize(@CallOnTerminate);
-end;
-
-function TThread.GetPriority: TThreadPriority;
-var
-  P: Integer;
-  I: TThreadPriority;
-begin
-  P := ThreadGetPriority(FHandle);
-  Result := tpNormal;
-  for I := Low(TThreadPriority) to High(TThreadPriority) do
-    if Priorities[I] = P then
-      Result := I;
-end;
-
-
-procedure TThread.SetPriority(Value: TThreadPriority);
-begin
-  ThreadSetPriority(FHandle, Priorities[Value]);
-end;
-

+ 7 - 1
rtl/inc/thread.inc

@@ -79,7 +79,7 @@ begin
   Result:=CurrentTM.BeginThread(sa,stacksize,threadfunction,P,creationflags,ThreadID);
   Result:=CurrentTM.BeginThread(sa,stacksize,threadfunction,P,creationflags,ThreadID);
 end;
 end;
 
 
-procedure EndThread(ExitCode : DWord);
+procedure FlushThread;
 
 
 begin
 begin
 {$ifdef FPC_HAS_FEATURE_CONSOLEIO}
 {$ifdef FPC_HAS_FEATURE_CONSOLEIO}
@@ -89,6 +89,12 @@ begin
   Flush(StdOut);
   Flush(StdOut);
   Flush(StdErr);
   Flush(StdErr);
 {$endif FPC_HAS_FEATURE_CONSOLEIO}
 {$endif FPC_HAS_FEATURE_CONSOLEIO}
+end;
+
+procedure EndThread(ExitCode : DWord);
+
+begin
+  FlushThread;
   CurrentTM.EndThread(ExitCode);
   CurrentTM.EndThread(ExitCode);
 end;
 end;
 
 

+ 1 - 0
rtl/inc/threadh.inc

@@ -129,6 +129,7 @@ procedure EndThread(ExitCode : DWord);
 procedure EndThread;
 procedure EndThread;
 
 
 {some thread support functions}
 {some thread support functions}
+procedure FlushThread;
 function  SuspendThread (threadHandle : TThreadID) : dword;
 function  SuspendThread (threadHandle : TThreadID) : dword;
 function  ResumeThread  (threadHandle : TThreadID) : dword;
 function  ResumeThread  (threadHandle : TThreadID) : dword;
 procedure ThreadSwitch;                                                                {give time to other threads}
 procedure ThreadSwitch;                                                                {give time to other threads}

+ 0 - 258
rtl/linux/tthread.inc

@@ -1,258 +0,0 @@
-{
-   This file is part of the Free Pascal run time library.
-   (c) 2000-2003 by Marco van de Voort
-   member of the Free Pascal development team.
-
-   See the file COPYING.FPC, included in this distribution,
-   for details about the copyright.
-
-   TThread implementation old (1.0) and new (pthreads) style
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY;without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-}
-
-
-{
-  What follows, is a short description on my implementation of TThread.
-  Most information can also be found by reading the source and accompanying
-  comments.
-
-  A thread is created using BeginThread, which in turn calls
-  pthread_create. So the threads here are always posix threads.
-  Posix doesn't define anything for suspending threads as this is
-  inherintly unsafe. Just don't suspend threads at points they cannot
-  control. Therefore, I didn't implement .Suspend() if its called from
-  outside the threads execution flow (except on Linux _without_ NPTL).
-
-  The implementation for .suspend uses a semaphore, which is initialized
-  at thread creation. If the thread tries to suspend itself, we simply
-  let it wait on the semaphore until it is unblocked by someone else
-  who calls .Resume.
-
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
-
-  Johannes Berg <[email protected]>, Sunday, November 16 2003
-}
-
-{ ok, so this is a hack, but it works nicely. Just never use
-  a multiline argument with WRITE_DEBUG! }
-{$MACRO ON}
-{$IFDEF DEBUG_MT}
-{$define WRITE_DEBUG := writeln} // actually write something
-{$ELSE}
-{$define WRITE_DEBUG := //}      // just comment out those lines
-{$ENDIF}
-
-var
-  ThreadsInited: boolean = false;
-  CurrentTM: TThreadManager;
-  GMainPID: LongInt = 0;
-  
-const
-  // stupid, considering its not even implemented...
-  Priorities: array [TThreadPriority] of Integer =
-   (-20,-19,-10,0,9,18,19);
-
-procedure InitThreads;
-begin
-  if not ThreadsInited then begin
-    GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-    GMainPid := fpgetpid();
-  end;
-end;
-
-procedure DoneThreads;
-begin
-  ThreadsInited := false;
-end;
-
-function ThreadFunc(parameter: Pointer): PtrInt;
-var
-  LThread: TThread;
-  c: char;
-begin
-  WRITE_DEBUG('ThreadFunc is here...');
-  LThread := TThread(parameter);
-  WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
-  // save the PID of the "thread"
-  // this is different from the PID of the main thread if
-  // the LinuxThreads implementation is used
-  LThread.FPid := fpgetpid();
-  try
-    if LThread.FInitialSuspended then begin
-      CurrentTM.SemaphoreWait(LThread.FSem);
-      if not LThread.FSuspended then begin
-        LThread.FInitialSuspended := false;
-        WRITE_DEBUG('going into LThread.Execute');
-        LThread.Execute;
-      end;
-    end else begin
-      WRITE_DEBUG('going into LThread.Execute');
-      LThread.Execute;
-    end;
-  except
-    on e: exception do begin
-      WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
-      // not sure if we should really do this...
-      // but .Destroy was called, so why not try FreeOnTerminate?
-      if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
-    end;
-  end;
-  WRITE_DEBUG('thread done running');
-  Result := LThread.FReturnValue;
-  WRITE_DEBUG('Result is ',Result);
-  LThread.FFinished := True;
-  LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
-  end;
-  WRITE_DEBUG('thread func calling EndThread');
-  EndThread(Result);
-end;
-
-{ TThread }
-constructor TThread.Create(CreateSuspended: Boolean;
-                           const StackSize: SizeUInt = DefaultStackSize);
-begin
-  // lets just hope that the user doesn't create a thread
-  // via BeginThread and creates the first TThread Object in there!
-  InitThreads;
-  inherited Create;
-  FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
-  FSuspendedExternal := false;
-  FInitialSuspended := CreateSuspended;
-  FFatalException := nil;
-  WRITE_DEBUG('creating thread, self = ',PtrInt(self));
-  FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID,StackSize);
-  WRITE_DEBUG('TThread.Create done');
-end;
-
-
-destructor TThread.Destroy;
-begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
-    raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
-  // if someone calls .Free on a thread with
-  // FreeOnTerminate, then don't crash!
-  FFreeOnTerminate := false;
-  if not FFinished and not FSuspended then begin
-    Terminate;
-    WaitFor;
-  end;
-  if (FInitialSuspended) then begin
-    // thread was created suspended but never woken up.
-    CurrentTM.SemaphorePost(FSem);
-    WaitFor;
-  end;
-  FFatalException.Free;
-  FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
-  inherited Destroy;
-end;
-
-procedure TThread.SetSuspended(Value: Boolean);
-begin
-  if Value <> FSuspended then
-    if Value then
-      Suspend
-    else
-      Resume;
-end;
-
-procedure TThread.Suspend;
-begin
-  if not FSuspended then begin
-    if FThreadID = GetCurrentThreadID then begin
-      FSuspended := true;
-      CurrentTM.SemaphoreWait(FSem);
-    end else begin
-      FSuspendedExternal := true;
-      SuspendThread(FHandle);
-    end;
-  end;
-end;
-
-
-procedure TThread.Resume;
-begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
-    end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
-end;
-
-
-procedure TThread.Terminate;
-begin
-  FTerminated := True;
-end;
-
-function TThread.WaitFor: Integer;
-begin
-  WRITE_DEBUG('waiting for thread ',FHandle);
-  WaitFor := WaitForThreadTerminate(FHandle, 0);
-  WRITE_DEBUG('thread terminated');
-end;
-
-procedure TThread.CallOnTerminate;
-begin
-  // no need to check if FOnTerminate <> nil, because
-  // thats already done in DoTerminate
-  FOnTerminate(self);
-end;
-
-procedure TThread.DoTerminate;
-begin
-  if Assigned(FOnTerminate) then
-    Synchronize(@CallOnTerminate);
-end;
-
-function TThread.GetPriority: TThreadPriority;
-var
-  P: Integer;
-  I: TThreadPriority;
-begin
-  P := ThreadGetPriority(FHandle);
-  Result := tpNormal;
-  for I := Low(TThreadPriority) to High(TThreadPriority) do
-    if Priorities[I] = P then
-      Result := I;
-end;
-
-
-procedure TThread.SetPriority(Value: TThreadPriority);
-begin
-  ThreadSetPriority(FHandle, Priorities[Value]);
-end;
-

+ 0 - 252
rtl/netbsd/tthread.inc

@@ -1,252 +0,0 @@
-{
-   This file is part of the Free Pascal run time library.
-   (c) 2000-2003 by Marco van de Voort
-   member of the Free Pascal development team.
-
-   See the file COPYING.FPC, included in this distribution,
-   for details about the copyright.
-
-   TThread implementation old (1.0) and new (pthreads) style
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY;without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-}
-
-
-{
-  What follows, is a short description on my implementation of TThread.
-  Most information can also be found by reading the source and accompanying
-  comments.
-
-  A thread is created using BeginThread, which in turn calls
-  pthread_create. So the threads here are always posix threads.
-  Posix doesn't define anything for suspending threads as this is
-  inherintly unsafe. Just don't suspend threads at points they cannot
-  control. Therefore, I didn't implement .Suspend() if its called from
-  outside the threads execution flow (except on Linux _without_ NPTL).
-
-  The implementation for .suspend uses a semaphore, which is initialized
-  at thread creation. If the thread tries to suspend itself, we simply
-  let it wait on the semaphore until it is unblocked by someone else
-  who calls .Resume.
-
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
-
-  Johannes Berg <[email protected]>, Sunday, November 16 2003
-}
-
-{ ok, so this is a hack, but it works nicely. Just never use
-  a multiline argument with WRITE_DEBUG! }
-{$MACRO ON}
-{$IFDEF DEBUG_MT}
-{$define WRITE_DEBUG := writeln} // actually write something
-{$ELSE}
-{$define WRITE_DEBUG := //}      // just comment out those lines
-{$ENDIF}
-
-var
-  ThreadsInited: boolean = false;
-  CurrentTM: TThreadManager;
-  
-const
-  // stupid, considering its not even implemented...
-  Priorities: array [TThreadPriority] of Integer =
-   (-20,-19,-10,0,9,18,19);
-
-procedure InitThreads;
-begin
-  if not ThreadsInited then begin
-    GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-  end;
-end;
-
-procedure DoneThreads;
-begin
-  ThreadsInited := false;
-end;
-
-function ThreadFunc(parameter: Pointer): LongInt;
-var
-  LThread: TThread;
-  c: char;
-begin
-  WRITE_DEBUG('ThreadFunc is here...');
-  LThread := TThread(parameter);
-  WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
-  try
-    if LThread.FInitialSuspended then begin
-      CurrentTM.SemaphoreWait(LThread.FSem);
-      if not LThread.FSuspended then begin
-        LThread.FInitialSuspended := false;
-        WRITE_DEBUG('going into LThread.Execute');
-        LThread.Execute;
-      end;
-    end else begin
-      WRITE_DEBUG('going into LThread.Execute');
-      LThread.Execute;
-    end;
-  except
-    on e: exception do begin
-      WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
-      // not sure if we should really do this...
-      // but .Destroy was called, so why not try FreeOnTerminate?
-      if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
-    end;
-  end;
-  WRITE_DEBUG('thread done running');
-  Result := LThread.FReturnValue;
-  WRITE_DEBUG('Result is ',Result);
-  LThread.FFinished := True;
-  LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
-  end;
-  WRITE_DEBUG('thread func calling EndThread');
-  EndThread(Result);
-end;
-
-{ TThread }
-constructor TThread.Create(CreateSuspended: Boolean;
-                           const StackSize: SizeUInt = DefaultStackSize);
-begin
-  // lets just hope that the user doesn't create a thread
-  // via BeginThread and creates the first TThread Object in there!
-  InitThreads;
-  inherited Create;
-  FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
-  FSuspendedExternal := false;
-  FInitialSuspended := CreateSuspended;
-  FFatalException := nil;
-  WRITE_DEBUG('creating thread, self = ',PtrInt(self));
-  FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID,StackSize);
-  WRITE_DEBUG('TThread.Create done');
-end;
-
-
-destructor TThread.Destroy;
-begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
-    raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
-  // if someone calls .Free on a thread with
-  // FreeOnTerminate, then don't crash!
-  FFreeOnTerminate := false;
-  if not FFinished and not FSuspended then begin
-    Terminate;
-    WaitFor;
-  end;
-  if (FInitialSuspended) then begin
-    // thread was created suspended but never woken up.
-    CurrentTM.SemaphorePost(FSem);
-    WaitFor;
-  end;
-  FFatalException.Free;
-  FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
-  inherited Destroy;
-end;
-
-procedure TThread.SetSuspended(Value: Boolean);
-begin
-  if Value <> FSuspended then
-    if Value then
-      Suspend
-    else
-      Resume;
-end;
-
-procedure TThread.Suspend;
-begin
-  if not FSuspended then begin
-    if FThreadID = GetCurrentThreadID then begin
-      FSuspended := true;
-      CurrentTM.SemaphoreWait(FSem);
-    end else begin
-      FSuspendedExternal := true;
-      SuspendThread(FHandle);
-    end;
-  end;
-end;
-
-
-procedure TThread.Resume;
-begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
-    end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
-end;
-
-
-procedure TThread.Terminate;
-begin
-  FTerminated := True;
-end;
-
-function TThread.WaitFor: Integer;
-begin
-  WRITE_DEBUG('waiting for thread ',FHandle);
-  WaitFor := WaitForThreadTerminate(FHandle, 0);
-  WRITE_DEBUG('thread terminated');
-end;
-
-procedure TThread.CallOnTerminate;
-begin
-  // no need to check if FOnTerminate <> nil, because
-  // thats already done in DoTerminate
-  FOnTerminate(self);
-end;
-
-procedure TThread.DoTerminate;
-begin
-  if Assigned(FOnTerminate) then
-    Synchronize(@CallOnTerminate);
-end;
-
-function TThread.GetPriority: TThreadPriority;
-var
-  P: Integer;
-  I: TThreadPriority;
-begin
-  P := ThreadGetPriority(FHandle);
-  Result := tpNormal;
-  for I := Low(TThreadPriority) to High(TThreadPriority) do
-    if Priorities[I] = P then
-      Result := I;
-end;
-
-
-procedure TThread.SetPriority(Value: TThreadPriority);
-begin
-  ThreadSetPriority(FHandle, Priorities[Value]);
-end;
-

+ 7 - 2
rtl/netwlibc/tthread.inc

@@ -218,8 +218,13 @@ begin
   {$ENDIF}
   {$ENDIF}
   WRITE_DEBUG('thread initing, parameter = %d'#13#10, LongInt(LThread));
   WRITE_DEBUG('thread initing, parameter = %d'#13#10, LongInt(LThread));
   try
   try
+    // wait until AfterConstruction has been called, so we cannot
+    // free ourselves before TThread.Create has finished
+    // (since that one may check our VTM in case of $R+, and
+    //  will call the AfterConstruction method in all cases)
+    LThread.Suspend;
     if LThread.FInitialSuspended then begin
     if LThread.FInitialSuspended then begin
-      SemaphoreWait(LThread.FSem);
+       LThread.Suspend;
       if not LThread.FInitialSuspended then begin
       if not LThread.FInitialSuspended then begin
         WRITE_DEBUG('going into LThread.Execute'#13#10);
         WRITE_DEBUG('going into LThread.Execute'#13#10);
         LThread.Execute;
         LThread.Execute;
@@ -261,7 +266,7 @@ begin
   AddThread(self);
   AddThread(self);
   inherited Create;
   inherited Create;
   FSem := SemaphoreInit;
   FSem := SemaphoreInit;
-  FSuspended :=CreateSuspended;
+  FSuspended := False;
   FSuspendedExternal := false;
   FSuspendedExternal := false;
   FInitialSuspended := CreateSuspended;
   FInitialSuspended := CreateSuspended;
   FFatalException := nil;
   FFatalException := nil;

+ 0 - 252
rtl/openbsd/tthread.inc

@@ -1,252 +0,0 @@
-{
-   This file is part of the Free Pascal run time library.
-   (c) 2000-2003 by Marco van de Voort
-   member of the Free Pascal development team.
-
-   See the file COPYING.FPC, included in this distribution,
-   for details about the copyright.
-
-   TThread implementation old (1.0) and new (pthreads) style
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY;without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-}
-
-
-{
-  What follows, is a short description on my implementation of TThread.
-  Most information can also be found by reading the source and accompanying
-  comments.
-
-  A thread is created using BeginThread, which in turn calls
-  pthread_create. So the threads here are always posix threads.
-  Posix doesn't define anything for suspending threads as this is
-  inherintly unsafe. Just don't suspend threads at points they cannot
-  control. Therefore, I didn't implement .Suspend() if its called from
-  outside the threads execution flow (except on Linux _without_ NPTL).
-
-  The implementation for .suspend uses a semaphore, which is initialized
-  at thread creation. If the thread tries to suspend itself, we simply
-  let it wait on the semaphore until it is unblocked by someone else
-  who calls .Resume.
-
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
-
-  Johannes Berg <[email protected]>, Sunday, November 16 2003
-}
-
-{ ok, so this is a hack, but it works nicely. Just never use
-  a multiline argument with WRITE_DEBUG! }
-{$MACRO ON}
-{$IFDEF DEBUG_MT}
-{$define WRITE_DEBUG := writeln} // actually write something
-{$ELSE}
-{$define WRITE_DEBUG := //}      // just comment out those lines
-{$ENDIF}
-
-var
-  ThreadsInited: boolean = false;
-  CurrentTM: TThreadManager;
-  
-const
-  // stupid, considering its not even implemented...
-  Priorities: array [TThreadPriority] of Integer =
-   (-20,-19,-10,0,9,18,19);
-
-procedure InitThreads;
-begin
-  if not ThreadsInited then begin
-    GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-  end;
-end;
-
-procedure DoneThreads;
-begin
-  ThreadsInited := false;
-end;
-
-function ThreadFunc(parameter: Pointer): LongInt;
-var
-  LThread: TThread;
-  c: char;
-begin
-  WRITE_DEBUG('ThreadFunc is here...');
-  LThread := TThread(parameter);
-  WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
-  try
-    if LThread.FInitialSuspended then begin
-      CurrentTM.SemaphoreWait(LThread.FSem);
-      if not LThread.FSuspended then begin
-        LThread.FInitialSuspended := false;
-        WRITE_DEBUG('going into LThread.Execute');
-        LThread.Execute;
-      end;
-    end else begin
-      WRITE_DEBUG('going into LThread.Execute');
-      LThread.Execute;
-    end;
-  except
-    on e: exception do begin
-      WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
-      // not sure if we should really do this...
-      // but .Destroy was called, so why not try FreeOnTerminate?
-      if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
-    end;
-  end;
-  WRITE_DEBUG('thread done running');
-  Result := LThread.FReturnValue;
-  WRITE_DEBUG('Result is ',Result);
-  LThread.FFinished := True;
-  LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
-  end;
-  WRITE_DEBUG('thread func calling EndThread');
-  EndThread(Result);
-end;
-
-{ TThread }
-constructor TThread.Create(CreateSuspended: Boolean;
-                           const StackSize: SizeUInt = DefaultStackSize);
-begin
-  // lets just hope that the user doesn't create a thread
-  // via BeginThread and creates the first TThread Object in there!
-  InitThreads;
-  inherited Create;
-  FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
-  FSuspendedExternal := false;
-  FInitialSuspended := CreateSuspended;
-  FFatalException := nil;
-  WRITE_DEBUG('creating thread, self = ',PtrInt(self));
-  FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID,StackSize);
-  WRITE_DEBUG('TThread.Create done');
-end;
-
-
-destructor TThread.Destroy;
-begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
-    raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
-  // if someone calls .Free on a thread with
-  // FreeOnTerminate, then don't crash!
-  FFreeOnTerminate := false;
-  if not FFinished and not FSuspended then begin
-    Terminate;
-    WaitFor;
-  end;
-  if (FInitialSuspended) then begin
-    // thread was created suspended but never woken up.
-    CurrentTM.SemaphorePost(FSem);
-    WaitFor;
-  end;
-  FFatalException.Free;
-  FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
-  inherited Destroy;
-end;
-
-procedure TThread.SetSuspended(Value: Boolean);
-begin
-  if Value <> FSuspended then
-    if Value then
-      Suspend
-    else
-      Resume;
-end;
-
-procedure TThread.Suspend;
-begin
-  if not FSuspended then begin
-    if FThreadID = GetCurrentThreadID then begin
-      FSuspended := true;
-      CurrentTM.SemaphoreWait(FSem);
-    end else begin
-      FSuspendedExternal := true;
-      SuspendThread(FHandle);
-    end;
-  end;
-end;
-
-
-procedure TThread.Resume;
-begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
-    end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
-end;
-
-
-procedure TThread.Terminate;
-begin
-  FTerminated := True;
-end;
-
-function TThread.WaitFor: Integer;
-begin
-  WRITE_DEBUG('waiting for thread ',FHandle);
-  WaitFor := WaitForThreadTerminate(FHandle, 0);
-  WRITE_DEBUG('thread terminated');
-end;
-
-procedure TThread.CallOnTerminate;
-begin
-  // no need to check if FOnTerminate <> nil, because
-  // thats already done in DoTerminate
-  FOnTerminate(self);
-end;
-
-procedure TThread.DoTerminate;
-begin
-  if Assigned(FOnTerminate) then
-    Synchronize(@CallOnTerminate);
-end;
-
-function TThread.GetPriority: TThreadPriority;
-var
-  P: Integer;
-  I: TThreadPriority;
-begin
-  P := ThreadGetPriority(FHandle);
-  Result := tpNormal;
-  for I := Low(TThreadPriority) to High(TThreadPriority) do
-    if Priorities[I] = P then
-      Result := I;
-end;
-
-
-procedure TThread.SetPriority(Value: TThreadPriority);
-begin
-  ThreadSetPriority(FHandle, Priorities[Value]);
-end;
-

+ 0 - 252
rtl/solaris/tthread.inc

@@ -1,252 +0,0 @@
-{
-   This file is part of the Free Pascal run time library.
-   (c) 2000-2003 by Marco van de Voort
-   member of the Free Pascal development team.
-
-   See the file COPYING.FPC, included in this distribution,
-   for details about the copyright.
-
-   TThread implementation old (1.0) and new (pthreads) style
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY;without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-}
-
-
-{
-  What follows, is a short description on my implementation of TThread.
-  Most information can also be found by reading the source and accompanying
-  comments.
-
-  A thread is created using BeginThread, which in turn calls
-  pthread_create. So the threads here are always posix threads.
-  Posix doesn't define anything for suspending threads as this is
-  inherintly unsafe. Just don't suspend threads at points they cannot
-  control. Therefore, I didn't implement .Suspend() if its called from
-  outside the threads execution flow (except on Linux _without_ NPTL).
-
-  The implementation for .suspend uses a semaphore, which is initialized
-  at thread creation. If the thread tries to suspend itself, we simply
-  let it wait on the semaphore until it is unblocked by someone else
-  who calls .Resume.
-
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
-
-  Johannes Berg <[email protected]>, Sunday, November 16 2003
-}
-
-{ ok, so this is a hack, but it works nicely. Just never use
-  a multiline argument with WRITE_DEBUG! }
-{$MACRO ON}
-{$IFDEF DEBUG_MT}
-{$define WRITE_DEBUG := writeln} // actually write something
-{$ELSE}
-{$define WRITE_DEBUG := //}      // just comment out those lines
-{$ENDIF}
-
-var
-  ThreadsInited: boolean = false;
-  CurrentTM: TThreadManager;
-  
-const
-  // stupid, considering its not even implemented...
-  Priorities: array [TThreadPriority] of Integer =
-   (-20,-19,-10,0,9,18,19);
-
-procedure InitThreads;
-begin
-  if not ThreadsInited then begin
-    GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-  end;
-end;
-
-procedure DoneThreads;
-begin
-  ThreadsInited := false;
-end;
-
-function ThreadFunc(parameter: Pointer): LongInt;
-var
-  LThread: TThread;
-  c: char;
-begin
-  WRITE_DEBUG('ThreadFunc is here...');
-  LThread := TThread(parameter);
-  WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
-  try
-    if LThread.FInitialSuspended then begin
-      CurrentTM.SemaphoreWait(LThread.FSem);
-      if not LThread.FSuspended then begin
-        LThread.FInitialSuspended := false;
-        WRITE_DEBUG('going into LThread.Execute');
-        LThread.Execute;
-      end;
-    end else begin
-      WRITE_DEBUG('going into LThread.Execute');
-      LThread.Execute;
-    end;
-  except
-    on e: exception do begin
-      WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
-      // not sure if we should really do this...
-      // but .Destroy was called, so why not try FreeOnTerminate?
-      if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
-    end;
-  end;
-  WRITE_DEBUG('thread done running');
-  Result := LThread.FReturnValue;
-  WRITE_DEBUG('Result is ',Result);
-  LThread.FFinished := True;
-  LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
-  end;
-  WRITE_DEBUG('thread func calling EndThread');
-  EndThread(Result);
-end;
-
-{ TThread }
-constructor TThread.Create(CreateSuspended: Boolean;
-                           const StackSize: SizeUInt = DefaultStackSize);
-begin
-  // lets just hope that the user doesn't create a thread
-  // via BeginThread and creates the first TThread Object in there!
-  InitThreads;
-  inherited Create;
-  FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
-  FSuspendedExternal := false;
-  FInitialSuspended := CreateSuspended;
-  FFatalException := nil;
-  WRITE_DEBUG('creating thread, self = ',PtrInt(self));
-  FHandle:= BeginThread(@ThreadFunc, Pointer(Self), FThreadID,StackSize);
-  WRITE_DEBUG('TThread.Create done');
-end;
-
-
-destructor TThread.Destroy;
-begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
-    raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
-  // if someone calls .Free on a thread with
-  // FreeOnTerminate, then don't crash!
-  FFreeOnTerminate := false;
-  if not FFinished and not FSuspended then begin
-    Terminate;
-    WaitFor;
-  end;
-  if (FInitialSuspended) then begin
-    // thread was created suspended but never woken up.
-    CurrentTM.SemaphorePost(FSem);
-    WaitFor;
-  end;
-  FFatalException.Free;
-  FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
-  inherited Destroy;
-end;
-
-procedure TThread.SetSuspended(Value: Boolean);
-begin
-  if Value <> FSuspended then
-    if Value then
-      Suspend
-    else
-      Resume;
-end;
-
-procedure TThread.Suspend;
-begin
-  if not FSuspended then begin
-    if FThreadID = GetCurrentThreadID then begin
-      FSuspended := true;
-      CurrentTM.SemaphoreWait(FSem);
-    end else begin
-      FSuspendedExternal := true;
-      SuspendThread(FHandle);
-    end;
-  end;
-end;
-
-
-procedure TThread.Resume;
-begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
-    end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
-end;
-
-
-procedure TThread.Terminate;
-begin
-  FTerminated := True;
-end;
-
-function TThread.WaitFor: Integer;
-begin
-  WRITE_DEBUG('waiting for thread ',FHandle);
-  WaitFor := WaitForThreadTerminate(FHandle, 0);
-  WRITE_DEBUG('thread terminated');
-end;
-
-procedure TThread.CallOnTerminate;
-begin
-  // no need to check if FOnTerminate <> nil, because
-  // thats already done in DoTerminate
-  FOnTerminate(self);
-end;
-
-procedure TThread.DoTerminate;
-begin
-  if Assigned(FOnTerminate) then
-    Synchronize(@CallOnTerminate);
-end;
-
-function TThread.GetPriority: TThreadPriority;
-var
-  P: Integer;
-  I: TThreadPriority;
-begin
-  P := ThreadGetPriority(FHandle);
-  Result := tpNormal;
-  for I := Low(TThreadPriority) to High(TThreadPriority) do
-    if Priorities[I] = P then
-      Result := I;
-end;
-
-
-procedure TThread.SetPriority(Value: TThreadPriority);
-begin
-  ThreadSetPriority(FHandle, Priorities[Value]);
-end;
-

+ 249 - 53
rtl/unix/cthreads.pp

@@ -18,6 +18,24 @@
 {$define dynpthreads} // Useless on BSD, since they are in libc
 {$define dynpthreads} // Useless on BSD, since they are in libc
 {$endif}
 {$endif}
 
 
+
+{ sem_init is best, since it does not consume any file descriptors.    }
+{ sem_open is second best, since it consumes only one file descriptor  }
+{ per semaphore.                                                       }
+{ If neither is available, pipe is used as fallback, which consumes 2  }
+{ file descriptors per semaphore.                                      }
+
+{ Darwin doesn't support nameless semaphores in at least }
+{ Mac OS X 10.4.8/Darwin 8.8                             }
+{$ifndef darwin}
+{$define has_sem_init}
+{$define has_sem_getvalue}
+{$else }
+{$ifdef darwin}
+{$define has_sem_open}
+{$endif}
+{$endif}
+
 unit cthreads;
 unit cthreads;
 interface
 interface
 {$S-}
 {$S-}
@@ -213,10 +231,10 @@ Type  PINTRTLEvent = ^TINTRTLEvent;
       { Initialize multithreading if not done }
       { Initialize multithreading if not done }
       if not IsMultiThread then
       if not IsMultiThread then
         begin
         begin
-          if (InterLockedExchange(longint(IsMultiThread),1) = 0) then
+          if (InterLockedExchange(longint(IsMultiThread),ord(true)) = 0) then
             begin
             begin
               { We're still running in single thread mode, setup the TLS }
               { We're still running in single thread mode, setup the TLS }
-               pthread_key_create(@TLSKey,nil);
+              pthread_key_create(@TLSKey,nil);
               InitThreadVars(@CRelocateThreadvar);
               InitThreadVars(@CRelocateThreadvar);
             end
             end
         end;
         end;
@@ -239,9 +257,11 @@ Type  PINTRTLEvent = ^TINTRTLEvent;
       // don't create detached, we need to be able to join (waitfor) on
       // don't create detached, we need to be able to join (waitfor) on
       // the newly created thread!
       // the newly created thread!
       //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
       //pthread_attr_setdetachstate(@thread_attr, PTHREAD_CREATE_DETACHED);
-      if pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0 then begin
-        threadid := TThreadID(0);
-      end;
+      if pthread_create(ppthread_t(@threadid), @thread_attr, @ThreadMain,ti) <> 0 then
+        begin
+          dispose(ti);
+          threadid := TThreadID(0);
+        end;
       CBeginThread:=threadid;
       CBeginThread:=threadid;
 {$ifdef DEBUG_MT}
 {$ifdef DEBUG_MT}
       writeln('BeginThread returning ',ptrint(CBeginThread));
       writeln('BeginThread returning ',ptrint(CBeginThread));
@@ -366,6 +386,130 @@ Type  PINTRTLEvent = ^TINTRTLEvent;
       end;
       end;
 
 
 
 
+{*****************************************************************************
+                           Semaphore routines
+*****************************************************************************}
+  
+
+procedure cSemaphoreWait(const FSem: Pointer);
+var
+  res: cint;
+  err: cint;
+{$if not defined(has_sem_init) and not defined(has_sem_open)}
+  b: byte;
+{$endif}
+begin
+{$if defined(has_sem_init) or defined(has_sem_open)}
+  repeat
+    res:=sem_wait(PSemaphore(FSem));
+    err:=fpgeterrno;
+  until (res<>-1) or (err<>ESysEINTR);
+{$else}
+  repeat
+    res:=fpread(PFilDes(FSem)^[0], b, 1);
+    err:=fpgeterrno;
+  until (res<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
+{$endif}
+end;
+
+procedure cSemaphorePost(const FSem: Pointer);
+{$if defined(has_sem_init) or defined(has_sem_open)}
+begin
+  sem_post(PSemaphore(FSem));
+end;
+{$else}
+var
+  writeres: cint;
+  err: cint;
+  b : byte;
+begin
+  b:=0;
+  repeat
+    writeres:=fpwrite(PFilDes(FSem)^[1], b, 1);
+    err:=fpgeterrno;
+  until (writeres<>-1) or ((err<>ESysEINTR) and (err<>ESysEAgain));
+end;
+{$endif}
+
+
+{$if defined(has_sem_open) and not defined(has_sem_init)}
+function cIntSemaphoreOpen(const name: pchar; initvalue: boolean): Pointer;
+var
+  err: cint;
+begin
+  repeat
+    cIntSemaphoreOpen := sem_open(name,O_CREAT,0,ord(initvalue));
+    err:=fpgeterrno;
+  until (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) or (err <> ESysEINTR);
+  if (ptrint(cIntSemaphoreOpen) <> SEM_FAILED) then
+    { immediately unlink so the semaphore will be destroyed when the }
+    { the process exits                                              }
+    sem_unlink(name)
+  else
+    cIntSemaphoreOpen:=NIL;
+end;
+{$endif}
+
+
+function cIntSemaphoreInit(initvalue: boolean): Pointer;
+{$if defined(has_sem_open) and not defined(has_sem_init)}
+var
+  tid: string[31];
+  semname: string[63];
+  err: cint;
+{$endif}
+begin
+{$ifdef has_sem_init}
+  cIntSemaphoreInit := GetMem(SizeOf(TSemaphore));
+  if sem_init(PSemaphore(cIntSemaphoreInit), 0, ord(initvalue)) <> 0 then
+    begin
+      FreeMem(cIntSemaphoreInit);
+      cIntSemaphoreInit:=NIL;
+    end;
+{$else}
+{$ifdef has_sem_open}
+  { avoid a potential temporary nameclash with another process/thread }
+  str(fpGetPid,semname);
+  str(ptruint(pthread_self),tid);
+  semname:='/FPC'+semname+'T'+tid+#0;
+  cIntSemaphoreInit:=cIntSemaphoreOpen(@semname[1],initvalue);
+{$else}
+  cIntSemaphoreInit := GetMem(SizeOf(TFilDes));
+  if (fppipe(PFilDes(cIntSemaphoreInit)^) <> 0) then
+    begin
+      FreeMem(cIntSemaphoreInit);
+      cIntSemaphoreInit:=nil;
+    end
+  else if initvalue then
+    cSemaphorePost(cIntSemaphoreInit);
+{$endif}
+{$endif}
+end;
+
+
+function cSemaphoreInit: Pointer;
+begin
+  cSemaphoreInit:=cIntSemaphoreInit(false);
+end;
+
+
+procedure cSemaphoreDestroy(const FSem: Pointer);
+begin
+{$ifdef has_sem_init}
+  sem_destroy(PSemaphore(FSem));
+  FreeMem(FSem);
+{$else}
+{$ifdef has_sem_open}
+  sem_close(PSemaphore(FSem));
+{$else has_sem_init}
+  fpclose(PFilDes(FSem)^[0]);
+  fpclose(PFilDes(FSem)^[1]);
+  FreeMem(FSem);
+{$endif}
+{$endif}
+end;
+
+
 {*****************************************************************************
 {*****************************************************************************
                            Heap Mutex Protection
                            Heap Mutex Protection
 *****************************************************************************}
 *****************************************************************************}
@@ -411,8 +555,8 @@ type
      TPthreadMutex = pthread_mutex_t;
      TPthreadMutex = pthread_mutex_t;
      Tbasiceventstate=record
      Tbasiceventstate=record
          FSem: Pointer;
          FSem: Pointer;
-         FManualReset: Boolean;
          FEventSection: TPthreadMutex;
          FEventSection: TPthreadMutex;
+         FManualReset: Boolean;
         end;
         end;
      plocaleventstate = ^tbasiceventstate;
      plocaleventstate = ^tbasiceventstate;
 //     peventstate=pointer;
 //     peventstate=pointer;
@@ -428,12 +572,35 @@ function IntBasicEventCreate(EventAttributes : Pointer; AManualReset,InitialStat
 var
 var
   MAttr : pthread_mutexattr_t;
   MAttr : pthread_mutexattr_t;
   res   : cint;
   res   : cint;
-
-
 begin
 begin
   new(plocaleventstate(result));
   new(plocaleventstate(result));
   plocaleventstate(result)^.FManualReset:=AManualReset;
   plocaleventstate(result)^.FManualReset:=AManualReset;
-  plocaleventstate(result)^.FSem:=New(PSemaphore);  //sem_t.
+{$ifdef has_sem_init}
+  plocaleventstate(result)^.FSem:=cIntSemaphoreInit(true);
+  if plocaleventstate(result)^.FSem=nil then
+    begin
+      FreeMem(result);
+      runerror(6);
+    end;
+{$else}
+{$ifdef has_sem_open}
+  plocaleventstate(result)^.FSem:=cIntSemaphoreOpen(PChar(Name),InitialState);
+  if (plocaleventstate(result)^.FSem = NIL) then
+    begin
+      FreeMem(result);
+      runerror(6);
+    end;
+{$else}
+  plocaleventstate(result)^.FSem:=cSemaphoreInit;
+  if (plocaleventstate(result)^.FSem = NIL) then
+    begin
+      FreeMem(result);
+      runerror(6);
+    end;
+  if InitialState then
+    cSemaphorePost(plocaleventstate(result)^.FSem);
+{$endif}
+{$endif}
 //  plocaleventstate(result)^.feventsection:=nil;
 //  plocaleventstate(result)^.feventsection:=nil;
   res:=pthread_mutexattr_init(@MAttr);
   res:=pthread_mutexattr_init(@MAttr);
   if res=0 then
   if res=0 then
@@ -448,35 +615,93 @@ begin
     res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
     res:=pthread_mutex_init(@plocaleventstate(result)^.feventsection,nil);
   pthread_mutexattr_destroy(@MAttr);
   pthread_mutexattr_destroy(@MAttr);
   if res <> 0 then
   if res <> 0 then
-    runerror(6);
-  if sem_init(psem_t(plocaleventstate(result)^.FSem),ord(False),Ord(InitialState)) <> 0 then
-    runerror(6);
+    begin
+      cSemaphoreDestroy(plocaleventstate(result)^.FSem);
+      FreeMem(result);
+      runerror(6);
+    end;
 end;
 end;
 
 
 procedure Intbasiceventdestroy(state:peventstate);
 procedure Intbasiceventdestroy(state:peventstate);
 
 
 begin
 begin
-  sem_destroy(psem_t(  plocaleventstate(state)^.FSem));
+  cSemaphoreDestroy(plocaleventstate(state)^.FSem);
+  FreeMem(state);
 end;
 end;
 
 
 procedure IntbasiceventResetEvent(state:peventstate);
 procedure IntbasiceventResetEvent(state:peventstate);
 
 
+{$if defined(has_sem_init) or defined(has_sem_open)}
+var
+  res: cint;
+  err: cint;
 begin
 begin
-  While sem_trywait(psem_t( plocaleventstate(state)^.FSem))=0 do
-    ;
+  repeat
+    res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
+    err:=fpgeterrno;
+  until (res<>0) and ((res<>-1) or (err<>ESysEINTR));
+{$else has_sem_init or has_sem_open}
+var
+  fds: TFDSet;
+  tv : timeval;
+begin
+  tv.tv_sec:=0;
+  tv.tv_usec:=0;
+  fpFD_ZERO(fds);
+  fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
+  pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
+  Try
+    while fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv) > 0 do
+      cSemaphoreWait(plocaleventstate(state)^.FSem);
+  finally
+    pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
+  end;
+{$endif has_sem_init or has_sem_open}
 end;
 end;
 
 
 procedure IntbasiceventSetEvent(state:peventstate);
 procedure IntbasiceventSetEvent(state:peventstate);
 
 
 Var
 Var
+{$if defined(has_sem_init) or defined(has_sem_open)}
   Value : Longint;
   Value : Longint;
-
+  res : cint;
+  err : cint;
+{$else}
+  fds: TFDSet;
+  tv : timeval;
+{$endif}
 begin
 begin
   pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
   pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
   Try
   Try
-    sem_getvalue(plocaleventstate(state)^.FSem,@value);
-    if Value=0 then
-      sem_post(psem_t( plocaleventstate(state)^.FSem));
+{$if defined(has_sem_init) or defined(has_sem_open)}
+    if (sem_getvalue(plocaleventstate(state)^.FSem,@value) <> -1) then
+      begin
+        if Value=0 then
+          cSemaphorePost(plocaleventstate(state)^.FSem);
+      end
+    else if (fpgeterrno = ESysENOSYS) then
+      { not yet implemented on Mac OS X 10.4.8 }
+      begin
+        repeat
+          res:=sem_trywait(psem_t(plocaleventstate(state)^.FSem));
+          err:=fpgeterrno;
+        until ((res<>-1) or (err<>ESysEINTR));
+        { now we've either decreased the semaphore by 1 (if it was  }
+        { not zero), or we've done nothing (if it was already zero) }
+        { -> increase by 1 and we have the same result as           }
+        { increasing by 1 only if it was 0                          }
+        cSemaphorePost(plocaleventstate(state)^.FSem);
+      end
+    else
+      runerror(6);
+{$else has_sem_init or has_sem_open}
+    tv.tv_sec:=0;
+    tv.tv_usec:=0;
+    fpFD_ZERO(fds);
+    fpFD_SET(PFilDes(plocaleventstate(state)^.FSem)^[0],fds);
+    if fpselect(PFilDes(plocaleventstate(state)^.FSem)^[0],@fds,nil,nil,@tv)=0 then
+      cSemaphorePost(plocaleventstate(state)^.FSem);
+{$endif has_sem_init or has_sem_open}
   finally
   finally
     pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
     pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
   end;
   end;
@@ -489,16 +714,16 @@ begin
     result:=wrError
     result:=wrError
   else
   else
     begin
     begin
-      sem_wait(psem_t(plocaleventstate(state)^.FSem));
+      cSemaphoreWait(plocaleventstate(state)^.FSem);
       result:=wrSignaled;
       result:=wrSignaled;
       if plocaleventstate(state)^.FManualReset then
       if plocaleventstate(state)^.FManualReset then
         begin
         begin
           pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
           pthread_mutex_lock(@plocaleventstate(state)^.feventsection);
           Try
           Try
-              intbasiceventresetevent(State);
-              sem_post(psem_t( plocaleventstate(state)^.FSem));
-            Finally
-          pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
+            intbasiceventresetevent(State);
+            cSemaphorePost(plocaleventstate(state)^.FSem);
+          Finally
+            pthread_mutex_unlock(@plocaleventstate(state)^.feventsection);
         end;
         end;
       end;
       end;
     end;
     end;
@@ -582,36 +807,7 @@ procedure intRTLEventWaitForTimeout(AEvent: PRTLEvent;timeout : longint);
     if (errres=0) or (errres=ESysETIMEDOUT) then
     if (errres=0) or (errres=ESysETIMEDOUT) then
       pthread_mutex_unlock(@p^.mutex);
       pthread_mutex_unlock(@p^.mutex);
   end;
   end;
-  
-function cSemaphoreInit: Pointer;
-var
-  s: PSemaphore;
-begin
-  GetMem(s, SizeOf(TSemaphore));
-  if sem_init(s, 0, 0) = 0 then
-    cSemaphoreInit:=s
-  else
-    cSemaphoreInit:=nil;
-end;
 
 
-procedure cSemaphoreWait(const FSem: Pointer);
-begin
-  sem_wait(PSemaphore(FSem));
-end;
-
-procedure cSemaphorePost(const FSem: Pointer);
-begin
-  sem_post(PSemaphore(FSem));
-end;
-
-procedure cSemaphoreDestroy(const FSem: Pointer);
-var
-  s: PSemaphore;
-begin
-  s:=FSem;
-  sem_destroy(PSemaphore(FSem));
-  FreeMem(s);
-end;
 
 
 type
 type
   threadmethod = procedure of object;
   threadmethod = procedure of object;

+ 64 - 49
rtl/darwin/tthread.inc → rtl/unix/tthread.inc

@@ -1,8 +1,10 @@
 {
 {
-    This file is part of the Free Component Library (FCL)
+    This file is part of the Free Pascal run time library.
     Copyright (c) 1999-2000 by Peter Vreman
     Copyright (c) 1999-2000 by Peter Vreman
+    Copyright (c) 2006 by Jonas Maebe
+    members of the Free Pascal development team.
 
 
-    Darwin TThread implementation
+    Generic *nix TThread implementation
 
 
     See the file COPYING.FPC, included in this distribution,
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
     for details about the copyright.
@@ -14,6 +16,7 @@
  **********************************************************************}
  **********************************************************************}
 
 
 
 
+
 {
 {
   What follows, is a short description on my implementation of TThread.
   What follows, is a short description on my implementation of TThread.
   Most information can also be found by reading the source and accompanying
   Most information can also be found by reading the source and accompanying
@@ -31,25 +34,6 @@
   let it wait on the semaphore until it is unblocked by someone else
   let it wait on the semaphore until it is unblocked by someone else
   who calls .Resume.
   who calls .Resume.
 
 
-  If a thread is supposed to be suspended (from outside its own path of
-  execution) on a system where the symbol LINUX is defined, two things
-  are possible.
-  1) the system has the LinuxThreads pthread implementation
-  2) the system has NPTL as the pthread implementation.
-
-  In the first case, each thread is a process on its own, which as far as
-  know actually violates posix with respect to signal handling.
-  But we can detect this case, because getpid(2) will
-  return a different PID for each thread. In that case, sending SIGSTOP
-  to the PID associated with a thread will actually stop that thread
-  only.
-  In the second case, this is not possible. But getpid(2) returns the same
-  PID across all threads, which is detected, and TThread.Suspend() does
-  nothing in that case. This should probably be changed, but I know of
-  no way to suspend a thread when using NPTL.
-
-  If the symbol LINUX is not defined, then the unimplemented
-  function SuspendThread is called.
 
 
   Johannes Berg <[email protected]>, Sunday, November 16 2003
   Johannes Berg <[email protected]>, Sunday, November 16 2003
 }
 }
@@ -73,10 +57,12 @@ const
 
 
 procedure InitThreads;
 procedure InitThreads;
 begin
 begin
-  if not ThreadsInited then begin
+  { This is not thread safe, but it doesn't matter if this is executed }
+  { multiple times. Conversely, if one thread goes by this without the }
+  { operation having been finished by another thread already, it will  }
+  { use an uninitialised thread manager -> leave as it is              }
+  if not ThreadsInited then
     GetThreadManager(CurrentTM);
     GetThreadManager(CurrentTM);
-    ThreadsInited := true;
-  end;
 end;
 end;
 
 
 procedure DoneThreads;
 procedure DoneThreads;
@@ -92,9 +78,15 @@ begin
   LThread := TThread(parameter);
   LThread := TThread(parameter);
   WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
   WRITE_DEBUG('thread initing, parameter = ', LongInt(LThread));
   try
   try
+    // wait until AfterConstruction has been called, so we cannot
+    // free ourselves before TThread.Create has finished
+    // (since that one may check our VTM in case of $R+, and
+    //  will call the AfterConstruction method in all cases)
+    LThread.Suspend;
+    WRITE_DEBUG('AfterConstruction should have been called for ',ptrint(lthread));
     if LThread.FInitialSuspended then
     if LThread.FInitialSuspended then
       begin
       begin
-        CurrentTM.SemaphoreWait(LThread.FSem);
+        LThread.Suspend;
         if not(LThread.FTerminated) then
         if not(LThread.FTerminated) then
           begin
           begin
             if not LThread.FSuspended then
             if not LThread.FSuspended then
@@ -113,7 +105,7 @@ begin
   except
   except
     on e: exception do begin
     on e: exception do begin
       WRITE_DEBUG('got exception: ',e.message);
       WRITE_DEBUG('got exception: ',e.message);
-      LThread.FFatalException :=  TObject(AcquireExceptionObject);
+      LThread.FFatalException := TObject(AcquireExceptionObject);
       // not sure if we should really do this...
       // not sure if we should really do this...
       // but .Destroy was called, so why not try FreeOnTerminate?
       // but .Destroy was called, so why not try FreeOnTerminate?
       if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
       if e is EThreadDestroyCalled then LThread.FFreeOnTerminate := true;
@@ -124,14 +116,19 @@ begin
   WRITE_DEBUG('Result is ',Result);
   WRITE_DEBUG('Result is ',Result);
   LThread.FFinished := True;
   LThread.FFinished := True;
   LThread.DoTerminate;
   LThread.DoTerminate;
-  if LThread.FreeOnTerminate then begin
-    WRITE_DEBUG('Thread should be freed');
-    LThread.Free;
-    WRITE_DEBUG('Thread freed');
+  if LThread.FreeOnTerminate then
+    begin
+      WRITE_DEBUG('Thread ',ptrint(lthread),' should be freed');
+      LThread.Free;
+      WRITE_DEBUG('Thread freed');
 //    tthread.destroy already frees all things and terminates the thread
 //    tthread.destroy already frees all things and terminates the thread
 //    WRITE_DEBUG('thread func calling EndThread');
 //    WRITE_DEBUG('thread func calling EndThread');
 //    EndThread(Result);
 //    EndThread(Result);
-  end;
+    end
+  else
+    begin
+      FlushThread;
+    end;
 end;
 end;
 
 
 { TThread }
 { TThread }
@@ -143,35 +140,49 @@ begin
   InitThreads;
   InitThreads;
   inherited Create;
   inherited Create;
   FSem := CurrentTM.SemaphoreInit();
   FSem := CurrentTM.SemaphoreInit();
-  FSuspended := CreateSuspended;
+  if FSem = nil then
+    raise EThread.create('Semaphore init failed (possibly too many concurrent threads)');
+  FSuspended := True;
   FSuspendedExternal := false;
   FSuspendedExternal := false;
   FInitialSuspended := CreateSuspended;
   FInitialSuspended := CreateSuspended;
   FFatalException := nil;
   FFatalException := nil;
   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);
-  WRITE_DEBUG('TThread.Create done');
+  if FHandle = TThreadID(0) then
+    raise EThread.create('Failed to create new thread');
+  WRITE_DEBUG('TThread.Create done, fhandle = ', ptrint(fhandle));
 end;
 end;
 
 
 
 
 destructor TThread.Destroy;
 destructor TThread.Destroy;
 begin
 begin
-  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not ffinished then begin
+  if (FSem = nil) then
+    { exception in constructor }
+    begin
+      inherited destroy;
+      exit;
+    end;
+  CurrentTM.SemaphoreDestroy(FSem);
+  if (FHandle = TThreadID(0)) then
+  { another exception in constructor }
+    begin
+      inherited destroy;
+      exit;
+    end;
+  if (FThreadID = GetCurrentThreadID) and not(FFreeOnTerminate) and not FFinished then
     raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
     raise EThreadDestroyCalled.Create('A thread cannot destroy itself except by setting FreeOnTerminate and leaving!');
-  end;
   // if someone calls .Free on a thread with
   // if someone calls .Free on a thread with
   // FreeOnTerminate, then don't crash!
   // FreeOnTerminate, then don't crash!
   FFreeOnTerminate := false;
   FFreeOnTerminate := false;
-  if (FInitialSuspended) then
-    // thread was created suspended but never woken up.
-    Resume;
-  if not FFinished and not FSuspended then
+  if not FFinished then
     begin
     begin
       Terminate;
       Terminate;
+      if (FInitialSuspended) then
+        Resume;
       WaitFor;
       WaitFor;
     end;
     end;
   FFatalException.Free;
   FFatalException.Free;
   FFatalException := nil;
   FFatalException := nil;
-  CurrentTM.SemaphoreDestroy(FSem);
   { threadvars have been released by cthreads.ThreadMain -> DoneThread }
   { threadvars have been released by cthreads.ThreadMain -> DoneThread }
   inherited Destroy;
   inherited Destroy;
 end;
 end;
@@ -201,15 +212,20 @@ end;
 
 
 procedure TThread.Resume;
 procedure TThread.Resume;
 begin
 begin
-  if (not FSuspendedExternal) then begin
-    if FSuspended then begin
-      FSuspended := False;
-      CurrentTM.SemaphorePost(FSem);
+  if (not FSuspendedExternal) then
+    begin
+      if FSuspended then
+        begin
+          WRITE_DEBUG('resuming ',ptrint(self));
+          FSuspended := False;
+          CurrentTM.SemaphorePost(FSem);
+        end;
+    end
+  else
+    begin
+      FSuspendedExternal := false;
+      ResumeThread(FHandle);
     end;
     end;
-  end else begin
-    FSuspendedExternal := false;
-    ResumeThread(FHandle);
-  end;
 end;
 end;
 
 
 
 
@@ -255,4 +271,3 @@ procedure TThread.SetPriority(Value: TThreadPriority);
 begin
 begin
   ThreadSetPriority(FHandle, Priorities[Value]);
   ThreadSetPriority(FHandle, Priorities[Value]);
 end;
 end;
-

+ 46 - 0
tests/test/tbrtlevt.pp

@@ -0,0 +1,46 @@
+{$mode objfpc}
+
+uses
+{$ifdef unix}
+ cthreads,
+{$endif}
+  sysutils,
+  classes;
+
+type
+  tc = class(tthread)
+    procedure execute; override;
+  end;
+
+var
+  event: pEventState;
+  waiting: boolean;
+
+procedure tc.execute;
+begin
+  { avoid deadlocks/bugs from causing this test to never quit }
+  sleep(1000*20);
+  halt(1);
+end;
+
+
+begin
+  tc.create(false);
+  event := BasicEventCreate(nil,false,false,'bla');;
+  basiceventSetEvent(event);
+  if (basiceventWaitFor(cardinal(-1),event) <> 0) then
+    begin
+      writeln('error');
+      halt(1);
+    end;
+  { shouldn't change anything }
+  basiceventResetEvent(event);
+  basiceventSetEvent(event);
+  { shouldn't change anything }
+  basiceventSetEvent(event);
+  if (basiceventWaitFor(cardinal(-1),event) <> 0) then
+    begin
+      writeln('error');
+      halt(1);
+    end;
+end.