#include "il2cpp-config.h"
|
|
#include "gc/GarbageCollector.h"
|
#include "mono/ThreadPool/threadpool-ms.h"
|
#include "mono/ThreadPool/ThreadPoolDataStructures.h"
|
#include "mono/ThreadPool/ThreadPoolMacros.h"
|
#include "mono/ThreadPool/ThreadPoolMonitorThread.h"
|
#include "mono/ThreadPool/ThreadPoolWorkerThread.h"
|
#include "vm/Runtime.h"
|
#include "vm/Thread.h"
|
#include "os/Time.h"
|
#include "os/CpuInfo.h"
|
|
#define MONITOR_INTERVAL 500 // ms
|
#define MONITOR_MINIMAL_LIFETIME 60 * 1000 // ms
|
|
static int32_t s_MonitorStatus = MONITOR_STATUS_NOT_RUNNING;
|
|
MonitorStatus GetMonitorStatus()
|
{
|
return static_cast<MonitorStatus>(s_MonitorStatus);
|
}
|
|
static int32_t cpu_info_usage(void* prev)
|
{
|
// Note : Implementing CpuInfo on all platforms will be challenging, so for now we are going to cheat
|
// and always say it's low
|
#if !IL2CPP_USE_GENERIC_CPU_INFO
|
return il2cpp::os::CpuInfo::Usage(prev);
|
#else
|
return CPU_USAGE_LOW;
|
#endif
|
}
|
|
static Il2CppException* mono_thread_interruption_checkpoint(void)
|
{
|
// For now just do nothing. The one place this is used doesn't care about the return value
|
return NULL;
|
}
|
|
/* LOCKING: threadpool->domains_lock must be held */
|
static bool domain_any_has_request(void)
|
{
|
unsigned int i;
|
|
for (i = 0; i < g_ThreadPool->domains.size(); ++i)
|
{
|
ThreadPoolDomain *tmp = g_ThreadPool->domains[i];
|
if (tmp->outstanding_request > 0)
|
return true;
|
}
|
|
return false;
|
}
|
|
static bool monitor_sufficient_delay_since_last_dequeue(void)
|
{
|
int64_t threshold;
|
|
IL2CPP_ASSERT(g_ThreadPool);
|
|
if (g_ThreadPool->cpu_usage < CPU_USAGE_LOW)
|
{
|
threshold = MONITOR_INTERVAL;
|
}
|
else
|
{
|
ThreadPoolCounter counter;
|
counter.as_int64_t = COUNTER_READ();
|
threshold = counter._.max_working * MONITOR_INTERVAL * 2;
|
}
|
|
return il2cpp::os::Time::GetTicksMillisecondsMonotonic() >= g_ThreadPool->heuristic_last_dequeue + threshold;
|
}
|
|
static bool monitor_should_keep_running(void)
|
{
|
static int64_t last_should_keep_running = -1;
|
|
IL2CPP_ASSERT(s_MonitorStatus == MONITOR_STATUS_WAITING_FOR_REQUEST || s_MonitorStatus == MONITOR_STATUS_REQUESTED);
|
|
if (il2cpp::vm::Atomic::Exchange(&s_MonitorStatus, MONITOR_STATUS_WAITING_FOR_REQUEST) == MONITOR_STATUS_WAITING_FOR_REQUEST)
|
{
|
bool should_keep_running = true, force_should_keep_running = false;
|
|
if (il2cpp::vm::Runtime::IsShuttingDown())
|
{
|
should_keep_running = false;
|
}
|
else
|
{
|
g_ThreadPool->domains_lock.Lock();
|
if (!domain_any_has_request())
|
should_keep_running = false;
|
g_ThreadPool->domains_lock.Unlock();
|
|
if (!should_keep_running)
|
{
|
if (last_should_keep_running == -1 || il2cpp::os::Time::GetTicks100NanosecondsMonotonic() - last_should_keep_running < MONITOR_MINIMAL_LIFETIME * 1000 * 10)
|
{
|
should_keep_running = force_should_keep_running = true;
|
}
|
}
|
}
|
|
if (should_keep_running)
|
{
|
if (last_should_keep_running == -1 || !force_should_keep_running)
|
last_should_keep_running = il2cpp::os::Time::GetTicks100NanosecondsMonotonic();
|
}
|
else
|
{
|
last_should_keep_running = -1;
|
if (il2cpp::vm::Atomic::CompareExchange(&s_MonitorStatus, MONITOR_STATUS_NOT_RUNNING, MONITOR_STATUS_WAITING_FOR_REQUEST) == MONITOR_STATUS_WAITING_FOR_REQUEST)
|
return false;
|
}
|
}
|
|
IL2CPP_ASSERT(s_MonitorStatus == MONITOR_STATUS_WAITING_FOR_REQUEST || s_MonitorStatus == MONITOR_STATUS_REQUESTED);
|
|
return true;
|
}
|
|
static void monitor_thread(void* data)
|
{
|
Il2CppInternalThread *current_thread = il2cpp::vm::Thread::CurrentInternal();
|
unsigned int i;
|
|
cpu_info_usage(g_ThreadPool->cpu_usage_state);
|
|
//mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, started", mono_native_thread_id_get ());
|
|
do
|
{
|
ThreadPoolCounter counter;
|
bool limit_worker_max_reached;
|
int32_t interval_left = MONITOR_INTERVAL;
|
int32_t awake = 0; /* number of spurious awakes we tolerate before doing a round of rebalancing */
|
|
IL2CPP_ASSERT(s_MonitorStatus != MONITOR_STATUS_NOT_RUNNING);
|
|
il2cpp::gc::GarbageCollector::SetSkipThread(true);
|
|
do
|
{
|
int64_t ts;
|
bool alerted = false;
|
|
if (il2cpp::vm::Runtime::IsShuttingDown())
|
break;
|
|
ts = il2cpp::os::Time::GetTicksMillisecondsMonotonic();
|
il2cpp::vm::Thread::Sleep(interval_left);
|
/*if (mono_thread_info_sleep (interval_left, &alerted) == 0)
|
break;*/
|
interval_left -= (int32_t)(il2cpp::os::Time::GetTicksMillisecondsMonotonic() - ts);
|
|
il2cpp::gc::GarbageCollector::SetSkipThread(false);
|
if ((current_thread->state & (il2cpp::vm::kThreadStateStopRequested | il2cpp::vm::kThreadStateSuspendRequested)) != 0)
|
mono_thread_interruption_checkpoint();
|
il2cpp::gc::GarbageCollector::SetSkipThread(true);
|
}
|
while (interval_left > 0 && ++awake < 10);
|
|
il2cpp::gc::GarbageCollector::SetSkipThread(false);
|
|
if (g_ThreadPool->suspended)
|
continue;
|
|
if (il2cpp::vm::Runtime::IsShuttingDown())
|
continue;
|
|
g_ThreadPool->domains_lock.Lock();
|
if (!domain_any_has_request())
|
{
|
g_ThreadPool->domains_lock.Unlock();
|
continue;
|
}
|
g_ThreadPool->domains_lock.Unlock();
|
|
g_ThreadPool->cpu_usage = cpu_info_usage(g_ThreadPool->cpu_usage_state);
|
|
if (!monitor_sufficient_delay_since_last_dequeue())
|
continue;
|
|
limit_worker_max_reached = false;
|
|
COUNTER_ATOMIC(counter,
|
{
|
if (counter._.max_working >= g_ThreadPool->limit_worker_max)
|
{
|
limit_worker_max_reached = true;
|
break;
|
}
|
counter._.max_working++;
|
});
|
|
if (limit_worker_max_reached)
|
continue;
|
|
hill_climbing_force_change(counter._.max_working, TRANSITION_STARVATION);
|
|
for (i = 0; i < 5; ++i)
|
{
|
if (il2cpp::vm::Runtime::IsShuttingDown())
|
break;
|
|
if (worker_try_unpark())
|
{
|
//mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, unparked", mono_native_thread_id_get ());
|
break;
|
}
|
|
if (worker_try_create())
|
{
|
//mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, created", mono_native_thread_id_get ());
|
break;
|
}
|
}
|
}
|
while (monitor_should_keep_running());
|
|
//mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_THREADPOOL, "[%p] monitor thread, finished", mono_native_thread_id_get ());
|
}
|
|
void monitor_ensure_running()
|
{
|
for (;;)
|
{
|
switch (s_MonitorStatus)
|
{
|
case MONITOR_STATUS_REQUESTED:
|
return;
|
case MONITOR_STATUS_WAITING_FOR_REQUEST:
|
il2cpp::vm::Atomic::CompareExchange(&s_MonitorStatus, MONITOR_STATUS_REQUESTED, MONITOR_STATUS_WAITING_FOR_REQUEST);
|
break;
|
case MONITOR_STATUS_NOT_RUNNING:
|
if (il2cpp::vm::Runtime::IsShuttingDown())
|
return;
|
if (il2cpp::vm::Atomic::CompareExchange(&s_MonitorStatus, MONITOR_STATUS_REQUESTED, MONITOR_STATUS_NOT_RUNNING) == MONITOR_STATUS_NOT_RUNNING)
|
{
|
if (!il2cpp::vm::Thread::CreateInternal(monitor_thread, NULL, true, SMALL_STACK))
|
s_MonitorStatus = MONITOR_STATUS_NOT_RUNNING;
|
|
return;
|
}
|
break;
|
default:
|
IL2CPP_ASSERT(0 && "should not be reached");
|
}
|
}
|
}
|