Add support for Cooperative Suspend when running on Mono
Informs mono runtime that Job System, Thread Pool or Content Load threads can wait when they are going idle between tasks.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
@@ -346,17 +347,21 @@ int32 LoadingThread::Run()
|
||||
ContentLoadTask* task;
|
||||
ThisLoadThread = this;
|
||||
|
||||
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
|
||||
while (Platform::AtomicRead(&_exitFlag) == 0)
|
||||
{
|
||||
if (LoadTasks.try_dequeue(task))
|
||||
{
|
||||
Run(task);
|
||||
MONO_THREAD_INFO_GET(monoThreadInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
|
||||
LoadTasksMutex.Lock();
|
||||
LoadTasksSignal.Wait(LoadTasksMutex);
|
||||
LoadTasksMutex.Unlock();
|
||||
MONO_EXIT_GC_SAFE_WITH_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#if defined(__clang__)
|
||||
// Helper utility to override vtable entry with automatic restore
|
||||
// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method
|
||||
// See BindingsGenerator.Cpp.cs that generates virtual method wrappers for scripting to properly call overriden base method
|
||||
struct FLAXENGINE_API VTableFunctionInjector
|
||||
{
|
||||
void** VTableAddr;
|
||||
@@ -100,3 +100,49 @@ T& InternalGetReference(T* obj)
|
||||
DebugLog::ThrowNullReference();
|
||||
return *obj;
|
||||
}
|
||||
|
||||
#ifdef USE_MONO_AOT_COOP
|
||||
|
||||
// Cooperative Suspend - where threads suspend themselves when the runtime requests it.
|
||||
// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/
|
||||
typedef struct _MonoStackData {
|
||||
void* stackpointer;
|
||||
const char* function_name;
|
||||
} MonoStackData;
|
||||
#if BUILD_DEBUG
|
||||
#define MONO_STACKDATA(x) MonoStackData x = { &x, __func__ }
|
||||
#else
|
||||
#define MONO_STACKDATA(x) MonoStackData x = { &x, NULL }
|
||||
#endif
|
||||
#define MONO_THREAD_INFO_TYPE struct MonoThreadInfo
|
||||
DLLIMPORT extern "C" MONO_THREAD_INFO_TYPE* mono_thread_info_attach(void);
|
||||
DLLIMPORT extern "C" void* mono_threads_enter_gc_safe_region_with_info(MONO_THREAD_INFO_TYPE* info, MonoStackData* stackdata);
|
||||
DLLIMPORT extern "C" void mono_threads_exit_gc_safe_region_internal(void* cookie, MonoStackData* stackdata);
|
||||
#ifndef _MONO_UTILS_FORWARD_
|
||||
typedef struct _MonoDomain MonoDomain;
|
||||
DLLIMPORT extern "C" MonoDomain* mono_domain_get(void);
|
||||
#endif
|
||||
#define MONO_ENTER_GC_SAFE \
|
||||
do { \
|
||||
MONO_STACKDATA(__gc_safe_dummy); \
|
||||
void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_internal(&__gc_safe_dummy)
|
||||
#define MONO_EXIT_GC_SAFE \
|
||||
mono_threads_exit_gc_safe_region_internal(__gc_safe_cookie, &__gc_safe_dummy); \
|
||||
} while (0)
|
||||
#define MONO_ENTER_GC_SAFE_WITH_INFO(info) \
|
||||
do { \
|
||||
MONO_STACKDATA(__gc_safe_dummy); \
|
||||
void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_with_info((info), &__gc_safe_dummy)
|
||||
#define MONO_EXIT_GC_SAFE_WITH_INFO MONO_EXIT_GC_SAFE
|
||||
#define MONO_THREAD_INFO_GET(info) if (!info && mono_domain_get()) info = mono_thread_info_attach()
|
||||
|
||||
#else
|
||||
|
||||
#define MONO_ENTER_GC_SAFE
|
||||
#define MONO_EXIT_GC_SAFE
|
||||
#define MONO_ENTER_GC_SAFE_WITH_INFO(info)
|
||||
#define MONO_EXIT_GC_SAFE_WITH_INFO
|
||||
#define MONO_THREAD_INFO_GET(info)
|
||||
#define mono_thread_info_attach() nullptr
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2147,7 +2147,13 @@ bool InitHostfxr()
|
||||
#endif
|
||||
|
||||
// Adjust GC threads suspending mode to not block attached native threads (eg. Job System)
|
||||
// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/
|
||||
#if USE_MONO_AOT_COOP
|
||||
Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("coop"));
|
||||
Platform::SetEnvironmentVariable(TEXT("MONO_SLEEP_ABORT_LIMIT"), TEXT("5000")); // in ms
|
||||
#else
|
||||
Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("preemptive"));
|
||||
#endif
|
||||
|
||||
#if defined(USE_MONO_AOT_MODE)
|
||||
// Enable AOT mode (per-platform)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if USE_CSHARP
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#endif
|
||||
|
||||
#define JOB_SYSTEM_ENABLED 1
|
||||
@@ -184,6 +185,7 @@ int32 JobSystemThread::Run()
|
||||
JobData data;
|
||||
Function<void(int32)> job;
|
||||
bool attachCSharpThread = true;
|
||||
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
|
||||
while (Platform::AtomicRead(&ExitFlag) == 0)
|
||||
{
|
||||
// Try to get a job
|
||||
@@ -205,6 +207,7 @@ int32 JobSystemThread::Run()
|
||||
{
|
||||
MCore::Thread::Attach();
|
||||
attachCSharpThread = false;
|
||||
monoThreadInfo = mono_thread_info_attach();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -244,9 +247,11 @@ int32 JobSystemThread::Run()
|
||||
else
|
||||
{
|
||||
// Wait for signal
|
||||
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
|
||||
JobsMutex.Lock();
|
||||
JobsSignal.Wait(JobsMutex);
|
||||
JobsMutex.Unlock();
|
||||
MONO_EXIT_GC_SAFE_WITH_INFO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "Engine/Platform/CPUInfo.h"
|
||||
#include "Engine/Platform/Thread.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
|
||||
FLAXENGINE_API bool IsInMainThread()
|
||||
{
|
||||
@@ -117,6 +118,7 @@ int32 ThreadPool::ThreadProc()
|
||||
Platform::SetThreadAffinityMask(THREAD_POOL_AFFINITY_MASK((int32)index));
|
||||
#endif
|
||||
ThreadPoolTask* task;
|
||||
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
|
||||
|
||||
// Work until end
|
||||
while (Platform::AtomicRead(&ThreadPoolImpl::ExitFlag) == 0)
|
||||
@@ -125,12 +127,15 @@ int32 ThreadPool::ThreadProc()
|
||||
if (ThreadPoolImpl::Jobs.try_dequeue(task))
|
||||
{
|
||||
task->Execute();
|
||||
MONO_THREAD_INFO_GET(monoThreadInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
|
||||
ThreadPoolImpl::JobsMutex.Lock();
|
||||
ThreadPoolImpl::JobsSignal.Wait(ThreadPoolImpl::JobsMutex);
|
||||
ThreadPoolImpl::JobsMutex.Unlock();
|
||||
MONO_EXIT_GC_SAFE_WITH_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user