diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md
index 1171f8fb4b6d20..415b5f862146df 100644
--- a/docs/workflow/trimming/feature-switches.md
+++ b/docs/workflow/trimming/feature-switches.md
@@ -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. |
diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props
index dcd11139bfa9e2..67a5eab17c8684 100644
--- a/src/coreclr/clr.featuredefines.props
+++ b/src/coreclr/clr.featuredefines.props
@@ -35,6 +35,10 @@
true
+
+ true
+
+
$(DefineConstants);FEATURE_ARRAYSTUB_AS_IL
$(DefineConstants);FEATURE_MULTICASTSTUB_AS_IL
@@ -44,6 +48,7 @@
$(DefineConstants);FEATURE_COMWRAPPERS
$(DefineConstants);FEATURE_COMINTEROP
$(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT
+ $(DefineConstants);FEATURE_OBJCMARSHAL
$(DefineConstants);FEATURE_MANAGED_ETW
$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS
$(DefineConstants);FEATURE_PERFTRACING
diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake
index 008332d3443f33..dfc2f617b3b37a 100644
--- a/src/coreclr/clrdefinitions.cmake
+++ b/src/coreclr/clrdefinitions.cmake
@@ -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)
diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp
index 542de57517f574..6f45022d10b777 100644
--- a/src/coreclr/vm/assembly.cpp
+++ b/src/coreclr/vm/assembly.cpp
@@ -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);
}
}
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index 77120d5f81d07f..e8c83ecf31f1aa 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -1030,7 +1030,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;
@@ -1056,7 +1056,6 @@ void EEStartupHelper()
// Perform CoreLib consistency check if requested
g_CoreLib.CheckExtended();
-
#endif // _DEBUG
#endif // !CROSSGEN_COMPILE
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 1821750337f4b9..86b99fcb832015 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -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)
diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
index cc71b577a74c7f..22c6826d7be8e9 100644
--- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
+++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
@@ -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
diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp
index b09b028c9ae1a4..a303400cbd7447 100644
--- a/src/coreclr/vm/finalizerthread.cpp
+++ b/src/coreclr/vm/finalizerthread.cpp
@@ -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)
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index 9efb8648a92b99..1ac8d476c25091 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -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
diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp
index ccf2af5410122b..deaafb66d1b634 100644
--- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp
+++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp
@@ -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
diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp
index 62f2b7eacd919b..a7adbd9c0266ee 100644
--- a/src/coreclr/vm/threads.cpp
+++ b/src/coreclr/vm/threads.cpp
@@ -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,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 @@ class ApartmentSpyImpl : public IUnknownCommon 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)
diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h
index f4c66fe0c53f10..50bf121eb0418a 100644
--- a/src/coreclr/vm/threads.h
+++ b/src/coreclr/vm/threads.h
@@ -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 @@ class Thread
// 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 @@ class Thread
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 @@ class Thread
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+ void FinishInitialization();
+
#ifdef FEATURE_COMINTEROP
bool IsDisableComObjectEagerCleanup()
{
@@ -1622,7 +1636,7 @@ class Thread
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 @@ class Thread
public:
-
//--------------------------------------------------------------
// Constructor.
//--------------------------------------------------------------
@@ -1764,16 +1777,15 @@ class Thread
//--------------------------------------------------------------
// 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 @@ class Thread
// 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 @@ class Thread
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 @@ class Thread
// 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 @@ class ThreadStore
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 @@ class ThreadStore
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 @@ class ThreadStore
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 @@ class ThreadStore
// 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 @@ class ThreadStore
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.
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 2302e2e4619dcb..68e4caf6e69e9e 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -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();
diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.OSX.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.OSX.xml
index 61dcb5b5a7bf21..3b747687397c0b 100644
--- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.OSX.xml
+++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.OSX.xml
@@ -1,7 +1,7 @@
-
-
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index b8f3489e465263..b91ee01b9144f6 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -1912,7 +1912,6 @@
-
@@ -2033,4 +2032,8 @@
Interop\Windows\Kernel32\Interop.Threading.cs
+
+
+
+
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
index 00000000000000..aea7d44ed1fb1d
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/AutoreleasePool.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
index 5baaa386b34741..aa6e8c6f8d582a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs
@@ -63,6 +63,11 @@ private void RunWorker()
Delegate start = _start;
_start = null!;
+#if FEATURE_OBJCMARSHAL
+ if (AutoreleasePool.EnableAutoreleasePool)
+ AutoreleasePool.CreateAutoreleasePool();
+#endif
+
if (start is ThreadStart threadStart)
{
threadStart();
@@ -76,6 +81,14 @@ private void RunWorker()
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()
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.AutoreleasePool.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.AutoreleasePool.OSX.cs
index 6991b82daf84a9..8e378ce9bf434e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.AutoreleasePool.OSX.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.AutoreleasePool.OSX.cs
@@ -6,12 +6,6 @@
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)]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
index bb55916ef78f11..f08e4ba4d7fe0a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs
@@ -691,8 +691,8 @@ internal static bool Dispatch()
//
// 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);
}
diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
index b6b4f889a81fb4..66e5328b631567 100644
--- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -110,12 +110,14 @@
true
true
true
+ true
$(DefineConstants);FEATURE_MANAGED_ETW
$(DefineConstants);FEATURE_MANAGED_ETW_CHANNELS
$(DefineConstants);FEATURE_PERFTRACING
+ $(DefineConstants);FEATURE_OBJCMARSHAL
@@ -131,6 +133,8 @@
+
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
index 00000000000000..2b19af074bedcc
--- /dev/null
+++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.OSX.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h
index 72d8dbbf923744..cd190c8ea0ab7b 100644
--- a/src/mono/mono/metadata/class-internals.h
+++ b/src/mono/mono/metadata/class-internals.h
@@ -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;
diff --git a/src/mono/mono/metadata/domain.c b/src/mono/mono/metadata/domain.c
index 1a9df287e8fb5a..62975338ea8d9e 100644
--- a/src/mono/mono/metadata/domain.c
+++ b/src/mono/mono/metadata/domain.c
@@ -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");
diff --git a/src/mono/mono/metadata/gc.c b/src/mono/mono/metadata/gc.c
index 110ebee75c5327..e6564335b949aa 100644
--- a/src/mono/mono/metadata/gc.c
+++ b/src/mono/mono/metadata/gc.c
@@ -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);
diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h
index 96d2ed237884da..78502016cc2fa5 100644
--- a/src/mono/mono/metadata/object-internals.h
+++ b/src/mono/mono/metadata/object-internals.h
@@ -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;
diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c
index 0b1f39741d0d77..1e59f65a70e0f8 100644
--- a/src/mono/mono/metadata/object.c
+++ b/src/mono/mono/metadata/object.c
@@ -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
diff --git a/src/mono/mono/metadata/threads-types.h b/src/mono/mono/metadata/threads-types.h
index e3869ca5b5fdd5..394a526c5524d1 100644
--- a/src/mono/mono/metadata/threads-types.h
+++ b/src/mono/mono/metadata/threads-types.h
@@ -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);
diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c
index 7dabfc87a2b8d4..c5f0f4306a7a5a 100644
--- a/src/mono/mono/metadata/threads.c
+++ b/src/mono/mono/metadata/threads.c
@@ -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)
{
diff --git a/src/tests/Common/CLRTest.Execute.Bash.targets b/src/tests/Common/CLRTest.Execute.Bash.targets
index 1814a296feee81..c506de46b2bdcf 100644
--- a/src/tests/Common/CLRTest.Execute.Bash.targets
+++ b/src/tests/Common/CLRTest.Execute.Bash.targets
@@ -19,7 +19,7 @@ WARNING: When setting properties based on their current state (for example:
-
+
+ DependsOnTargets="FetchExternalPropertiesForXplat;$(BashScriptSnippetGen);GetIlasmRoundTripBashScript">
0
+ @(RuntimeHostConfigurationOption -> '-p "%(Identity)=%(Value)"', ' ')
- <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"$CORE_ROOT/corerun"
+ <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"$CORE_ROOT/corerun" $(CoreRunArgs)
+ @(RuntimeHostConfigurationOption -> '-p "%(Identity)=%(Value)"', ' ')
- <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe"
+ <_CLRTestRunFile Condition="'$(CLRTestIsHosted)'=='true'">"%CORE_ROOT%\corerun.exe" $(CoreRunArgs)
+ {
+ 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();
diff --git a/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj b/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
index 8bc03710399655..4287f1cc2a1542 100644
--- a/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
+++ b/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
@@ -4,6 +4,9 @@
true
true
+
+
+