NSAutoreleasePool instance should be added to all threads. (#52023)
authorAaron Robinson <arobins@microsoft.com>
Mon, 10 May 2021 20:31:39 +0000 (13:31 -0700)
committerGitHub <noreply@github.com>
Mon, 10 May 2021 20:31:39 +0000 (13:31 -0700)
Add an NSAutoreleasePool to all managed create threads including the Main and Finalizer.

This expands the current support where support was only added to ThreadPool threads.

New feature switch was created and the ThreadPool one was removed.
 - System.Threading.Thread.EnableAutoreleasePool

Updated AutoReleaseTest for the new scenarios.

32 files changed:
docs/workflow/trimming/feature-switches.md
src/coreclr/clr.featuredefines.props
src/coreclr/clrdefinitions.cmake
src/coreclr/vm/assembly.cpp
src/coreclr/vm/ceemain.cpp
src/coreclr/vm/corelib.h
src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
src/coreclr/vm/finalizerthread.cpp
src/coreclr/vm/gcenv.ee.cpp
src/coreclr/vm/proftoeeinterfaceimpl.cpp
src/coreclr/vm/threads.cpp
src/coreclr/vm/threads.h
src/coreclr/vm/threadsuspend.cpp
src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.OSX.xml
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Threading/AutoreleasePool.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.AutoreleasePool.OSX.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.OSX.xml [new file with mode: 0644]
src/mono/mono/metadata/class-internals.h
src/mono/mono/metadata/domain.c
src/mono/mono/metadata/gc.c
src/mono/mono/metadata/object-internals.h
src/mono/mono/metadata/object.c
src/mono/mono/metadata/threads-types.h
src/mono/mono/metadata/threads.c
src/tests/Common/CLRTest.Execute.Bash.targets
src/tests/Common/CLRTest.Execute.Batch.targets
src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.cs
src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj

index 84f4aad6faba868289a6a08e5c8ca35a37fc45dd..bc9390022812bd563e62492418bbc93099e35897 100644 (file)
@@ -17,7 +17,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif
 | HttpActivityPropagationSupport | System.Net.Http.EnableActivityPropagation | Any dependency related to diagnostics support for System.Net.Http is trimmed when set to false |
 | UseNativeHttpHandler | System.Net.Http.UseNativeHttpHandler | HttpClient uses by default platform native implementation of HttpMessageHandler if set to true. |
 | StartupHookSupport | System.StartupHookProvider.IsSupported | Startup hooks are disabled when set to false. Startup hook related functionality can be trimmed. |
-| TBD | System.Threading.ThreadPool.EnableDispatchAutoreleasePool | When set to true, creates an NSAutoreleasePool around each thread pool work item on applicable platforms. |
+| TBD | System.Threading.Thread.EnableAutoreleasePool | When set to true, creates an NSAutoreleasePool for each thread and thread pool work item on applicable platforms. |
 | CustomResourceTypesSupport | System.Resources.ResourceManager.AllowCustomResourceTypes | Use of custom resource types is disabled when set to false. ResourceManager code paths that use reflection for custom types can be trimmed. |
 | EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | BinaryFormatter serialization support is trimmed when set to false. |
 | BuiltInComInteropSupport | System.Runtime.InteropServices.BuiltInComInterop.IsSupported | Built-in COM support is trimmed when set to false. |
index dcd11139bfa9e2b80be36f1331203687063db9f6..67a5eab17c8684e86af7c80b2bcc1cd949fe03f3 100644 (file)
         <FeatureInstantiatingStubAsIL Condition="'$(Platform)' != 'x86'">true</FeatureInstantiatingStubAsIL>
     </PropertyGroup>
 
+    <PropertyGroup Condition="'$(TargetsOSX)' == 'true' OR '$(TargetsMacCatalyst)' == 'true' OR '$(TargetsiOS)' == 'true' OR '$(TargetstvOS)' == 'true'">
+        <FeatureObjCMarshal>true</FeatureObjCMarshal>
+    </PropertyGroup>
+
     <PropertyGroup>
         <DefineConstants Condition="'$(FeatureArrayStubAsIL)' == 'true'">$(DefineConstants);FEATURE_ARRAYSTUB_AS_IL</DefineConstants>
         <DefineConstants Condition="'$(FeatureMulticastStubAsIL)' == 'true'">$(DefineConstants);FEATURE_MULTICASTSTUB_AS_IL</DefineConstants>
@@ -44,6 +48,7 @@
         <DefineConstants Condition="'$(FeatureComWrappers)' == 'true'">$(DefineConstants);FEATURE_COMWRAPPERS</DefineConstants>
         <DefineConstants Condition="'$(FeatureCominterop)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP</DefineConstants>
         <DefineConstants Condition="'$(FeatureCominteropApartmentSupport)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT</DefineConstants>
+        <DefineConstants Condition="'$(FeatureObjCMarshal)' == 'true'">$(DefineConstants);FEATURE_OBJCMARSHAL</DefineConstants>
         <DefineConstants Condition="'$(FeatureManagedEtw)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW</DefineConstants>
         <DefineConstants Condition="'$(FeatureManagedEtwChannels)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS</DefineConstants>
         <DefineConstants Condition="'$(FeaturePerfTracing)' == 'true'">$(DefineConstants);FEATURE_PERFTRACING</DefineConstants>
index 008332d3443f3363ddf85606110a7044a6521b31..dfc2f617b3b37a12f18488d5ade5b63dc0a8373a 100644 (file)
@@ -93,6 +93,10 @@ if(CLR_CMAKE_TARGET_WIN32)
     add_definitions(-DFEATURE_COMINTEROP_UNMANAGED_ACTIVATION)
 endif(CLR_CMAKE_TARGET_WIN32)
 
+if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
+    add_definitions(-DFEATURE_OBJCMARSHAL)
+endif(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
+
 add_definitions(-DFEATURE_BASICFREEZE)
 add_definitions(-DFEATURE_CORECLR)
 add_definitions(-DFEATURE_CORESYSTEM)
index 542de57517f574e91c0c10aa7d920edc52dd69be..6f45022d10b7778f60fa4c937443a54de4be5c2f 100644 (file)
@@ -1656,9 +1656,17 @@ INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThre
             AppDomain * pDomain = pThread->GetDomain();
             pDomain->SetRootAssembly(pMeth->GetAssembly());
 
+            // Perform additional managed thread initialization.
+            // This would is normally done in the runtime when a managed
+            // thread is started, but is done here instead since the
+            // Main thread wasn't started by the runtime.
+            Thread::InitializationForManagedThreadInNative(pThread);
+
             RunStartupHooks();
 
             hr = RunMain(pMeth, 1, &iRetVal, stringArgs);
+
+            Thread::CleanUpForManagedThreadInNative(pThread);
         }
     }
 
index 1a94c44f2a270a54f8a67bb1f71595da86b75ebe..eb31d0f928de5e02488f41064743b86f1ee18749 100644 (file)
@@ -1035,7 +1035,7 @@ void EEStartupHelper()
                                                 g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE);
 #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
 
-#endif // CROSSGEN_COMPILE
+#endif // !CROSSGEN_COMPILE
 
         g_fEEStarted = TRUE;
         g_EEStartupStatus = S_OK;
@@ -1061,7 +1061,6 @@ void EEStartupHelper()
 
         // Perform CoreLib consistency check if requested
         g_CoreLib.CheckExtended();
-
 #endif // _DEBUG
 
 #endif // !CROSSGEN_COMPILE
index 1821750337f4b901249f46ac8dea997a90bad66c..86b99fcb832015beec29d7ce676b4f82d820c2bf 100644 (file)
@@ -881,6 +881,11 @@ DEFINE_FIELD_U(_priority,                 ThreadBaseObject,   m_Priority)
 DEFINE_CLASS(THREAD,                Threading,              Thread)
 DEFINE_METHOD(THREAD,               INTERNAL_GET_CURRENT_THREAD,             InternalGetCurrentThread,                    SM_RetIntPtr)
 DEFINE_METHOD(THREAD,               START_CALLBACK,                          StartCallback,                               IM_RetVoid)
+#ifdef FEATURE_OBJCMARSHAL
+DEFINE_CLASS(AUTORELEASEPOOL,       Threading,              AutoreleasePool)
+DEFINE_METHOD(AUTORELEASEPOOL,      CREATEAUTORELEASEPOOL,  CreateAutoreleasePool,  SM_RetVoid)
+DEFINE_METHOD(AUTORELEASEPOOL,      DRAINAUTORELEASEPOOL,   DrainAutoreleasePool,   SM_RetVoid)
+#endif // FEATURE_OBJCMARSHAL
 
 DEFINE_CLASS(IOCB_HELPER,              Threading,            _IOCompletionCallback)
 DEFINE_METHOD(IOCB_HELPER,             PERFORM_IOCOMPLETION_CALLBACK,        PerformIOCompletionCallback,          SM_UInt_UInt_PtrNativeOverlapped_RetVoid)
index cc71b577a74c7fdc5e04dff0be11ca7d1e1f6dfc..22c6826d7be8e9a3d0efb22051dbb0e6bf221f84 100644 (file)
@@ -2745,12 +2745,8 @@ ep_rt_thread_setup (void)
 {
        STATIC_CONTRACT_NOTHROW;
 
-       EX_TRY
-       {
-               SetupThread ();
-       }
-       EX_CATCH {}
-       EX_END_CATCH(SwallowAllExceptions);
+       Thread* thread_handle = SetupThreadNoThrow ();
+       EP_ASSERT (thread_handle != NULL);
 }
 
 static
index b09b028c9ae1a445f5753f8fc1367dd9150d4c63..a303400cbd74479b1656bcce4d80f7a891acab86 100644 (file)
@@ -222,6 +222,7 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event)
 }
 
 static BOOL s_FinalizerThreadOK = FALSE;
+static BOOL s_InitializedFinalizerThreadForPlatform = FALSE;
 
 VOID FinalizerThread::FinalizerThreadWorker(void *args)
 {
@@ -289,6 +290,15 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args)
                 bPriorityBoosted = TRUE;
         }
 
+        // The Finalizer thread is started very early in EE startup. We deferred
+        // some initialization until a point we are sure the EE is up and running. At
+        // this point we make a single attempt and if it fails won't try again.
+        if (!s_InitializedFinalizerThreadForPlatform)
+        {
+            s_InitializedFinalizerThreadForPlatform = TRUE;
+            Thread::InitializationForManagedThreadInNative(GetFinalizerThread());
+        }
+
         JitHost::Reclaim();
 
         GetFinalizerThread()->DisablePreemptiveGC();
@@ -330,6 +340,9 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args)
         // acceptable.
         SignalFinalizationDone(TRUE);
     }
+
+    if (s_InitializedFinalizerThreadForPlatform)
+        Thread::CleanUpForManagedThreadInNative(GetFinalizerThread());
 }
 
 DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args)
index 9efb8648a92b99575223c135022a70323ab42604..1ac8d476c2509160b8840bbe63e2bfe9181e7816 100644 (file)
@@ -1361,7 +1361,7 @@ namespace
 
         EX_TRY
         {
-            args.Thread = SetupUnstartedThread(FALSE);
+            args.Thread = SetupUnstartedThread(SUTF_ThreadStoreLockAlreadyTaken);
         }
         EX_CATCH
         {
@@ -1382,7 +1382,7 @@ namespace
             ClrFlsSetThreadType(ThreadType_GC);
             args->Thread->SetGCSpecial(true);
             STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY);
-            args->HasStarted = !!args->Thread->HasStarted(false);
+            args->HasStarted = !!args->Thread->HasStarted();
 
             Thread* thread = args->Thread;
             auto threadStart = args->ThreadStart;
@@ -1407,7 +1407,7 @@ namespace
             return false;
         }
 
-        args.Thread->SetBackground(TRUE, FALSE);
+        args.Thread->SetBackground(TRUE);
         args.Thread->StartThread();
 
         // Wait for the thread to be in its main loop
index ccf2af5410122b312ac80d6590a1db444a67c269..deaafb66d1b634d34c32d44310f00040a5d7e8b3 100644 (file)
@@ -9073,20 +9073,17 @@ HRESULT ProfToEEInterfaceImpl::SetupThreadForReJIT()
 {
     LIMITED_METHOD_CONTRACT;
 
-    HRESULT hr = S_OK;
-    EX_TRY
+    Thread* pThread = GetThreadNULLOk();
+    if (pThread == NULL)
     {
-        if (GetThreadNULLOk() == NULL)
-        {
-            SetupThread();
-        }
-
-        Thread *pThread = GetThreadNULLOk();
-        pThread->SetProfilerCallbackStateFlags(COR_PRF_CALLBACKSTATE_REJIT_WAS_CALLED);
+        HRESULT hr = S_OK;
+        pThread = SetupThreadNoThrow(&hr);
+        if (pThread == NULL)
+            return hr;
     }
-    EX_CATCH_HRESULT(hr);
 
-    return hr;
+    pThread->SetProfilerCallbackStateFlags(COR_PRF_CALLBACKSTATE_REJIT_WAS_CALLED);
+    return S_OK;
 }
 
 HRESULT ProfToEEInterfaceImpl::RequestReJIT(ULONG       cFunctions,   // in
index 62f2b7eacd919b72626f66c4d343b9fe6930c4a1..a7adbd9c0266eef8bb952254f8368d358ca4491e 100644 (file)
@@ -570,73 +570,22 @@ DWORD Thread::StartThread()
     }
     CONTRACTL_END;
 
-    DWORD dwRetVal = (DWORD) -1;
 #ifdef _DEBUG
-    _ASSERTE (m_Creater.IsCurrentThread());
-    m_Creater.Clear();
+    _ASSERTE (m_Creator.IsCurrentThread());
+    m_Creator.Clear();
 #endif
 
     _ASSERTE (GetThreadHandle() != INVALID_HANDLE_VALUE);
-    dwRetVal = ::ResumeThread(GetThreadHandle());
-
-
+    DWORD dwRetVal = ::ResumeThread(GetThreadHandle());
     return dwRetVal;
 }
 
-
 // Class static data:
 LONG    Thread::m_DebugWillSyncCount = -1;
 LONG    Thread::m_DetachCount = 0;
 LONG    Thread::m_ActiveDetachCount = 0;
 
-//-------------------------------------------------------------------------
-// Public function: SetupThreadNoThrow()
-// Creates Thread for current thread if not previously created.
-// Returns NULL for failure (usually due to out-of-memory.)
-//-------------------------------------------------------------------------
-Thread* SetupThreadNoThrow(HRESULT *pHR)
-{
-    CONTRACTL {
-        NOTHROW;
-        if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
-    }
-    CONTRACTL_END;
-
-    HRESULT hr = S_OK;
-
-    Thread *pThread = GetThreadNULLOk();
-    if (pThread != NULL)
-    {
-        return pThread;
-    }
-
-    EX_TRY
-    {
-        pThread = SetupThread();
-    }
-    EX_CATCH
-    {
-        // We failed SetupThread.  GET_EXCEPTION() may depend on Thread object.
-        if (__pException == NULL)
-        {
-            hr = E_OUTOFMEMORY;
-        }
-        else
-        {
-        hr = GET_EXCEPTION()->GetHR();
-    }
-    }
-    EX_END_CATCH(SwallowAllExceptions);
-
-    if (pHR)
-    {
-        *pHR = hr;
-    }
-
-    return pThread;
-}
-
-void DeleteThread(Thread* pThread)
+static void DeleteThread(Thread* pThread)
 {
     CONTRACTL {
         NOTHROW;
@@ -670,7 +619,7 @@ void DeleteThread(Thread* pThread)
     }
 }
 
-void EnsurePreemptive()
+static void EnsurePreemptive()
 {
     WRAPPER_NO_CONTRACT;
     Thread *pThread = GetThreadNULLOk();
@@ -720,7 +669,7 @@ Thread* SetupThread()
     // that call into managed code.  In that case, a call to SetupThread here must
     // find the correct Thread object and install it into TLS.
 
-    if (ThreadStore::s_pThreadStore->m_PendingThreadCount != 0)
+    if (ThreadStore::s_pThreadStore->GetPendingThreadCount() != 0)
     {
         DWORD  ourOSThreadId = ::GetCurrentThreadId();
         {
@@ -774,10 +723,7 @@ Thread* SetupThread()
     Holder<Thread*,DoNothing<Thread*>,DeleteThread> threadHolder(pThread);
 
     SetupTLSForThread();
-
-    if (!pThread->InitThread() ||
-        !pThread->PrepareApartmentAndContext())
-        ThrowOutOfMemory();
+    pThread->InitThread();
 
     // reset any unstarted bits on the thread object
     FastInterlockAnd((ULONG *) &pThread->m_State, ~Thread::TS_Unstarted);
@@ -852,6 +798,9 @@ Thread* SetupThread()
         FastInterlockOr((ULONG *) &pThread->m_State, Thread::TS_TPWorkerThread);
     }
 
+    // Initialize the thread for the platform as the final step.
+    pThread->FinishInitialization();
+
 #ifdef FEATURE_EVENT_TRACE
     ETW::ThreadLog::FireThreadCreated(pThread);
 #endif // FEATURE_EVENT_TRACE
@@ -859,6 +808,53 @@ Thread* SetupThread()
     return pThread;
 }
 
+//-------------------------------------------------------------------------
+// Public function: SetupThreadNoThrow()
+// Creates Thread for current thread if not previously created.
+// Returns NULL for failure (usually due to out-of-memory.)
+//-------------------------------------------------------------------------
+Thread* SetupThreadNoThrow(HRESULT *pHR)
+{
+    CONTRACTL {
+        NOTHROW;
+        if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
+    }
+    CONTRACTL_END;
+
+    HRESULT hr = S_OK;
+
+    Thread *pThread = GetThreadNULLOk();
+    if (pThread != NULL)
+    {
+        return pThread;
+    }
+
+    EX_TRY
+    {
+        pThread = SetupThread();
+    }
+    EX_CATCH
+    {
+        // We failed SetupThread.  GET_EXCEPTION() may depend on Thread object.
+        if (__pException == NULL)
+        {
+            hr = E_OUTOFMEMORY;
+        }
+        else
+        {
+        hr = GET_EXCEPTION()->GetHR();
+    }
+    }
+    EX_END_CATCH(SwallowAllExceptions);
+
+    if (pHR)
+    {
+        *pHR = hr;
+    }
+
+    return pThread;
+}
+
 //-------------------------------------------------------------------------
 // Public function: SetupUnstartedThread()
 // This sets up a Thread object for an exposed System.Thread that
@@ -868,7 +864,7 @@ Thread* SetupThread()
 //
 // When there is, complete the setup with code:Thread::HasStarted()
 //-------------------------------------------------------------------------
-Thread* SetupUnstartedThread(BOOL bRequiresTSL)
+Thread* SetupUnstartedThread(SetupUnstartedThreadFlags flags)
 {
     CONTRACTL {
         THROWS;
@@ -878,10 +874,16 @@ Thread* SetupUnstartedThread(BOOL bRequiresTSL)
 
     Thread* pThread = new Thread();
 
+    if (flags & SUTF_ThreadStoreLockAlreadyTaken)
+    {
+        _ASSERTE(ThreadStore::HoldingThreadStore());
+        pThread->SetThreadStateNC(Thread::TSNC_TSLTakenForStartup);
+    }
+
     FastInterlockOr((ULONG *) &pThread->m_State,
                     (Thread::TS_Unstarted | Thread::TS_WeOwn));
 
-    ThreadStore::AddThread(pThread, bRequiresTSL);
+    ThreadStore::AddThread(pThread);
 
     return pThread;
 }
@@ -1374,7 +1376,7 @@ Thread::Thread()
 #ifdef _DEBUG
     dbg_m_cSuspendedThreads = 0;
     dbg_m_cSuspendedThreadsWithoutOSLock = 0;
-    m_Creater.Clear();
+    m_Creator.Clear();
     m_dwUnbreakableLockCount = 0;
 #endif
 
@@ -1469,7 +1471,6 @@ Thread::Thread()
 #endif  // TRACK_SYNC
 
     m_PreventAsync = 0;
-    m_pDomain = NULL;
 #ifdef FEATURE_COMINTEROP
     m_fDisableComObjectEagerCleanup = false;
 #endif //FEATURE_COMINTEROP
@@ -1562,7 +1563,7 @@ Thread::Thread()
     m_ioThreadPoolCompletionCount = 0;
     m_monitorLockContentionCount = 0;
 
-    InitContext();
+    m_pDomain = SystemDomain::System()->DefaultDomain();
 
     // Do not expose thread until it is fully constructed
     g_pThinLockThreadIdDispenser->NewId(this, this->m_ThreadId);
@@ -1612,7 +1613,7 @@ Thread::Thread()
 //--------------------------------------------------------------------
 // Failable initialization occurs here.
 //--------------------------------------------------------------------
-BOOL Thread::InitThread()
+void Thread::InitThread()
 {
     CONTRACTL {
         THROWS;
@@ -1739,9 +1740,6 @@ BOOL Thread::InitThread()
     {
         ThrowOutOfMemory();
     }
-
-    _ASSERTE(ret); // every failure case for ret should throw.
-    return ret;
 }
 
 // Allocate all the handles.  When we are kicking of a new thread, we can call
@@ -1775,12 +1773,11 @@ BOOL Thread::AllocHandles()
     return fOK;
 }
 
-
 //--------------------------------------------------------------------
 // This is the alternate path to SetupThread/InitThread.  If we created
 // an unstarted thread, we have SetupUnstartedThread/HasStarted.
 //--------------------------------------------------------------------
-BOOL Thread::HasStarted(BOOL bRequiresTSL)
+BOOL Thread::HasStarted()
 {
     CONTRACTL {
         NOTHROW;
@@ -1803,11 +1800,9 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL)
     if (GetThreadNULLOk() == this)
         return TRUE;
 
-
     _ASSERTE(GetThreadNULLOk() == 0);
     _ASSERTE(HasValidThreadHandle());
 
-    BOOL    fKeepTLS = FALSE;
     BOOL    fCanCleanupCOMState = FALSE;
     BOOL    res = TRUE;
 
@@ -1822,25 +1817,17 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL)
     // which will be thrown in Thread.Start as an internal exception
     EX_TRY
     {
-        //
-        // Initialization must happen in the following order - hosts like SQL Server depend on this.
-        //
-
         SetupTLSForThread();
 
-        fCanCleanupCOMState = TRUE;
-        res = PrepareApartmentAndContext();
-        if (!res)
-        {
-            ThrowOutOfMemory();
-        }
-
         InitThread();
 
         SetThread(this);
         SetAppDomain(m_pDomain);
 
-        ThreadStore::TransferStartedThread(this, bRequiresTSL);
+        fCanCleanupCOMState = TRUE;
+        FinishInitialization();
+
+        ThreadStore::TransferStartedThread(this);
 
 #ifdef FEATURE_EVENT_TRACE
         ETW::ThreadLog::FireThreadCreated(this);
@@ -1857,90 +1844,88 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL)
     }
     EX_END_CATCH(SwallowAllExceptions);
 
-FAILURE:
     if (res == FALSE)
-    {
-        if (m_fPreemptiveGCDisabled)
-        {
-            m_fPreemptiveGCDisabled = FALSE;
-        }
-        _ASSERTE (HasThreadState(TS_Unstarted));
-
-        SetThreadState(TS_FailStarted);
+        goto FAILURE;
 
-        if (GetThreadNULLOk() != NULL && IsAbortRequested())
-            UnmarkThreadForAbort();
+    FastInterlockOr((ULONG *) &m_State, TS_FullyInitialized);
 
-        if (!fKeepTLS)
-        {
-#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
-            //
-            // Undo our call to PrepareApartmentAndContext above, so we don't leak a CoInitialize
-            // If we're keeping TLS, then the host's call to ExitTask will clean this up instead.
-            //
-            if (fCanCleanupCOMState)
-            {
-                // The thread pointer in TLS may not be set yet, if we had a failure before we set it.
-                // So we'll set it up here (we'll unset it a few lines down).
-                SetThread(this);
-                CleanupCOMState();
-            }
-#endif
-            FastInterlockDecrement(&ThreadStore::s_pThreadStore->m_PendingThreadCount);
-            // One of the components of OtherThreadsComplete() has changed, so check whether
-            // we should now exit the EE.
-            ThreadStore::CheckForEEShutdown();
-            DecExternalCount(/*holdingLock*/ !bRequiresTSL);
-            SetThread(NULL);
-            SetAppDomain(NULL);
-        }
+#ifdef DEBUGGING_SUPPORTED
+    //
+    // If we're debugging, let the debugger know that this
+    // thread is up and running now.
+    //
+    if (CORDebuggerAttached())
+    {
+        g_pDebugInterface->ThreadCreated(this);
     }
     else
     {
-        FastInterlockOr((ULONG *) &m_State, TS_FullyInitialized);
-
-#ifdef DEBUGGING_SUPPORTED
-        //
-        // If we're debugging, let the debugger know that this
-        // thread is up and running now.
-        //
-        if (CORDebuggerAttached())
-        {
-            g_pDebugInterface->ThreadCreated(this);
-        }
-        else
-        {
-            LOG((LF_CORDB, LL_INFO10000, "ThreadCreated() not called due to CORDebuggerAttached() being FALSE for thread 0x%x\n", GetThreadId()));
-        }
+        LOG((LF_CORDB, LL_INFO10000, "ThreadCreated() not called due to CORDebuggerAttached() being FALSE for thread 0x%x\n", GetThreadId()));
+    }
 
 #endif // DEBUGGING_SUPPORTED
 
 #ifdef PROFILING_SUPPORTED
-        // If a profiler is running, let them know about the new thread.
-        //
-        // The call to IsGCSpecial is crucial to avoid a deadlock.  See code:Thread::m_fGCSpecial for more
-        // information
-        if (!IsGCSpecial())
-        {
-            BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
-            BOOL gcOnTransition = GC_ON_TRANSITIONS(FALSE);     // disable GCStress 2 to avoid the profiler receiving a RuntimeThreadSuspended notification even before the ThreadCreated notification
+    // If a profiler is running, let them know about the new thread.
+    //
+    // The call to IsGCSpecial is crucial to avoid a deadlock.  See code:Thread::m_fGCSpecial for more
+    // information
+    if (!IsGCSpecial())
+    {
+        BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
+        BOOL gcOnTransition = GC_ON_TRANSITIONS(FALSE);     // disable GCStress 2 to avoid the profiler receiving a RuntimeThreadSuspended notification even before the ThreadCreated notification
 
-            {
-                GCX_PREEMP();
-                g_profControlBlock.pProfInterface->ThreadCreated((ThreadID) this);
-            }
+        {
+            GCX_PREEMP();
+            g_profControlBlock.pProfInterface->ThreadCreated((ThreadID) this);
+        }
 
-            GC_ON_TRANSITIONS(gcOnTransition);
+        GC_ON_TRANSITIONS(gcOnTransition);
 
-            DWORD osThreadId = ::GetCurrentThreadId();
-            g_profControlBlock.pProfInterface->ThreadAssignedToOSThread(
-                (ThreadID) this, osThreadId);
-            END_PIN_PROFILER();
-        }
+        DWORD osThreadId = ::GetCurrentThreadId();
+        g_profControlBlock.pProfInterface->ThreadAssignedToOSThread(
+            (ThreadID) this, osThreadId);
+        END_PIN_PROFILER();
+    }
 #endif // PROFILING_SUPPORTED
+
+    // Reset the ThreadStoreLock state flag since the thread
+    // has now been started.
+    ResetThreadStateNC(Thread::TSNC_TSLTakenForStartup);
+    return TRUE;
+
+FAILURE:
+    if (m_fPreemptiveGCDisabled)
+    {
+        m_fPreemptiveGCDisabled = FALSE;
     }
+    _ASSERTE (HasThreadState(TS_Unstarted));
 
-    return res;
+    SetThreadState(TS_FailStarted);
+
+    if (GetThreadNULLOk() != NULL && IsAbortRequested())
+        UnmarkThreadForAbort();
+
+#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
+    //
+    // Undo the platform context initialization, so we don't leak a CoInitialize.
+    //
+    if (fCanCleanupCOMState)
+    {
+        // The thread pointer in TLS may not be set yet, if we had a failure before we set it.
+        // So we'll set it up here (we'll unset it a few lines down).
+        SetThread(this);
+        CleanupCOMState();
+    }
+#endif
+    FastInterlockDecrement(&ThreadStore::s_pThreadStore->m_PendingThreadCount);
+    // One of the components of OtherThreadsComplete() has changed, so check whether
+    // we should now exit the EE.
+    ThreadStore::CheckForEEShutdown();
+    DecExternalCount(/*holdingLock*/ HasThreadStateNC(Thread::TSNC_TSLTakenForStartup));
+    SetThread(NULL);
+    SetAppDomain(NULL);
+    return FALSE;
 }
 
 BOOL Thread::AllocateIOCompletionContext()
@@ -2086,6 +2071,48 @@ BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, voi
     return bRet;
 }
 
+void Thread::InitializationForManagedThreadInNative(_In_ Thread* pThread)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        MODE_ANY;
+        GC_TRIGGERS;
+        PRECONDITION(pThread != NULL);
+    }
+    CONTRACTL_END;
+
+#ifdef FEATURE_OBJCMARSHAL
+    {
+        GCX_COOP_THREAD_EXISTS(pThread);
+        PREPARE_NONVIRTUAL_CALLSITE(METHOD__AUTORELEASEPOOL__CREATEAUTORELEASEPOOL);
+        DECLARE_ARGHOLDER_ARRAY(args, 0);
+        CALL_MANAGED_METHOD_NORET(args);
+    }
+#endif // FEATURE_OBJCMARSHAL
+}
+
+void Thread::CleanUpForManagedThreadInNative(_In_ Thread* pThread)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        MODE_ANY;
+        GC_TRIGGERS;
+        PRECONDITION(pThread != NULL);
+    }
+    CONTRACTL_END;
+
+#ifdef FEATURE_OBJCMARSHAL
+    {
+        GCX_COOP_THREAD_EXISTS(pThread);
+        PREPARE_NONVIRTUAL_CALLSITE(METHOD__AUTORELEASEPOOL__DRAINAUTORELEASEPOOL);
+        DECLARE_ARGHOLDER_ARRAY(args, 0);
+        CALL_MANAGED_METHOD_NORET(args);
+    }
+#endif // FEATURE_OBJCMARSHAL
+}
+
 HANDLE Thread::CreateUtilityThread(Thread::StackSizeBucket stackSizeBucket, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName, DWORD flags, DWORD* pThreadId)
 {
     LIMITED_METHOD_CONTRACT;
@@ -2127,7 +2154,6 @@ HANDLE Thread::CreateUtilityThread(Thread::StackSizeBucket stackSizeBucket, LPTH
     return hThread;
 }
 
-
 // Represent the value of DEFAULT_STACK_SIZE as passed in the property bag to the host during construction
 static unsigned long s_defaultStackSizeProperty = 0;
 
@@ -2303,7 +2329,7 @@ BOOL Thread::CreateNewOSThread(SIZE_T sizeToCommitOrReserve, LPTHREAD_START_ROUT
     FastInterlockIncrement(&ThreadStore::s_pThreadStore->m_PendingThreadCount);
 
 #ifdef _DEBUG
-    m_Creater.SetToCurrentThread();
+    m_Creator.SetToCurrentThread();
 #endif
 
     return TRUE;
@@ -2405,10 +2431,8 @@ int Thread::DecExternalCount(BOOL holdingLock)
 
         ToggleGC = pCurThread->PreemptiveGCDisabled();
         if (ToggleGC)
-        {
             pCurThread->EnablePreemptiveGC();
     }
-    }
 
     GCX_ASSERT_PREEMP();
 
@@ -4564,7 +4588,7 @@ void Thread::SafeUpdateLastThrownObject(void)
 
 // Background threads must be counted, because the EE should shut down when the
 // last non-background thread terminates.  But we only count running ones.
-void Thread::SetBackground(BOOL isBack, BOOL bRequiresTSL)
+void Thread::SetBackground(BOOL isBack)
 {
     CONTRACTL {
         NOTHROW;
@@ -4576,12 +4600,11 @@ void Thread::SetBackground(BOOL isBack, BOOL bRequiresTSL)
     if (isBack == !!IsBackground())
         return;
 
+    BOOL lockHeld = HasThreadStateNC(Thread::TSNC_TSLTakenForStartup);
+    _ASSERTE(!lockHeld || (lockHeld && ThreadStore::HoldingThreadStore()));
+
     LOG((LF_SYNC, INFO3, "SetBackground obtain lock\n"));
-    ThreadStoreLockHolder TSLockHolder(FALSE);
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Acquire();
-    }
+    ThreadStoreLockHolder TSLockHolder(!lockHeld);
 
     if (IsDead())
     {
@@ -4625,11 +4648,6 @@ void Thread::SetBackground(BOOL isBack, BOOL bRequiresTSL)
                      ThreadStore::s_pThreadStore->m_ThreadCount);
         }
     }
-
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Release();
-    }
 }
 
 #ifdef FEATURE_COMINTEROP
@@ -4691,9 +4709,7 @@ public:
 };
 #endif // FEATURE_COMINTEROP
 
-// When the thread starts running, make sure it is running in the correct apartment
-// and context.
-BOOL Thread::PrepareApartmentAndContext()
+void Thread::FinishInitialization()
 {
     CONTRACTL {
         THROWS;
@@ -4748,11 +4764,8 @@ BOOL Thread::PrepareApartmentAndContext()
         m_fInitializeSpyRegistered = true;
     }
 #endif // FEATURE_COMINTEROP
-
-    return TRUE;
 }
 
-
 #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
 
 // TS_InSTA (0x00004000) -> AS_InSTA (0)
@@ -4831,7 +4844,6 @@ Thread::ApartmentState Thread::GetApartmentRare(Thread::ApartmentState as)
     return as;
 }
 
-
 // Retrieve the explicit apartment state of the current thread. There are three possible
 // states: thread hosts an STA, thread is part of the MTA or thread state is
 // undecided. The last state may indicate that the apartment has not been set at
@@ -4860,7 +4872,6 @@ Thread::ApartmentState Thread::GetExplicitApartment()
     return as;
 }
 
-
 Thread::ApartmentState Thread::GetFinalApartment()
 {
     CONTRACTL
@@ -5135,7 +5146,6 @@ ThreadStore::ThreadStore()
              m_DeadThreadCount(0),
              m_DeadThreadCountForGCTrigger(0),
              m_TriggerGCForDeadThreads(false),
-             m_GuidCreated(FALSE),
              m_HoldingThread(0)
 {
     CONTRACTL {
@@ -5230,7 +5240,7 @@ void ThreadStore::UnlockThreadStore()
 }
 
 // AddThread adds 'newThread' to m_ThreadList
-void ThreadStore::AddThread(Thread *newThread, BOOL bRequiresTSL)
+void ThreadStore::AddThread(Thread *newThread)
 {
     CONTRACTL {
         NOTHROW;
@@ -5240,11 +5250,10 @@ void ThreadStore::AddThread(Thread *newThread, BOOL bRequiresTSL)
 
     LOG((LF_SYNC, INFO3, "AddThread obtain lock\n"));
 
-    ThreadStoreLockHolder TSLockHolder(FALSE);
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Acquire();
-    }
+    BOOL lockHeld = newThread->HasThreadStateNC(Thread::TSNC_TSLTakenForStartup);
+    _ASSERTE(!lockHeld || (lockHeld && ThreadStore::HoldingThreadStore()));
+
+    ThreadStoreLockHolder TSLockHolder(!lockHeld);
 
     s_pThreadStore->m_ThreadList.InsertTail(newThread);
 
@@ -5259,11 +5268,6 @@ void ThreadStore::AddThread(Thread *newThread, BOOL bRequiresTSL)
 
     _ASSERTE(!newThread->IsBackground());
     _ASSERTE(!newThread->IsDead());
-
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Release();
-    }
 }
 
 // this function is just desgined to avoid deadlocks during abnormal process termination, and should not be used for any other purpose
@@ -5368,22 +5372,31 @@ BOOL ThreadStore::RemoveThread(Thread *target)
 // When a thread is created as unstarted.  Later it may get started, in which case
 // someone calls Thread::HasStarted() on that physical thread.  This completes
 // the Setup and calls here.
-void ThreadStore::TransferStartedThread(Thread *thread, BOOL bRequiresTSL)
+void ThreadStore::TransferStartedThread(Thread *thread)
 {
     CONTRACTL {
-        THROWS;
+        NOTHROW;
         GC_TRIGGERS;
+        PRECONDITION(thread != NULL);
     }
     CONTRACTL_END;
 
     _ASSERTE(GetThreadNULLOk() == thread);
 
-    LOG((LF_SYNC, INFO3, "TransferUnstartedThread obtain lock\n"));
-    ThreadStoreLockHolder TSLockHolder(FALSE);
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Acquire();
-    }
+    BOOL lockHeld = thread->HasThreadStateNC(Thread::TSNC_TSLTakenForStartup);
+
+    // This ASSERT is correct for one of the following reasons.
+    //  - The lock is not currently held which means it will be taken below.
+    //  - The thread was created in an Unstarted state and the lock is
+    //    being held by the creator thread. The only thing we know for sure
+    //    is that the lock is held and not by this thread.
+    _ASSERTE(!lockHeld
+        || (lockHeld
+            && s_pThreadStore->m_HoldingThread != NULL
+            && !ThreadStore::HoldingThreadStore()));
+
+    LOG((LF_SYNC, INFO3, "TransferStartedThread obtain lock\n"));
+    ThreadStoreLockHolder TSLockHolder(!lockHeld);
 
     _ASSERTE(s_pThreadStore->DbgFindThread(thread));
     _ASSERTE(thread->HasValidThreadHandle());
@@ -5391,14 +5404,8 @@ void ThreadStore::TransferStartedThread(Thread *thread, BOOL bRequiresTSL)
     _ASSERTE(thread->IsUnstarted());
     _ASSERTE(!thread->IsDead());
 
-    if (thread->m_State & Thread::TS_AbortRequested)
-    {
-        PAL_CPP_THROW(EEException *, new EEException(COR_E_THREADABORTED));
-    }
-
     // Of course, m_ThreadCount is already correct since it includes started and
     // unstarted threads.
-
     s_pThreadStore->m_UnstartedThreadCount--;
 
     // We only count background threads that have been started
@@ -5413,12 +5420,6 @@ void ThreadStore::TransferStartedThread(Thread *thread, BOOL bRequiresTSL)
     FastInterlockAnd((ULONG *) &thread->m_State, ~Thread::TS_Unstarted);
     FastInterlockOr((ULONG *) &thread->m_State, Thread::TS_LegalToJoin);
 
-    // release ThreadStore Crst to avoid Crst Violation when calling HandleThreadAbort later
-    if (bRequiresTSL)
-    {
-        TSLockHolder.Release();
-    }
-
     // One of the components of OtherThreadsComplete() has changed, so check whether
     // we should now exit the EE.
     CheckForEEShutdown();
@@ -5752,36 +5753,6 @@ void ThreadStore::WaitForOtherThreads()
     }
 }
 
-
-// Every EE process can lazily create a GUID that uniquely identifies it (for
-// purposes of remoting).
-const GUID &ThreadStore::GetUniqueEEId()
-{
-    CONTRACTL {
-        NOTHROW;
-        GC_TRIGGERS;
-    }
-    CONTRACTL_END;
-
-    if (!m_GuidCreated)
-    {
-        ThreadStoreLockHolder TSLockHolder(TRUE);
-        if (!m_GuidCreated)
-        {
-            HRESULT hr = ::CoCreateGuid(&m_EEGuid);
-
-            _ASSERTE(SUCCEEDED(hr));
-            if (SUCCEEDED(hr))
-                m_GuidCreated = TRUE;
-        }
-
-        if (!m_GuidCreated)
-            return IID_NULL;
-    }
-    return m_EEGuid;
-}
-
-
 #ifdef _DEBUG
 BOOL ThreadStore::DbgFindThread(Thread *target)
 {
@@ -7150,20 +7121,6 @@ T_CONTEXT *Thread::GetFilterContext(void)
 
 #ifndef DACCESS_COMPILE
 
-void Thread::InitContext()
-{
-    CONTRACTL {
-        THROWS;
-        if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
-    }
-    CONTRACTL_END;
-
-    // this should only be called when initializing a thread
-    _ASSERTE(m_pDomain == NULL);
-    GCX_COOP_NO_THREAD_BROKEN();
-    m_pDomain = SystemDomain::System()->DefaultDomain();
-}
-
 void Thread::ClearContext()
 {
     CONTRACTL {
@@ -7187,7 +7144,7 @@ BOOL Thread::HaveExtraWorkForFinalizer()
 {
     LIMITED_METHOD_CONTRACT;
 
-    return m_ThreadTasks
+    return RequireSyncBlockCleanup()
         || ThreadpoolMgr::HaveTimerInfosToFlush()
         || Thread::CleanupNeededForFinalizedThread()
         || (m_DetachCount > 0)
index f4c66fe0c53f1099e7b908e1a696ebbc68830d3e..50bf121eb0418a54266ae134b3760ca7fb12eb64 100644 (file)
@@ -544,8 +544,9 @@ enum ThreadpoolThreadType
 //***************************************************************************
 // Public functions
 //
-//      Thread* GetThread()             - returns current Thread
-//      Thread* SetupThread()           - creates new Thread.
+//      Thread* GetThread()             - returns current Thread.
+//      Thread* SetupThread()           - creates a new Thread.
+//      Thread* SetupThreadNoThrow()    - creates a new Thread without throwing.
 //      Thread* SetupUnstartedThread()  - creates new unstarted Thread which
 //                                        (obviously) isn't in a TLS.
 //      void    DestroyThread()         - the underlying logical thread is going
@@ -580,8 +581,18 @@ enum ThreadpoolThreadType
 //---------------------------------------------------------------------------
 Thread* SetupThread();
 Thread* SetupThreadNoThrow(HRESULT *phresult = NULL);
-// WARNING : only GC calls this with bRequiresTSL set to FALSE.
-Thread* SetupUnstartedThread(BOOL bRequiresTSL=TRUE);
+
+enum SetupUnstartedThreadFlags
+{
+    SUTF_None = 0,
+
+    // The ThreadStoreLock is being held during Thread startup.
+    SUTF_ThreadStoreLockAlreadyTaken = 1,
+
+    // The default flags for the majority of threads.
+    SUTF_Default = SUTF_None,
+};
+Thread* SetupUnstartedThread(SetupUnstartedThreadFlags flags = SUTF_Default);
 void    DestroyThread(Thread *th);
 
 DWORD GetRuntimeId();
@@ -1124,7 +1135,7 @@ public:
 
         // unused                 = 0x00400000,
 
-        // unused                 = 0x00800000,    
+        // unused                 = 0x00800000,
         TS_TPWorkerThread         = 0x01000000,    // is this a threadpool worker thread?
 
         TS_Interruptible          = 0x02000000,    // sitting in a Sleep(), Wait(), Join()
@@ -1213,7 +1224,8 @@ public:
         TSNC_WinRTInitialized           = 0x08000000, // the thread has initialized WinRT
 #endif // FEATURE_COMINTEROP
 
-        // TSNC_Unused                  = 0x10000000,
+        TSNC_TSLTakenForStartup         = 0x10000000, // The ThreadStoreLock (TSL) is held by another mechansim during
+                                                      // thread startup so can be skipped.
 
         TSNC_CallingManagedCodeDisabled = 0x20000000, // Use by multicore JIT feature to asert on calling managed code/loading module in background thread
                                                       // Exception, system module is allowed, security demand is allowed
@@ -1370,6 +1382,8 @@ public:
 
 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
 
+    void FinishInitialization();
+
 #ifdef FEATURE_COMINTEROP
     bool IsDisableComObjectEagerCleanup()
     {
@@ -1622,7 +1636,7 @@ private:
     DWORD dbg_m_cSuspendedThreads;
     // Count of suspended threads that we know are not in native code (and therefore cannot hold OS lock which prevents us calling out to host)
     DWORD dbg_m_cSuspendedThreadsWithoutOSLock;
-    EEThreadId m_Creater;
+    EEThreadId m_Creator;
 #endif
 
     // A thread may forbid its own suspension. For example when holding certain locks.
@@ -1753,7 +1767,6 @@ public:
 
 
 public:
-
     //--------------------------------------------------------------
     // Constructor.
     //--------------------------------------------------------------
@@ -1764,16 +1777,15 @@ public:
     //--------------------------------------------------------------
     // Failable initialization occurs here.
     //--------------------------------------------------------------
-    BOOL InitThread();
+    void InitThread();
     BOOL AllocHandles();
 
     //--------------------------------------------------------------
     // If the thread was setup through SetupUnstartedThread, rather
     // than SetupThread, complete the setup here when the thread is
     // actually running.
-    // WARNING : only GC calls this with bRequiresTSL set to FALSE.
     //--------------------------------------------------------------
-    BOOL HasStarted(BOOL bRequiresTSL=TRUE);
+    BOOL HasStarted();
 
     // We don't want ::CreateThread() calls scattered throughout the source.
     // Create all new threads here.  The thread is created as suspended, so
@@ -1781,6 +1793,12 @@ public:
     // thread, or throw.
     BOOL CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName=NULL);
 
+    // Functions used to perform initialization and cleanup on a managed thread
+    // that would normally occur if the thread was stated when the runtime was
+    // fully initialized and ready to run.
+    // Examples where this applies are the Main and Finalizer threads. 
+    static void InitializationForManagedThreadInNative(_In_ Thread* pThread);
+    static void CleanUpForManagedThreadInNative(_In_ Thread* pThread);
 
     enum StackSizeBucket
     {
@@ -2225,15 +2243,10 @@ public:
         return PTR_ThreadExceptionState(PTR_HOST_MEMBER_TADDR(Thread, this, m_ExceptionState));
     }
 
-public:
-
+private:
     // ClearContext are to be called only during shutdown
     void ClearContext();
 
-private:
-    // don't ever call these except when creating thread!!!!!
-    void InitContext();
-
 public:
     PTR_AppDomain GetDomain(INDEBUG(BOOL fMidContextTransitionOK = FALSE))
     {
@@ -2845,12 +2858,7 @@ public:
     // Indicate whether this thread should run in the background.  Background threads
     // don't interfere with the EE shutting down.  Whereas a running non-background
     // thread prevents us from shutting down (except through System.Exit(), of course)
-    // WARNING : only GC calls this with bRequiresTSL set to FALSE.
-    void           SetBackground(BOOL isBack, BOOL bRequiresTSL=TRUE);
-
-    // When the thread starts running, make sure it is running in the correct apartment
-    // and context.
-    BOOL           PrepareApartmentAndContext();
+    void           SetBackground(BOOL isBack);
 
 #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
     // Retrieve the apartment state of the current thread. There are three possible
@@ -4669,7 +4677,6 @@ class ThreadStore
 {
     friend class Thread;
     friend class ThreadSuspend;
-    friend Thread* SetupThread();
     friend class AppDomain;
 #ifdef DACCESS_COMPILE
     friend class ClrDataAccess;
@@ -4685,8 +4692,7 @@ public:
     static void UnlockThreadStore();
 
     // Add a Thread to the ThreadStore
-    // WARNING : only GC calls this with bRequiresTSL set to FALSE.
-    static void AddThread(Thread *newThread, BOOL bRequiresTSL=TRUE);
+    static void AddThread(Thread *newThread);
 
     // RemoveThread finds the thread in the ThreadStore and discards it.
     static BOOL RemoveThread(Thread *target);
@@ -4694,8 +4700,7 @@ public:
     static BOOL CanAcquireLock();
 
     // Transfer a thread from the unstarted to the started list.
-    // WARNING : only GC calls this with bRequiresTSL set to FALSE.
-    static void TransferStartedThread(Thread *target, BOOL bRequiresTSL=TRUE);
+    static void TransferStartedThread(Thread *target);
 
     // Before using the thread list, be sure to take the critical section.  Otherwise
     // it can change underneath you, perhaps leading to an exception after Remove.
@@ -4703,10 +4708,6 @@ public:
     static Thread *GetAllThreadList(Thread *Prev, ULONG mask, ULONG bits);
     static Thread *GetThreadList(Thread *Prev);
 
-    // Every EE process can lazily create a GUID that uniquely identifies it (for
-    // purposes of remoting).
-    const GUID    &GetUniqueEEId();
-
     // We shut down the EE when the last non-background thread terminates.  This event
     // is used to signal the main thread when this condition occurs.
     void            WaitForOtherThreads();
@@ -4760,7 +4761,7 @@ private:
     // m_PendingThreadCount is used to solve a race condition.  The main thread could
     // start another thread running and then exit.  The main thread might then start
     // tearing down the EE before the new thread moves itself out of m_UnstartedThread-
-    // Count in TransferUnstartedThread.  This count is atomically bumped in
+    // Count in TransferStartedThread.  This count is atomically bumped in
     // CreateNewThread, and atomically reduced within a locked thread store.
     //
     // m_DeadThreadCount is the subset of m_ThreadCount which have died.  The Win32
@@ -4790,16 +4791,19 @@ private:
     LONG        m_UnstartedThreadCount;
     LONG        m_BackgroundThreadCount;
     LONG        m_PendingThreadCount;
+public:
+    LONG        GetPendingThreadCount ()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_PendingThreadCount;
+    }
+private:
 
     LONG        m_DeadThreadCount;
     LONG        m_DeadThreadCountForGCTrigger;
     bool        m_TriggerGCForDeadThreads;
 
 private:
-    // Space for the lazily-created GUID.
-    GUID        m_EEGuid;
-    BOOL        m_GuidCreated;
-
     // Even in the release product, we need to know what thread holds the lock on
     // the ThreadStore.  This is so we never deadlock when the GC thread halts a
     // thread that holds this lock.
index 2302e2e4619dcbbccf3d1f0c51e8d25227139e6a..68e4caf6e69e9e3fb6e04a3332c1607032b54def 100644 (file)
@@ -438,7 +438,7 @@ DWORD Thread::ResumeThread()
     DWORD res = ::ResumeThread(m_ThreadHandleForResume);
     _ASSERTE (res != 0 && "Thread is not previously suspended");
 #ifdef _DEBUG_IMPL
-    _ASSERTE (!m_Creater.IsCurrentThread());
+    _ASSERTE (!m_Creator.IsCurrentThread());
     if ((res != (DWORD)-1) && (res != 0))
     {
         Thread * pCurThread = GetThreadNULLOk();
index 61dcb5b5a7bf21992b1b6df5ccfff3b7449bfb23..3b747687397c0b8518a4fb4902d9b8c65dca9f46 100644 (file)
@@ -1,7 +1,7 @@
 <linker>
   <assembly fullname="System.Private.CoreLib">
-    <type fullname="System.Threading.ThreadPool" feature="System.Threading.ThreadPool.EnableDispatchAutoreleasePool" featurevalue="false">
-      <method signature="System.Boolean get_EnableDispatchAutoreleasePool()" body="stub" value="false" />
+    <type fullname="System.Threading.AutoreleasePool" feature="System.Threading.Thread.EnableAutoreleasePool" featurevalue="false">
+      <method signature="System.Boolean get_EnableAutoreleasePool()" body="stub" value="false" />
     </type>
   </assembly>
 </linker>
index 55e6938440c4af1b91ff2d716c84e0912ca04699..80dac9fdd762463fc3bc50f8b5308339b9400ebe 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Unix.cs" Condition="'$(IsOSXLike)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.SunOS.cs" Condition="'$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true'" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.FullGlobalizationData.Unix.cs" Condition="'$(UseMinimalGlobalizationData)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveInfoInternal.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\PersistedFiles.Unix.cs" />
       <Link>Interop\Windows\Kernel32\Interop.Threading.cs</Link>
     </Compile>
   </ItemGroup>
+  <ItemGroup Condition="'$(IsOSXLike)' == 'true'">
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\AutoreleasePool.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.OSX.cs" />
+  </ItemGroup>
 </Project>
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/AutoreleasePool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/AutoreleasePool.cs
new file mode 100644 (file)
index 0000000..aea7d44
--- /dev/null
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System.Threading
+{
+    internal static class AutoreleasePool
+    {
+        private static bool CheckEnableAutoreleasePool()
+        {
+            const string feature = "System.Threading.Thread.EnableAutoreleasePool";
+#if !CORECLR
+            return AppContextConfigHelper.GetBooleanConfig(feature, false);
+#else
+            bool isEnabled = CLRConfig.GetBoolValue(feature, out bool isSet);
+            if (!isSet)
+                return false;
+
+            return isEnabled;
+#endif
+        }
+
+        public static bool EnableAutoreleasePool { get; } = CheckEnableAutoreleasePool();
+
+        [ThreadStatic]
+        private static IntPtr s_AutoreleasePoolInstance;
+
+        internal static void CreateAutoreleasePool()
+        {
+            if (EnableAutoreleasePool)
+            {
+                Debug.Assert(s_AutoreleasePoolInstance == IntPtr.Zero);
+                s_AutoreleasePoolInstance = Interop.Sys.CreateAutoreleasePool();
+            }
+        }
+
+        internal static void DrainAutoreleasePool()
+        {
+            if (EnableAutoreleasePool
+                && s_AutoreleasePoolInstance != IntPtr.Zero)
+            {
+                Interop.Sys.DrainAutoreleasePool(s_AutoreleasePoolInstance);
+                s_AutoreleasePoolInstance = IntPtr.Zero;
+            }
+        }
+    }
+}
index 5baaa386b34741427bd35dc66c57e2e24f172c40..aa6e8c6f8d582a6289396b23d93e3a51cb5b873e 100644 (file)
@@ -63,6 +63,11 @@ namespace System.Threading
                 Delegate start = _start;
                 _start = null!;
 
+#if FEATURE_OBJCMARSHAL
+                if (AutoreleasePool.EnableAutoreleasePool)
+                    AutoreleasePool.CreateAutoreleasePool();
+#endif
+
                 if (start is ThreadStart threadStart)
                 {
                     threadStart();
@@ -76,6 +81,14 @@ namespace System.Threading
 
                     parameterizedThreadStart(startArg);
                 }
+
+#if FEATURE_OBJCMARSHAL
+                // There is no need to wrap this "clean up" code in a finally block since
+                // if an exception is thrown above, the process is going to terminate.
+                // Optimize for the most common case - no exceptions escape a thread.
+                if (AutoreleasePool.EnableAutoreleasePool)
+                    AutoreleasePool.DrainAutoreleasePool();
+#endif
             }
 
             private void InitializeCulture()
index 6991b82daf84a9049908182e554258a061b94c46..8e378ce9bf434e9576ae493aa7c2b52ef455fb99 100644 (file)
@@ -6,12 +6,6 @@ using System.Runtime.Versioning;
 
 namespace System.Threading
 {
-    public static partial class ThreadPool
-    {
-        internal static bool EnableDispatchAutoreleasePool { get; } =
-            AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableDispatchAutoreleasePool", false);
-    }
-
     internal sealed partial class ThreadPoolWorkQueue
     {
         [MethodImpl(MethodImplOptions.NoInlining)]
index bb55916ef78f11e67ffa0c254c9f50625792b7bc..f08e4ba4d7fe0a367f03ce29b6d50ddae82b2fa9 100644 (file)
@@ -691,8 +691,8 @@ namespace System.Threading
                     //
                     // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
                     //
-#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
-                    if (ThreadPool.EnableDispatchAutoreleasePool)
+#if FEATURE_OBJCMARSHAL
+                    if (AutoreleasePool.EnableAutoreleasePool)
                     {
                         DispatchItemWithAutoreleasePool(workItem, currentThread);
                     }
index b6b4f889a81fb4ca3931e8ae49166b7b4a7dcfd4..66e5328b631567d8f5995be478b7e0a54d3e1971 100644 (file)
     <FeaturePortableTimer Condition="'$(TargetsBrowser)' != 'true'">true</FeaturePortableTimer>
     <FeaturePortableThreadPool Condition="'$(TargetsBrowser)' != 'true'">true</FeaturePortableThreadPool>
     <FeaturePerfTracing Condition="'$(TargetsBrowser)' != 'true'">true</FeaturePerfTracing>
+    <FeatureObjCMarshal Condition="'$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</FeatureObjCMarshal>
   </PropertyGroup>
 
   <PropertyGroup>
     <DefineConstants Condition="'$(FeatureManagedEtw)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW</DefineConstants>
     <DefineConstants Condition="'$(FeatureManagedEtwChannels)' == 'true'">$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS</DefineConstants>
     <DefineConstants Condition="'$(FeaturePerfTracing)' == 'true'">$(DefineConstants);FEATURE_PERFTRACING</DefineConstants>
+    <DefineConstants Condition="'$(FeatureObjCMarshal)' == 'true'">$(DefineConstants);FEATURE_OBJCMARSHAL</DefineConstants>
   </PropertyGroup>
 
   <!-- Experimental mono metadata update feature -->
 
   <ItemGroup>
     <ILLinkDescriptorsXmls Include="$(ILLinkDirectory)ILLink.Descriptors.xml" />
+    <ILLinkDescriptorsXmls Include="$(ILLinkDirectory)ILLink.Descriptors.OSX.xml"
+                           Condition="'$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'" />
     <ILLinkDescriptorsXmls Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.Shared.xml" />
 
     <ILLinkSubstitutionsXmls Include="$(ILLinkDirectory)ILLink.Substitutions.xml" />
diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.OSX.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.OSX.xml
new file mode 100644 (file)
index 0000000..2b19af0
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<linker>
+       <assembly fullname="System.Private.CoreLib">
+               <type fullname="System.Threading.AutoreleasePool">
+                       <!-- threads.c -->
+                       <method name="CreateAutoreleasePool" />
+                       <method name="DrainAutoreleasePool" />
+               </type>
+       </assembly>
+</linker>
\ No newline at end of file
index 72d8dbbf923744035f787dee3ab932cb7dff3bcc..cd190c8ea0ab7b48638cf3d629c724dd6cf372b0 100644 (file)
@@ -962,6 +962,7 @@ typedef struct {
        MonoClass *threadabortexception_class;
        MonoClass *thread_class;
        MonoClass *internal_thread_class;
+       MonoClass *autoreleasepool_class;
        MonoClass *mono_method_message_class;
        MonoClass *field_info_class;
        MonoClass *method_info_class;
index 1a9df287e8fb5aecf6c9f48652449ab04554141f..62975338ea8d9ef037300e742e1f54c508a65743 100644 (file)
@@ -365,6 +365,13 @@ mono_init_internal (const char *filename, const char *exe_filename, const char *
        /* There is only one thread class */
        mono_defaults.internal_thread_class = mono_defaults.thread_class;
 
+#if defined(HOST_DARWIN)
+       mono_defaults.autoreleasepool_class = mono_class_load_from_name (
+                mono_defaults.corlib, "System.Threading", "AutoreleasePool");
+#else
+       mono_defaults.autoreleasepool_class = NULL;
+#endif
+
        mono_defaults.field_info_class = mono_class_load_from_name (
                mono_defaults.corlib, "System.Reflection", "FieldInfo");
 
index 110ebee75c5327a558244bd3c2752187ad2813ea..e6564335b949aa94111336bc2b98aef49b930462 100644 (file)
@@ -856,6 +856,7 @@ static gsize WINAPI
 finalizer_thread (gpointer unused)
 {
        gboolean wait = TRUE;
+       gboolean did_init_from_native = FALSE;
 
        mono_thread_set_name_constant_ignore_error (mono_thread_internal_current (), "Finalizer", MonoSetThreadNameFlag_None);
 
@@ -878,6 +879,14 @@ finalizer_thread (gpointer unused)
 
                mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE);
 
+               /* The Finalizer thread doesn't initialize during creation because base managed
+                       libraries may not be loaded yet. However, the first time the Finalizer is
+                       to run managed finalizer, we can take this opportunity to initialize. */
+               if (!did_init_from_native) {
+                       did_init_from_native = TRUE;
+                       mono_thread_init_from_native ();
+               }
+
                mono_runtime_do_background_work ();
 
                /* Avoid posting the pending done event until there are pending finalizers */
@@ -896,6 +905,11 @@ finalizer_thread (gpointer unused)
                }
        }
 
+       /* If the initialization from native was done, do the clean up */
+       if (did_init_from_native) {
+               mono_thread_cleanup_from_native ();
+       }
+
        mono_finalizer_lock ();
        finalizer_thread_exited = TRUE;
        mono_coop_cond_signal (&exited_cond);
index 96d2ed237884da4757a77880d287f606824aca51..78502016cc2fa5d38f303fa0ef53ea58d68c342c 100644 (file)
@@ -533,8 +533,9 @@ typedef struct {
 TYPED_HANDLE_DECL (MonoStackFrame);
 
 typedef enum {
-       MONO_THREAD_FLAG_DONT_MANAGE = 1, // Don't wait for or abort this thread
-       MONO_THREAD_FLAG_NAME_SET = 2, // Thread name set from managed code
+       MONO_THREAD_FLAG_DONT_MANAGE                    = 1, // Don't wait for or abort this thread
+       MONO_THREAD_FLAG_NAME_SET                               = 2, // Thread name set from managed code
+       MONO_THREAD_FLAG_CLEANUP_FROM_NATIVE    = 4, // Thread initialized in native so clean up in native
 } MonoThreadFlags;
 
 struct _MonoThreadInfo;
index 0b1f39741d0d77ed8badbb37c978a194af2cef34..1e59f65a70e0f8574c6a0e8c110d43c324897154 100644 (file)
@@ -4369,7 +4369,7 @@ prepare_thread_to_exec_main (MonoMethod *method)
                thread->apartment_state = ThreadApartmentState_MTA;
        }
        mono_thread_init_apartment_state ();
-
+       mono_thread_init_from_native ();
 }
 
 static int
index e3869ca5b5fdd5a07156c7c4a8282dc7d365aaf0..394a526c5524d1684746d7dd0972f3967f6bbf6f 100644 (file)
@@ -73,11 +73,11 @@ void
 mono_thread_callbacks_init (void);
 
 typedef enum {
-       MONO_THREAD_CREATE_FLAGS_NONE         = 0x0,
-       MONO_THREAD_CREATE_FLAGS_THREADPOOL   = 0x1,
-       MONO_THREAD_CREATE_FLAGS_DEBUGGER     = 0x2,
-       MONO_THREAD_CREATE_FLAGS_FORCE_CREATE = 0x4,
-       MONO_THREAD_CREATE_FLAGS_SMALL_STACK  = 0x8,
+       MONO_THREAD_CREATE_FLAGS_NONE                   = 0x00,
+       MONO_THREAD_CREATE_FLAGS_THREADPOOL             = 0x01,
+       MONO_THREAD_CREATE_FLAGS_DEBUGGER               = 0x02,
+       MONO_THREAD_CREATE_FLAGS_FORCE_CREATE   = 0x04,
+       MONO_THREAD_CREATE_FLAGS_SMALL_STACK    = 0x08,
 } MonoThreadCreateFlags;
 
 MonoInternalThread*
@@ -216,6 +216,12 @@ void mono_thread_clear_and_set_state (MonoInternalThread *thread, MonoThreadStat
 void mono_thread_init_apartment_state (void);
 void mono_thread_cleanup_apartment_state (void);
 
+/* There are some threads that need initialization that would normally
+       occur in managed code. Some threads occur prior to the runtime being
+       fully initialized so that must be done in native. For example, Main and Finalizer. */
+void mono_thread_init_from_native (void);
+void mono_thread_cleanup_from_native (void);
+
 void mono_threads_set_shutting_down (void);
 
 MONO_API MonoException* mono_thread_get_undeniable_exception (void);
index 7dabfc87a2b8d4737c9d96f930165ec167b5709e..c5f0f4306a7a5ab85d508719a62c404a79f332ea 100644 (file)
@@ -3914,18 +3914,62 @@ mono_thread_init_apartment_state (void)
 #endif
 }
 
-void 
+void
 mono_thread_cleanup_apartment_state (void)
 {
 #ifdef HOST_WIN32
        MonoInternalThread* thread = mono_thread_internal_current ();
-
        if (thread && thread->apartment_state != ThreadApartmentState_Unknown) {
                CoUninitialize ();
        }
 #endif
 }
 
+void
+mono_thread_init_from_native (void)
+{
+#if defined(HOST_DARWIN)
+       MonoInternalThread* thread = mono_thread_internal_current ();
+
+       g_assert (mono_defaults.autoreleasepool_class != NULL);
+       ERROR_DECL (error);
+       MONO_STATIC_POINTER_INIT (MonoMethod, create_autoreleasepool)
+
+               create_autoreleasepool = mono_class_get_method_from_name_checked (mono_defaults.autoreleasepool_class, "CreateAutoreleasePool", 0, 0, error);
+               mono_error_assert_ok (error);
+
+       MONO_STATIC_POINTER_INIT_END (MonoMethod, create_autoreleasepool)
+
+       mono_runtime_invoke_handle_void (create_autoreleasepool, NULL_HANDLE, NULL, error);
+       mono_error_cleanup (error);
+
+       thread->flags |= MONO_THREAD_FLAG_CLEANUP_FROM_NATIVE;
+#endif
+}
+
+void
+mono_thread_cleanup_from_native (void)
+{
+#if defined(HOST_DARWIN)
+       MonoInternalThread* thread = mono_thread_internal_current ();
+       if (!(thread->flags & MONO_THREAD_FLAG_CLEANUP_FROM_NATIVE))
+               return;
+
+       g_assert (mono_defaults.autoreleasepool_class != NULL);
+       ERROR_DECL (error);
+       MONO_STATIC_POINTER_INIT (MonoMethod, drain_autoreleasepool)
+
+               drain_autoreleasepool = mono_class_get_method_from_name_checked (mono_defaults.autoreleasepool_class, "DrainAutoreleasePool", 0, 0, error);
+               mono_error_assert_ok (error);
+
+       MONO_STATIC_POINTER_INIT_END (MonoMethod, drain_autoreleasepool)
+
+       mono_runtime_invoke_handle_void (drain_autoreleasepool, NULL_HANDLE, NULL, error);
+       mono_error_cleanup (error);
+
+#endif
+}
+
 static void
 mono_thread_notify_change_state (MonoThreadState old_state, MonoThreadState new_state)
 {
index 1814a296feee81191d4944d49f5529d5409decb9..c506de46b2bdcfe68e70373756345969984555d1 100644 (file)
@@ -19,7 +19,7 @@ WARNING:   When setting properties based on their current state (for example:
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 
   <!-- This is here because of this bug: https://docs.microsoft.com/en-us/archive/blogs/msbuild/well-known-limitation-dynamic-items-and-properties-not-emitted-until-target-execution-completes -->
-  <Target Name="FetchExternalPropertiesForXpalt">
+  <Target Name="FetchExternalPropertiesForXplat">
     <!--Call GetExecuteShFullPath to get ToRunProject cmd file Path  -->
     <MSBuild Projects="$(CLRTestProjectToRun)" 
              Targets="GetExecuteShFullPath"
@@ -64,12 +64,13 @@ WARNING:   When setting properties based on their current state (for example:
   <Target Name="GenerateBashExecutionScript"
     Inputs="$(MSBuildProjectFullPath)"
     Outputs="$(OutputPath)\$(MSBuildProjectName).sh"
-    DependsOnTargets="FetchExternalPropertiesForXpalt;$(BashScriptSnippetGen);GetIlasmRoundTripBashScript">
+    DependsOnTargets="FetchExternalPropertiesForXplat;$(BashScriptSnippetGen);GetIlasmRoundTripBashScript">
 
     <Message Text="Project depends on $(_CLRTestToRunFileFullPath)." Condition="'$(_CLRTestNeedsProjectToRun)' == 'True'" />
 
     <PropertyGroup>
       <IncompatibleTestBashScriptExitCode>0</IncompatibleTestBashScriptExitCode>
+      <CoreRunArgs>@(RuntimeHostConfigurationOption -> '-p &quot;%(Identity)=%(Value)&quot;', ' ')</CoreRunArgs>
 
       <BashCLRTestEnvironmentCompatibilityCheck Condition="'$(GCStressIncompatible)' == 'true'"><![CDATA[
 $(BashCLRTestEnvironmentCompatibilityCheck)
@@ -262,7 +263,7 @@ fi
           </BashLinkerTestCleanupCmds>
       </PropertyGroup>
       <PropertyGroup>
-      <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"$CORE_ROOT/corerun"</_CLRTestRunFile>
+      <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"$CORE_ROOT/corerun" $(CoreRunArgs)</_CLRTestRunFile>
 
       <BashCLRTestPreCommands Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(TargetArchitecture)' == 'wasm'">
    <![CDATA[
index f1c643f60decb1571bd3a96e728f3a7aef762e0d..868a377bd83e9f8b6be42e2370869fe54aeda6ac 100644 (file)
@@ -68,6 +68,7 @@ WARNING:   When setting properties based on their current state (for example:
     <Message Text="Project depends on $(_CLRTestToRunFileFullPath)." Condition="'$(_CLRTestNeedsProjectToRun)' == 'True'" />
 
     <PropertyGroup>
+      <CoreRunArgs>@(RuntimeHostConfigurationOption -> '-p &quot;%(Identity)=%(Value)&quot;', ' ')</CoreRunArgs>
       <BatchCLRTestEnvironmentCompatibilityCheck Condition="'$(GCStressIncompatible)' == 'true'"><![CDATA[
 $(BatchCLRTestEnvironmentCompatibilityCheck)
 IF NOT "%COMPlus_GCStress%"=="" (
@@ -281,7 +282,7 @@ if defined DoLink (
           </BatchLinkerTestCleanupCmds>
       </PropertyGroup>
       <PropertyGroup>      
-      <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe"</_CLRTestRunFile>
+      <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe" $(CoreRunArgs)</_CLRTestRunFile>
       <BatchCopyCoreShimLocalCmds Condition="'$(CLRTestScriptLocalCoreShim)' == 'true'"><![CDATA[
 REM Local CoreShim requested - see MSBuild property 'CLRTestScriptLocalCoreShim'
 ECHO Copying '%CORE_ROOT%\CoreShim.dll'...
index d37dedcff300b710d933253c224aebd630c35f14..b3efd220ff6ac0bb4bb3a3766b57d9604c60ad9f 100644 (file)
@@ -6,24 +6,24 @@ using System.Runtime.InteropServices;
 using System.Threading;
 using TestLibrary;
 
-internal static class ObjectiveC
+internal static unsafe class ObjectiveC
 {
     [DllImport(nameof(ObjectiveC))]
-    internal static extern IntPtr initObject();
+    public static extern IntPtr initObject();
     [DllImport(nameof(ObjectiveC))]
-    internal static extern void autoreleaseObject(IntPtr art);
+    public static extern void autoreleaseObject(IntPtr art);
     [DllImport(nameof(ObjectiveC))]
-    internal static extern int getNumReleaseCalls();
+    public static extern int getNumReleaseCalls();
 }
 
 public class AutoReleaseTest
 {
     public static int Main()
     {
-        AppContext.SetSwitch("System.Threading.ThreadPool.EnableDispatchAutoreleasePool", true);
         try
         {
-            TestAutoRelease();
+            ValidateNewManagedThreadAutoRelease();
+            ValidateThreadPoolAutoRelease();
         }
         catch (Exception e)
         {
@@ -34,8 +34,40 @@ public class AutoReleaseTest
         return 100;
     }
 
-    private static void TestAutoRelease()
+    private static void ValidateNewManagedThreadAutoRelease()
     {
+        Console.WriteLine($"Running {nameof(ValidateNewManagedThreadAutoRelease)}...");
+        using (AutoResetEvent evt = new AutoResetEvent(false))
+        {
+            int numReleaseCalls = ObjectiveC.getNumReleaseCalls();
+
+            RunScenario(evt);
+
+            // Trigger the GC and wait to clean up the allocated managed Thread instance.
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+
+            Assert.AreEqual(numReleaseCalls + 1, ObjectiveC.getNumReleaseCalls());
+        }
+
+        static void RunScenario(AutoResetEvent evt)
+        {
+            IntPtr obj = ObjectiveC.initObject();
+            var thread = new Thread(_ =>
+            {
+                ObjectiveC.autoreleaseObject(obj);
+                evt.Set();
+            });
+            thread.Start();
+
+            evt.WaitOne();
+            thread.Join();
+        }
+    }
+
+    private static void ValidateThreadPoolAutoRelease()
+    {
+        Console.WriteLine($"Running {nameof(ValidateThreadPoolAutoRelease)}...");
         using (AutoResetEvent evt = new AutoResetEvent(false))
         {
             int numReleaseCalls = ObjectiveC.getNumReleaseCalls();
index 8bc037103996553aeb491e8213f0e8a3a93f1c8d..4287f1cc2a1542c028357d6c9e381eb39deaaa68 100644 (file)
@@ -4,6 +4,9 @@
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <CLRTestTargetUnsupported Condition="'$(TargetsOSX)' != 'true' and '$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'">true</CLRTestTargetUnsupported>
   </PropertyGroup>
+  <ItemGroup>
+    <RuntimeHostConfigurationOption Include="System.Threading.Thread.EnableAutoreleasePool" Value="true" Trim="true" />
+  </ItemGroup>
   <ItemGroup>
     <Compile Include="*.cs" />
   </ItemGroup>