summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Gillespie <segilles@microsoft.com>2017-11-09 09:58:47 -0800
committerGitHub <noreply@github.com>2017-11-09 09:58:47 -0800
commite64cbb5c722ab9b7d93ca32445e17df1115be70c (patch)
tree493ffcbff24c7d14c950187606a3635e51c05bec /src
parentb45e91e74ff4d721e3b44e260e673ef024ddb774 (diff)
downloadcoreclr-e64cbb5c722ab9b7d93ca32445e17df1115be70c.tar.gz
coreclr-e64cbb5c722ab9b7d93ca32445e17df1115be70c.tar.bz2
coreclr-e64cbb5c722ab9b7d93ca32445e17df1115be70c.zip
[Local GC] Unify background GC thread and server GC thread creation (#14821)
* Initial cut, ignoring thread affinity * Integrate thread affinity * Affinity for standalone Windows * Add 'specialness' and the thread name as arguments to CreateThread * First crack at unified implementation * Set priority for server GC threads * Remove unused parameter * Address code review feedback and remove some dead code that broke the clang build * Use char* on the interface instead of wchar_t (doesn't play well cross-platform) * Rename IsGCSpecialThread -> CurrentThreadWasCreatedByGC * Code review feedback and fix up the build * rename CurrentThreadWasCreatedByGC -> WasCurrentThreadCreatedByGC * Fix a contract violation when converting string encodings * Thread::CreateUtilityThread returns a thread that is not suspended - restarting a non-suspended thread is incorrect * CreateUnsuspendableThread -> CreateNonSuspendableThread
Diffstat (limited to 'src')
-rw-r--r--src/gc/env/gcenv.ee.h6
-rw-r--r--src/gc/env/gcenv.os.h26
-rw-r--r--src/gc/gc.cpp73
-rw-r--r--src/gc/gcenv.ee.standalone.inl17
-rw-r--r--src/gc/gcinterface.ee.h35
-rw-r--r--src/gc/gcpriv.h4
-rw-r--r--src/gc/sample/gcenv.ee.cpp13
-rw-r--r--src/gc/unix/gcenv.unix.cpp87
-rw-r--r--src/gc/windows/gcenv.windows.cpp104
-rw-r--r--src/vm/gcenv.ee.cpp252
-rw-r--r--src/vm/gcenv.ee.h4
-rw-r--r--src/vm/gcenv.os.cpp123
12 files changed, 394 insertions, 350 deletions
diff --git a/src/gc/env/gcenv.ee.h b/src/gc/env/gcenv.ee.h
index 3d2f659a75..d747a5bbef 100644
--- a/src/gc/env/gcenv.ee.h
+++ b/src/gc/env/gcenv.ee.h
@@ -56,9 +56,6 @@ public:
static bool CatchAtSafePoint(Thread * pThread);
static void GcEnumAllocContexts(enum_alloc_context_func* fn, void* param);
-
- static Thread* CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg);
-
// Diagnostics methods.
static void DiagGCStart(int gen, bool isInduced);
static void DiagUpdateGenerationBounds();
@@ -81,7 +78,8 @@ public:
static bool GetStringConfigValue(const char* key, const char** value);
static void FreeStringConfigValue(const char* key);
static bool IsGCThread();
- static bool IsGCSpecialThread();
+ static bool WasCurrentThreadCreatedByGC();
+ static bool CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name);
};
#endif // __GCENV_EE_H__
diff --git a/src/gc/env/gcenv.os.h b/src/gc/env/gcenv.os.h
index aee1330f3f..1707f0dabe 100644
--- a/src/gc/env/gcenv.os.h
+++ b/src/gc/env/gcenv.os.h
@@ -243,15 +243,6 @@ public:
// Thread and process
//
- // Create a new thread
- // Parameters:
- // function - the function to be executed by the thread
- // param - parameters of the thread
- // affinity - processor affinity of the thread
- // Return:
- // true if it has succeeded, false if it has failed
- static bool CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity);
-
// Causes the calling thread to sleep for the specified number of milliseconds
// Parameters:
// sleepMSec - time to sleep before switching to another thread
@@ -307,6 +298,23 @@ public:
// The number of processors
static uint32_t GetCurrentProcessCpuCount();
+ // Sets the calling thread's affinity to only run on the processor specified
+ // in the GCThreadAffinity structure.
+ // Parameters:
+ // affinity - The requested affinity for the calling thread. At most one processor
+ // can be provided.
+ // Return:
+ // true if setting the affinity was successful, false otherwise.
+ static bool SetThreadAffinity(GCThreadAffinity* affinity);
+
+ // Boosts the calling thread's thread priority to a level higher than the default
+ // for new threads.
+ // Parameters:
+ // None.
+ // Return:
+ // true if the priority boost was successful, false otherwise.
+ static bool BoostThreadPriority();
+
// Get affinity mask of the current process
// Parameters:
// processMask - affinity mask for the specified process
diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp
index 042f2f4929..445812cd40 100644
--- a/src/gc/gc.cpp
+++ b/src/gc/gc.cpp
@@ -1740,7 +1740,7 @@ static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
inline
static void leave_spin_lock(GCSpinLock *pSpinLock)
{
- bool gc_thread_p = GCToEEInterface::IsGCSpecialThread();
+ bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC();
// _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
pSpinLock->released_by_gc_p = gc_thread_p;
pSpinLock->holding_thread = (Thread*) -1;
@@ -5351,23 +5351,7 @@ void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affini
bool gc_heap::create_gc_thread ()
{
dprintf (3, ("Creating gc thread\n"));
-
- GCThreadAffinity affinity;
- affinity.Group = GCThreadAffinity::None;
- affinity.Processor = GCThreadAffinity::None;
-
- if (!gc_thread_no_affinitize_p)
- {
- // We are about to set affinity for GC threads. It is a good place to set up NUMA and
- // CPU groups because the process mask, processor number, and group number are all
- // readily available.
- if (CPUGroupInfo::CanEnableGCCPUGroups())
- set_thread_group_affinity_for_heap(heap_number, &affinity);
- else
- set_thread_affinity_mask_for_heap(heap_number, &affinity);
- }
-
- return GCToOSInterface::CreateThread(gc_thread_stub, this, &affinity);
+ return GCToEEInterface::CreateThread(gc_thread_stub, this, false, "Server GC");
}
#ifdef _MSC_VER
@@ -24917,27 +24901,29 @@ void gc_heap::compact_phase (int condemned_gen_number,
#endif //_MSC_VER
void gc_heap::gc_thread_stub (void* arg)
{
- ClrFlsSetThreadType (ThreadType_GC);
- STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
+ gc_heap* heap = (gc_heap*)arg;
+ if (!gc_thread_no_affinitize_p)
+ {
+ GCThreadAffinity affinity;
+ affinity.Group = GCThreadAffinity::None;
+ affinity.Processor = GCThreadAffinity::None;
-#ifndef FEATURE_REDHAWK
- // We commit the thread's entire stack to ensure we're robust in low memory conditions.
- BOOL fSuccess = Thread::CommitThreadStack(NULL);
+ // We are about to set affinity for GC threads. It is a good place to set up NUMA and
+ // CPU groups because the process mask, processor number, and group number are all
+ // readily available.
+ if (CPUGroupInfo::CanEnableGCCPUGroups())
+ set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
+ else
+ set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
- if (!fSuccess)
- {
-#ifdef BACKGROUND_GC
- // For background GC we revert to doing a blocking GC.
- return;
-#else
- STRESS_LOG0(LF_GC, LL_ALWAYS, "Thread::CommitThreadStack failed.");
- _ASSERTE(!"Thread::CommitThreadStack failed.");
- GCToEEInterface::HandleFatalError(COR_E_STACKOVERFLOW);
-#endif //BACKGROUND_GC
+ if (!GCToOSInterface::SetThreadAffinity(&affinity))
+ {
+ dprintf(1, ("Failed to set thread affinity for server GC thread"));
+ }
}
-#endif // FEATURE_REDHAWK
- gc_heap* heap = (gc_heap*)arg;
+ // server GC threads run at a higher priority than normal.
+ GCToOSInterface::BoostThreadPriority();
_alloca (256*heap->heap_number);
heap->gc_thread_function();
}
@@ -24953,10 +24939,12 @@ void gc_heap::gc_thread_stub (void* arg)
#pragma warning(push)
#pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
#endif //_MSC_VER
-uint32_t __stdcall gc_heap::bgc_thread_stub (void* arg)
+void gc_heap::bgc_thread_stub (void* arg)
{
gc_heap* heap = (gc_heap*)arg;
- return heap->bgc_thread_function();
+ heap->bgc_thread = GCToEEInterface::GetThread();
+ assert(heap->bgc_thread != nullptr);
+ heap->bgc_thread_function();
}
#ifdef _MSC_VER
#pragma warning(pop)
@@ -26700,7 +26688,6 @@ BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
BOOL success = FALSE;
BOOL thread_created = FALSE;
dprintf (2, ("Preparing gc thread"));
-
gh->bgc_threads_timeout_cs.Enter();
if (!(gh->bgc_thread_running))
{
@@ -26730,9 +26717,7 @@ BOOL gc_heap::create_bgc_thread(gc_heap* gh)
//dprintf (2, ("Creating BGC thread"));
- gh->bgc_thread = GCToEEInterface::CreateBackgroundThread(gh->bgc_thread_stub, gh);
- gh->bgc_thread_running = (gh->bgc_thread != NULL);
-
+ gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, "Background GC");
return gh->bgc_thread_running;
}
@@ -26908,7 +26893,7 @@ void gc_heap::kill_gc_thread()
recursive_gc_sync::shutdown();
}
-uint32_t gc_heap::bgc_thread_function()
+void gc_heap::bgc_thread_function()
{
assert (background_gc_done_event.IsValid());
assert (bgc_start_event.IsValid());
@@ -27066,7 +27051,7 @@ uint32_t gc_heap::bgc_thread_function()
FireEtwGCTerminateConcurrentThread_V1(GetClrInstanceId());
dprintf (3, ("bgc_thread thread exiting"));
- return 0;
+ return;
}
#endif //BACKGROUND_GC
@@ -34028,7 +34013,7 @@ bool GCHeap::StressHeap(gc_alloc_context * context)
#ifdef BACKGROUND_GC
// don't trigger a GC from the GC threads but still trigger GCs from user threads.
- if (GCToEEInterface::IsGCSpecialThread())
+ if (GCToEEInterface::WasCurrentThreadCreatedByGC())
{
return FALSE;
}
diff --git a/src/gc/gcenv.ee.standalone.inl b/src/gc/gcenv.ee.standalone.inl
index 0b6a1cfb11..a9e45c953b 100644
--- a/src/gc/gcenv.ee.standalone.inl
+++ b/src/gc/gcenv.ee.standalone.inl
@@ -132,12 +132,6 @@ inline void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, vo
g_theGCToCLR->GcEnumAllocContexts(fn, param);
}
-inline Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
-{
- assert(g_theGCToCLR != nullptr);
- return g_theGCToCLR->CreateBackgroundThread(threadStart, arg);
-}
-
inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
{
assert(g_theGCToCLR != nullptr);
@@ -252,10 +246,17 @@ inline bool GCToEEInterface::IsGCThread()
return g_theGCToCLR->IsGCThread();
}
-inline bool GCToEEInterface::IsGCSpecialThread()
+inline bool GCToEEInterface::WasCurrentThreadCreatedByGC()
{
assert(g_theGCToCLR != nullptr);
- return g_theGCToCLR->IsGCSpecialThread();
+ return g_theGCToCLR->WasCurrentThreadCreatedByGC();
}
+inline bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name)
+{
+ assert(g_theGCToCLR != nullptr);
+ return g_theGCToCLR->CreateThread(threadStart, arg, is_suspendable, name);
+}
+
+
#endif // __GCTOENV_EE_STANDALONE_INL__
diff --git a/src/gc/gcinterface.ee.h b/src/gc/gcinterface.ee.h
index d65da60678..84578b6d8a 100644
--- a/src/gc/gcinterface.ee.h
+++ b/src/gc/gcinterface.ee.h
@@ -79,6 +79,9 @@ public:
// Gets the Thread instance for the current thread, or null if no thread
// instance is associated with this thread.
+ //
+ // If the GC created the current thread, GetThread returns null for threads
+ // that were not created as suspendable (see `IGCHeap::CreateThread`).
virtual
Thread* GetThread() = 0;
@@ -98,9 +101,21 @@ public:
virtual
void GcEnumAllocContexts(enum_alloc_context_func* fn, void* param) = 0;
- // Creates and returns a new background thread.
- virtual
- Thread* CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg) = 0;
+ // Creates and returns a new thread.
+ // Parameters:
+ // threadStart - The function that will serve as the thread stub for the
+ // new thread. It will be invoked immediately upon the
+ // new thread upon creation.
+ // arg - The argument that will be passed verbatim to threadStart.
+ // is_suspendable - Whether or not the thread that is created should be suspendable
+ // from a runtime perspective. Threads that are suspendable have
+ // a VM Thread object associated with them that can be accessed
+ // using `IGCHeap::GetThread`.
+ // name - The name of this thread, optionally used for diagnostic purposes.
+ // Returns:
+ // true if the thread was started successfully, false if not.
+ virtual
+ bool CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name) = 0;
// When a GC starts, gives the diagnostics code a chance to run.
virtual
@@ -192,16 +207,18 @@ public:
virtual
void FreeStringConfigValue(const char* value) = 0;
- // Asks the EE about whether or not the current thread is a GC thread:
- // a server GC thread, background GC thread, or the thread that suspended
- // the EE at the start of a GC.
+ // Returns true if this thread is a "GC thread", or a thread capable of
+ // doing GC work. Threads are either /always/ GC threads
+ // (if they were created for this purpose - background GC threads
+ // and server GC threads) or they became GC threads by suspending the EE
+ // and initiating a collection.
virtual
bool IsGCThread() = 0;
- // Asks the EE about whether or not the current thread is a GC "special"
- // thread: a server GC thread or a background GC thread.
+ // Returns true if the current thread is either a background GC thread
+ // or a server GC thread.
virtual
- bool IsGCSpecialThread() = 0;
+ bool WasCurrentThreadCreatedByGC() = 0;
};
#endif // _GCINTERFACE_EE_H_
diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h
index c9c6fa32f9..470b82f2ef 100644
--- a/src/gc/gcpriv.h
+++ b/src/gc/gcpriv.h
@@ -2671,11 +2671,11 @@ protected:
PER_HEAP
void kill_gc_thread();
PER_HEAP
- uint32_t bgc_thread_function();
+ void bgc_thread_function();
PER_HEAP_ISOLATED
void do_background_gc();
static
- uint32_t __stdcall bgc_thread_stub (void* arg);
+ void bgc_thread_stub (void* arg);
#endif //BACKGROUND_GC
diff --git a/src/gc/sample/gcenv.ee.cpp b/src/gc/sample/gcenv.ee.cpp
index 8fd4487192..72ef9b5574 100644
--- a/src/gc/sample/gcenv.ee.cpp
+++ b/src/gc/sample/gcenv.ee.cpp
@@ -231,12 +231,6 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/)
{
}
-Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
-{
- // TODO: Implement for background GC
- return NULL;
-}
-
void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
{
}
@@ -321,7 +315,7 @@ bool GCToEEInterface::IsGCThread()
return false;
}
-bool GCToEEInterface::IsGCSpecialThread()
+bool GCToEEInterface::WasCurrentThreadCreatedByGC()
{
return false;
}
@@ -330,3 +324,8 @@ MethodTable* GCToEEInterface::GetFreeObjectMethodTable()
{
return g_pFreeObjectMethodTable;
}
+
+bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name)
+{
+ return false;
+}
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index f564b28239..75428c4707 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -402,6 +402,31 @@ size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
return 0;
}
+// Sets the calling thread's affinity to only run on the processor specified
+// in the GCThreadAffinity structure.
+// Parameters:
+// affinity - The requested affinity for the calling thread. At most one processor
+// can be provided.
+// Return:
+// true if setting the affinity was successful, false otherwise.
+bool GCToOSInterface::SetThreadAffinity(GCThreadAffinity* affinity)
+{
+ // [LOCALGC TODO] Thread affinity for unix
+ return false;
+}
+
+// Boosts the calling thread's thread priority to a level higher than the default
+// for new threads.
+// Parameters:
+// None.
+// Return:
+// true if the priority boost was successful, false otherwise.
+bool GCToOSInterface::BoostThreadPriority()
+{
+ // [LOCALGC TODO] Thread priority for unix
+ return false;
+}
+
/*++
Function:
GetFullAffinityMask
@@ -654,68 +679,6 @@ uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
return retval;
}
-// Parameters of the GC thread stub
-struct GCThreadStubParam
-{
- GCThreadFunction GCThreadFunction;
- void* GCThreadParam;
-};
-
-// GC thread stub to convert GC thread function to an OS specific thread function
-static void* GCThreadStub(void* param)
-{
- GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
- GCThreadFunction function = stubParam->GCThreadFunction;
- void* threadParam = stubParam->GCThreadParam;
-
- delete stubParam;
-
- function(threadParam);
-
- return NULL;
-}
-
-// Create a new thread for GC use
-// Parameters:
-// function - the function to be executed by the thread
-// param - parameters of the thread
-// affinity - processor affinity of the thread
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
-{
- std::unique_ptr<GCThreadStubParam> stubParam(new (std::nothrow) GCThreadStubParam());
- if (!stubParam)
- {
- return false;
- }
-
- stubParam->GCThreadFunction = function;
- stubParam->GCThreadParam = param;
-
- pthread_attr_t attrs;
-
- int st = pthread_attr_init(&attrs);
- assert(st == 0);
-
- // Create the thread as detached, that means not joinable
- st = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
- assert(st == 0);
-
- pthread_t threadId;
- st = pthread_create(&threadId, &attrs, GCThreadStub, stubParam.get());
-
- if (st == 0)
- {
- stubParam.release();
- }
-
- int st2 = pthread_attr_destroy(&attrs);
- assert(st2 == 0);
-
- return (st == 0);
-}
-
// Gets the total number of processors on the machine, not taking
// into account current process affinity.
// Return:
diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index e05233fe7c..69e5d7273a 100644
--- a/src/gc/windows/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp
@@ -387,6 +387,48 @@ size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
return 0;
}
+// Sets the calling thread's affinity to only run on the processor specified
+// in the GCThreadAffinity structure.
+// Parameters:
+// affinity - The requested affinity for the calling thread. At most one processor
+// can be provided.
+// Return:
+// true if setting the affinity was successful, false otherwise.
+bool GCToOSInterface::SetThreadAffinity(GCThreadAffinity* affinity)
+{
+ assert(affinity != nullptr);
+ if (affinity->Group != GCThreadAffinity::None)
+ {
+ assert(affinity->Processor != GCThreadAffinity::None);
+
+ GROUP_AFFINITY ga;
+ ga.Group = (WORD)affinity->Group;
+ ga.Reserved[0] = 0; // reserve must be filled with zero
+ ga.Reserved[1] = 0; // otherwise call may fail
+ ga.Reserved[2] = 0;
+ ga.Mask = (size_t)1 << affinity->Processor;
+ return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
+ }
+ else if (affinity->Processor != GCThreadAffinity::None)
+ {
+ return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << affinity->Processor);
+ }
+
+ // Given affinity must specify at least one processor to use.
+ return false;
+}
+
+// Boosts the calling thread's thread priority to a level higher than the default
+// for new threads.
+// Parameters:
+// None.
+// Return:
+// true if the priority boost was successful, false otherwise.
+bool GCToOSInterface::BoostThreadPriority()
+{
+ return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+}
+
// Get affinity mask of the current process
// Parameters:
// processMask - affinity mask for the specified process
@@ -545,68 +587,6 @@ static DWORD GCThreadStub(void* param)
return 0;
}
-
-// Create a new thread for GC use
-// Parameters:
-// function - the function to be executed by the thread
-// param - parameters of the thread
-// affinity - processor affinity of the thread
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
-{
- uint32_t thread_id;
-
- std::unique_ptr<GCThreadStubParam> stubParam(new (std::nothrow) GCThreadStubParam());
- if (!stubParam)
- {
- return false;
- }
-
- stubParam->GCThreadFunction = function;
- stubParam->GCThreadParam = param;
-
- HANDLE gc_thread = ::CreateThread(
- nullptr,
- 512 * 1024 /* Thread::StackSize_Medium */,
- (LPTHREAD_START_ROUTINE)GCThreadStub,
- stubParam.get(),
- CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
- (DWORD*)&thread_id);
-
- if (!gc_thread)
- {
- return false;
- }
-
- stubParam.release();
- bool result = !!::SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
- assert(result && "failed to set thread priority");
-
- if (affinity->Group != GCThreadAffinity::None)
- {
- assert(affinity->Processor != GCThreadAffinity::None);
- GROUP_AFFINITY ga;
- ga.Group = (WORD)affinity->Group;
- ga.Reserved[0] = 0; // reserve must be filled with zero
- ga.Reserved[1] = 0; // otherwise call may fail
- ga.Reserved[2] = 0;
- ga.Mask = (size_t)1 << affinity->Processor;
-
- bool result = !!::SetThreadGroupAffinity(gc_thread, &ga, nullptr);
- assert(result && "failed to set thread affinity");
- }
- else if (affinity->Processor != GCThreadAffinity::None)
- {
- ::SetThreadAffinityMask(gc_thread, (DWORD_PTR)1 << affinity->Processor);
- }
-
- ResumeThread(gc_thread);
- CloseHandle(gc_thread);
-
- return true;
-}
-
// Gets the total number of processors on the machine, not taking
// into account current process affinity.
// Return:
diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp
index 959687b7b8..e5da889a64 100644
--- a/src/vm/gcenv.ee.cpp
+++ b/src/vm/gcenv.ee.cpp
@@ -409,67 +409,6 @@ DWORD WINAPI BackgroundThreadStub(void* arg)
return result;
}
-Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- BackgroundThreadStubArgs threadStubArgs;
-
- threadStubArgs.arg = arg;
- threadStubArgs.thread = NULL;
- threadStubArgs.threadStart = threadStart;
- threadStubArgs.hasStarted = false;
-
- if (!threadStubArgs.threadStartedEvent.CreateAutoEventNoThrow(FALSE))
- {
- return NULL;
- }
-
- EX_TRY
- {
- threadStubArgs.thread = SetupUnstartedThread(FALSE);
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions);
-
- if (threadStubArgs.thread == NULL)
- {
- threadStubArgs.threadStartedEvent.CloseEvent();
- return NULL;
- }
-
- if (threadStubArgs.thread->CreateNewThread(0, (LPTHREAD_START_ROUTINE)BackgroundThreadStub, &threadStubArgs, W("Background GC")))
- {
- threadStubArgs.thread->SetBackground (TRUE, FALSE);
- threadStubArgs.thread->StartThread();
-
- // Wait for the thread to be in its main loop
- uint32_t res = threadStubArgs.threadStartedEvent.Wait(INFINITE, FALSE);
- threadStubArgs.threadStartedEvent.CloseEvent();
- _ASSERTE(res == WAIT_OBJECT_0);
-
- if (!threadStubArgs.hasStarted)
- {
- // The thread has failed to start and the Thread object was destroyed in the Thread::HasStarted
- // failure code path.
- return NULL;
- }
-
- return threadStubArgs.thread;
- }
-
- // Destroy the Thread object
- threadStubArgs.thread->DecExternalCount(FALSE);
- return NULL;
-}
-
//
// Diagnostics code
//
@@ -1175,7 +1114,196 @@ bool GCToEEInterface::IsGCThread()
return !!::IsGCThread();
}
-bool GCToEEInterface::IsGCSpecialThread()
+bool GCToEEInterface::WasCurrentThreadCreatedByGC()
{
return !!::IsGCSpecialThread();
}
+
+struct SuspendableThreadStubArguments
+{
+ void* Argument;
+ void (*ThreadStart)(void*);
+ Thread* Thread;
+ bool HasStarted;
+ CLREvent ThreadStartedEvent;
+};
+
+struct ThreadStubArguments
+{
+ void* Argument;
+ void (*ThreadStart)(void*);
+ HANDLE Thread;
+ bool HasStarted;
+ CLREvent ThreadStartedEvent;
+};
+
+namespace
+{
+ const size_t MaxThreadNameSize = 255;
+
+ bool CreateSuspendableThread(
+ void (*threadStart)(void*),
+ void* argument,
+ const char* name)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ SuspendableThreadStubArguments args;
+ args.Argument = argument;
+ args.ThreadStart = threadStart;
+ args.Thread = nullptr;
+ args.HasStarted = false;
+ if (!args.ThreadStartedEvent.CreateAutoEventNoThrow(FALSE))
+ {
+ return false;
+ }
+
+ EX_TRY
+ {
+ args.Thread = SetupUnstartedThread(FALSE);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ if (!args.Thread)
+ {
+ args.ThreadStartedEvent.CloseEvent();
+ return false;
+ }
+
+ auto threadStub = [](void* argument) -> DWORD
+ {
+ SuspendableThreadStubArguments* args = static_cast<SuspendableThreadStubArguments*>(argument);
+ assert(args != nullptr);
+
+ ClrFlsSetThreadType(ThreadType_GC);
+ args->Thread->SetGCSpecial(true);
+ STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY);
+ args->HasStarted = !!args->Thread->HasStarted(false);
+
+ Thread* thread = args->Thread;
+ auto threadStart = args->ThreadStart;
+ void* threadArgument = args->Argument;
+ bool hasStarted = args->HasStarted;
+ args->ThreadStartedEvent.Set();
+
+ // The stubArgs cannot be used once the event is set, since that releases wait on the
+ // event in the function that created this thread and the stubArgs go out of scope.
+ if (hasStarted)
+ {
+ threadStart(threadArgument);
+ DestroyThread(thread);
+ }
+
+ return 0;
+ };
+
+ InlineSString<MaxThreadNameSize> wideName;
+ const WCHAR* namePtr;
+ EX_TRY
+ {
+ if (name != nullptr)
+ {
+ wideName.SetUTF8(name);
+ namePtr = wideName.GetUnicode();
+ }
+ }
+ EX_CATCH
+ {
+ // we're not obligated to provide a name - if it's not valid,
+ // just report nullptr as the name.
+ namePtr = nullptr;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ if (!args.Thread->CreateNewThread(0, threadStub, &args, namePtr))
+ {
+ args.Thread->DecExternalCount(FALSE);
+ args.ThreadStartedEvent.CloseEvent();
+ return false;
+ }
+
+ args.Thread->SetBackground(TRUE, FALSE);
+ args.Thread->StartThread();
+
+ // Wait for the thread to be in its main loop
+ uint32_t res = args.ThreadStartedEvent.Wait(INFINITE, FALSE);
+ args.ThreadStartedEvent.CloseEvent();
+ _ASSERTE(res == WAIT_OBJECT_0);
+
+ if (!args.HasStarted)
+ {
+ // The thread has failed to start and the Thread object was destroyed in the Thread::HasStarted
+ // failure code path.
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CreateNonSuspendableThread(
+ void (*threadStart)(void*),
+ void* argument,
+ const char* name)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ ThreadStubArguments args;
+ args.Argument = argument;
+ args.ThreadStart = threadStart;
+ args.Thread = INVALID_HANDLE_VALUE;
+ if (!args.ThreadStartedEvent.CreateAutoEventNoThrow(FALSE))
+ {
+ return false;
+ }
+
+ auto threadStub = [](void* argument) -> DWORD
+ {
+ ThreadStubArguments* args = static_cast<ThreadStubArguments*>(argument);
+ assert(args != nullptr);
+
+ ClrFlsSetThreadType(ThreadType_GC);
+ STRESS_LOG_RESERVE_MEM(GC_STRESSLOG_MULTIPLY);
+
+ args->HasStarted = true;
+ auto threadStart = args->ThreadStart;
+ void* threadArgument = args->Argument;
+ args->ThreadStartedEvent.Set();
+
+ // The stub args cannot be used once the event is set, since that releases wait on the
+ // event in the function that created this thread and the stubArgs go out of scope.
+ threadStart(threadArgument);
+ return 0;
+ };
+
+ args.Thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, threadStub, &args);
+ if (args.Thread == INVALID_HANDLE_VALUE)
+ {
+ args.ThreadStartedEvent.CloseEvent();
+ return false;
+ }
+
+ // Wait for the thread to be in its main loop
+ uint32_t res = args.ThreadStartedEvent.Wait(INFINITE, FALSE);
+ args.ThreadStartedEvent.CloseEvent();
+ _ASSERTE(res == WAIT_OBJECT_0);
+
+ CloseHandle(args.Thread);
+ return true;
+ }
+} // anonymous namespace
+
+bool GCToEEInterface::CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (is_suspendable)
+ {
+ return CreateSuspendableThread(threadStart, arg, name);
+ }
+ else
+ {
+ return CreateNonSuspendableThread(threadStart, arg, name);
+ }
+}
diff --git a/src/vm/gcenv.ee.h b/src/vm/gcenv.ee.h
index 063fa2554e..b2ada36bcd 100644
--- a/src/vm/gcenv.ee.h
+++ b/src/vm/gcenv.ee.h
@@ -36,7 +36,6 @@ public:
gc_alloc_context * GetAllocContext(Thread * pThread);
bool CatchAtSafePoint(Thread * pThread);
void GcEnumAllocContexts(enum_alloc_context_func* fn, void* param);
- Thread* CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg);
// Diagnostics methods.
void DiagGCStart(int gen, bool isInduced);
@@ -59,7 +58,8 @@ public:
bool GetStringConfigValue(const char* key, const char** value);
void FreeStringConfigValue(const char* value);
bool IsGCThread();
- bool IsGCSpecialThread();
+ bool WasCurrentThreadCreatedByGC();
+ bool CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name);
};
} // namespace standalone
diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp
index b1b9c32635..78670b0af3 100644
--- a/src/vm/gcenv.os.cpp
+++ b/src/vm/gcenv.os.cpp
@@ -329,6 +329,50 @@ size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
return ::GetLargestOnDieCacheSize(trueSize);
}
+// Sets the calling thread's affinity to only run on the processor specified
+// in the GCThreadAffinity structure.
+// Parameters:
+// affinity - The requested affinity for the calling thread. At most one processor
+// can be provided.
+// Return:
+// true if setting the affinity was successful, false otherwise.
+bool GCToOSInterface::SetThreadAffinity(GCThreadAffinity* affinity)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ assert(affinity != nullptr);
+ if (affinity->Group != GCThreadAffinity::None)
+ {
+ assert(affinity->Processor != GCThreadAffinity::None);
+
+ GROUP_AFFINITY ga;
+ ga.Group = (WORD)affinity->Group;
+ ga.Reserved[0] = 0; // reserve must be filled with zero
+ ga.Reserved[1] = 0; // otherwise call may fail
+ ga.Reserved[2] = 0;
+ ga.Mask = (size_t)1 << affinity->Processor;
+ return !!SetThreadGroupAffinity(GetCurrentThread(), &ga, nullptr);
+ }
+ else if (affinity->Processor != GCThreadAffinity::None)
+ {
+ return !!SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR)1 << affinity->Processor);
+ }
+
+ // Given affinity must specify at least one processor to use.
+ return false;
+}
+
+// Boosts the calling thread's thread priority to a level higher than the default
+// for new threads.
+// Parameters:
+// None.
+// Return:
+// true if the priority boost was successful, false otherwise.
+bool GCToOSInterface::BoostThreadPriority()
+{
+ return !!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+}
+
// Get affinity mask of the current process
// Parameters:
// processMask - affinity mask for the specified process
@@ -605,85 +649,6 @@ uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
return ::GetTickCount();
}
-// Parameters of the GC thread stub
-struct GCThreadStubParam
-{
- GCThreadFunction GCThreadFunction;
- void* GCThreadParam;
-};
-
-// GC thread stub to convert GC thread function to an OS specific thread function
-static DWORD WINAPI GCThreadStub(void* param)
-{
- WRAPPER_NO_CONTRACT;
-
- GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
- GCThreadFunction function = stubParam->GCThreadFunction;
- void* threadParam = stubParam->GCThreadParam;
-
- delete stubParam;
-
- function(threadParam);
-
- return 0;
-}
-
-// Create a new thread
-// Parameters:
-// function - the function to be executed by the thread
-// param - parameters of the thread
-// affinity - processor affinity of the thread
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
-{
- LIMITED_METHOD_CONTRACT;
-
- uint32_t thread_id;
-
- NewHolder<GCThreadStubParam> stubParam = new (nothrow) GCThreadStubParam();
- if (stubParam == NULL)
- {
- return false;
- }
-
- stubParam->GCThreadFunction = function;
- stubParam->GCThreadParam = param;
-
- HANDLE gc_thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, GCThreadStub, stubParam, CREATE_SUSPENDED, (DWORD*)&thread_id);
-
- if (!gc_thread)
- {
- return false;
- }
-
- stubParam.SuppressRelease();
-
- SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
-
- if (affinity->Group != GCThreadAffinity::None)
- {
- _ASSERTE(affinity->Processor != GCThreadAffinity::None);
- GROUP_AFFINITY ga;
- ga.Group = (WORD)affinity->Group;
- ga.Reserved[0] = 0; // reserve must be filled with zero
- ga.Reserved[1] = 0; // otherwise call may fail
- ga.Reserved[2] = 0;
- ga.Mask = (size_t)1 << affinity->Processor;
-
- CPUGroupInfo::SetThreadGroupAffinity(gc_thread, &ga, NULL);
- }
- else if (affinity->Processor != GCThreadAffinity::None)
- {
- SetThreadAffinityMask(gc_thread, (DWORD_PTR)1 << affinity->Processor);
- }
-
- ResumeThread(gc_thread);
- CloseHandle(gc_thread);
-
- return true;
-}
-
uint32_t GCToOSInterface::GetTotalProcessorCount()
{
LIMITED_METHOD_CONTRACT;