123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- #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::os::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.Acquire();
- if (!domain_any_has_request())
- should_keep_running = false;
- g_ThreadPool->domains_lock.Release();
- 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::os::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.Acquire();
- if (!domain_any_has_request())
- {
- g_ThreadPool->domains_lock.Release();
- continue;
- }
- g_ThreadPool->domains_lock.Release();
- 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::os::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::os::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");
- }
- }
- }
|