Bladeren bron

* Hacking TLS callbacks into proper shape:
- Do not call DLL hooks from exe callback handler.
- Do not call SysInitMultitheading from DLL_THREAD_ATTACH handler, it may only be called from the main thread. See comments in syswin.inc for details. Reverts rev. 17892.
+ To guarantee that SysInitMultithreading is called from the main thread, assume executables always multithreaded, just like DLLs are.
- Also removed all checks with MainThreadIdWin32, except checks for double DLL_PROCESS_DETACH in DLLs. They duplicate what Windows already does (the main thread invokes only PROCESS_ATTACH/PROCESS_DETACH and never THREAD_ATTACH/THREAD_DETACH callbacks).
* Attempts to avoid double initialization/finalization (once in callback, second time in normal control flow).
* Net result: webtbs/tw2423 and webtbs/tw15530 fixed, webtbs/tw3661 broken (because heaptrc does not support checking pointers in TLS area, and with 'always multithreaded' apps the Output varible moves into TLS).

git-svn-id: trunk@17938 -

sergei 14 jaren geleden
bovenliggende
commit
138c2b6b0a
2 gewijzigde bestanden met toevoegingen van 34 en 49 verwijderingen
  1. 8 5
      rtl/win/systhrd.inc
  2. 26 44
      rtl/win/syswin.inc

+ 8 - 5
rtl/win/systhrd.inc

@@ -225,16 +225,17 @@ var
       var
         ti : tthreadinfo;
       begin
-        { Allocate local thread vars, this must be the first thing,
-          because the exception management and io depends on threadvars }
-        SysAllocateThreadVars;
-
         { Copy parameter to local data }
         ti:=pthreadinfo(param)^;
-        dispose(pthreadinfo(param));
 
+        { Allocate local thread vars, this must be the first thing,
+          because the exception management and io depends on threadvars }
+{$ifndef FPC_USE_TLS_DIRECTORY}
+        SysAllocateThreadVars;
         { Initialize thread }
         InitThread(ti.stklen);
+{$endif}
+        dispose(pthreadinfo(param));
 
         { Start thread function }
 {$ifdef DEBUG_MT}
@@ -541,7 +542,9 @@ begin
     end;
   SetThreadManager(WinThreadManager);
   ThreadID := GetCurrentThreadID;
+{$ifndef FPC_USE_TLS_DIRECTORY}
   if IsLibrary then
+{$endif}
     SysInitMultithreading;
 {$IFDEF SUPPORT_WIN95}
   { Try to find TryEnterCriticalSection function }

+ 26 - 44
rtl/win/syswin.inc

@@ -50,17 +50,14 @@ function Dll_entry{$ifdef FPC_HAS_INDIRECT_MAIN_INFORMATION}(const info : TEntry
          end;
        DLL_THREAD_ATTACH :
          begin
-           if Win32GetCurrentThreadId <> MainThreadIdWin32 then
-           begin
-             { Initialize multithreading if not done }
-             SysInitMultithreading;
-             { Allocate Threadvars  }
-             SysAllocateThreadVars;
-
-             { NS : no idea what is correct to pass here - pass dummy value for now }
-             { passing a dummy is ok, the correct value is read from the coff header of SysInstance (FK) }
-             InitThread($1000000); { Assume everything is idempotent there, as the thread could have been created with BeginThread... }
-           end;
+           { SysInitMultithreading must not be called here,
+             see comments in exec_tls_callback below }
+           { Allocate Threadvars  }
+           SysAllocateThreadVars;
+
+           { NS : no idea what is correct to pass here - pass dummy value for now }
+           { passing a dummy is ok, the correct value is read from the coff header of SysInstance (FK) }
+           InitThread($1000000); { Assume everything is idempotent there, as the thread could have been created with BeginThread... }
 
            if assigned(Dll_Thread_Attach_Hook) then
              Dll_Thread_Attach_Hook(DllParam);
@@ -71,7 +68,7 @@ function Dll_entry{$ifdef FPC_HAS_INDIRECT_MAIN_INFORMATION}(const info : TEntry
            if assigned(Dll_Thread_Detach_Hook) then
              Dll_Thread_Detach_Hook(DllParam);
            { Release Threadvars }
-           if Win32GetCurrentThreadId<>MainThreadIdWin32 then
+           if TlsGetValue(TLSKey)<>nil then
              DoneThread; { Assume everything is idempotent there }
            Dll_entry:=true; { return value is ignored }
          end;
@@ -104,8 +101,6 @@ Procedure ExitDLL(Exitcode : longint);
 { Process TLS callback function }
 { This is only useful for executables
   for DLLs, DLL_Entry gets called. PM }
-const
-   Thread_count : longint = 0;
 
 procedure Exec_Tls_callback(Handle : pointer; reason : Dword; Reserved : pointer);
   stdcall; [public,alias:'_FPC_Tls_Callback'];
@@ -115,48 +110,35 @@ procedure Exec_Tls_callback(Handle : pointer; reason : Dword; Reserved : pointer
      case reason of
        DLL_PROCESS_ATTACH :
          begin
-           MainThreadIdWin32 := Win32GetCurrentThreadId;
          end;
        DLL_THREAD_ATTACH :
          begin
-           inclocked(Thread_count);
-           if Win32GetCurrentThreadId <> MainThreadIdWin32 then
-           begin
-             { Initialize multithreading if not done }
-             SysInitMultithreading;
-             { Allocate Threadvars  }
-             SysAllocateThreadVars;
-
-             { NS : no idea what is correct to pass here - pass dummy value for now }
-             { passing a dummy is ok, the correct value is read from the coff header of SysInstance (FK) }
-             InitThread($1000000); { Assume everything is idempotent there, as the thread could have been created with BeginThread... }
-           end;
-
-           if assigned(Dll_Thread_Attach_Hook) then
-             Dll_Thread_Attach_Hook(DllParam);
-        end;
+         {  !!! SysInitMultithreading must NOT be called here. Windows guarantees that
+            the main thread invokes PROCESS_ATTACH, not THREAD_ATTACH. So this always
+            executes in non-main thread. SysInitMultithreading() here will cause
+            initial threadvars to be copied to TLS of non-main thread, and threadvars
+            of the main thread will be reinitialized upon the next access with zeroes,
+            ending up in a delayed failure which is very hard to debug.
+            Fortunately this nasty scenario can happen only when the first non-main thread
+            was created outside of RTL (Sergei).
+         }
+           { Allocate Threadvars  }
+           SysAllocateThreadVars;
+
+           { NS : no idea what is correct to pass here - pass dummy value for now }
+           { passing a dummy is ok, the correct value is read from the coff header of SysInstance (FK) }
+           InitThread($1000000); { Assume everything is idempotent there, as the thread could have been created with BeginThread... }
+         end;
        DLL_THREAD_DETACH :
          begin
-           declocked(Thread_count);
-           if assigned(Dll_Thread_Detach_Hook) then
-             Dll_Thread_Detach_Hook(DllParam);
-           { Release Threadvars }
-           if Win32GetCurrentThreadId<>MainThreadIdWin32 then
+           if TlsGetValue(TLSKey)<>nil then
              DoneThread; { Assume everything is idempotent there }
          end;
        DLL_PROCESS_DETACH :
          begin
-	   if MainThreadIDWin32=0 then // already been here.
-             exit;
-           If SetJmp(DLLBuf) = 0 then
-             FPC_Do_Exit;
-           if assigned(Dll_Process_Detach_Hook) then
-             Dll_Process_Detach_Hook(DllParam);
-
            DoneThread;
            { Free TLS resources used by ThreadVars }
            SysFiniMultiThreading;
-           MainThreadIDWin32:=0;
          end;
      end;
   end;