static void monitor_thread (gpointer unused) { ThreadPool *pools [2]; MonoInternalThread *thread; guint32 ms; gboolean need_one; int i; pools [0] = &async_tp; pools [1] = &async_io_tp; thread = mono_thread_internal_current (); ves_icall_System_Threading_Thread_SetName_internal (thread, mono_string_new (mono_domain_get (), "Threadpool monitor")); while (1) { ms = 500; i = 10; //number of spurious awakes we tolerate before doing a round of rebalancing. do { guint32 ts; ts = mono_msec_ticks (); if (SleepEx (ms, TRUE) == 0) break; ms -= (mono_msec_ticks () - ts); if (mono_runtime_is_shutting_down ()) break; if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } while (ms > 0 && i--); if (mono_runtime_is_shutting_down ()) break; if (suspended) continue; for (i = 0; i < 2; i++) { ThreadPool *tp; tp = pools [i]; if (tp->waiting > 0) continue; need_one = (mono_cq_count (tp->queue) > 0); if (!need_one && !tp->is_io) { EnterCriticalSection (&wsqs_lock); for (i = 0; wsqs != NULL && i < wsqs->len; i++) { MonoWSQ *wsq; wsq = g_ptr_array_index (wsqs, i); if (mono_wsq_count (wsq) != 0) { need_one = TRUE; break; } } LeaveCriticalSection (&wsqs_lock); } if (need_one) threadpool_start_thread (tp); } } }
static void remove_wsq (MonoWSQ *wsq) { gpointer data; if (wsq == NULL) return; EnterCriticalSection (&wsqs_lock); if (wsqs == NULL) { LeaveCriticalSection (&wsqs_lock); return; } g_ptr_array_remove_fast (wsqs, wsq); data = NULL; /* * Only clean this up when shutting down, any other case will error out * if we're removing a queue that still has work items. */ if (mono_runtime_is_shutting_down ()) { while (mono_wsq_local_pop (&data)) { threadpool_jobs_dec (data); data = NULL; } } mono_wsq_destroy (wsq); LeaveCriticalSection (&wsqs_lock); }
static void cleanup (void) { /* we make the assumption along the code that we are * cleaning up only if the runtime is shutting down */ g_assert (mono_runtime_is_shutting_down ()); selector_thread_wakeup (); while (io_selector_running) g_usleep (1000); mono_mutex_destroy (&threadpool_io->updates_lock); mono_cond_destroy (&threadpool_io->updates_cond); threadpool_io->backend.cleanup (); #if !defined(HOST_WIN32) close (threadpool_io->wakeup_pipes [0]); close (threadpool_io->wakeup_pipes [1]); #else closesocket (threadpool_io->wakeup_pipes [0]); closesocket (threadpool_io->wakeup_pipes [1]); #endif g_assert (threadpool_io); g_free (threadpool_io); threadpool_io = NULL; g_assert (!threadpool_io); }
static void threadpool_clear_queue (ThreadPool *tp, MonoDomain *domain) { MonoObject *obj; MonoMList *other = NULL; MonoCQ *queue = tp->queue; if (!queue) return; while (mono_cq_dequeue (queue, &obj)) { if (obj == NULL) continue; if (obj->vtable->domain != domain) other = mono_mlist_prepend (other, obj); threadpool_jobs_dec (obj); } if (mono_runtime_is_shutting_down ()) return; while (other) { threadpool_append_job (tp, (MonoObject *) mono_mlist_get_data (other)); other = mono_mlist_next (other); } }
void ves_icall_System_IOSelector_Add (gpointer handle, MonoIOSelectorJob *job) { ThreadPoolIOUpdate *update; g_assert (handle >= 0); g_assert (job->operation == EVENT_IN ^ job->operation == EVENT_OUT); g_assert (job->callback); if (mono_runtime_is_shutting_down ()) return; if (mono_domain_is_unloading (mono_object_domain (job))) return; mono_lazy_initialize (&io_status, initialize); mono_mutex_lock (&threadpool_io->updates_lock); update = update_get_new (); update->type = UPDATE_ADD; update->data.add.fd = GPOINTER_TO_INT (handle); update->data.add.job = job; mono_memory_barrier (); /* Ensure this is safely published before we wake up the selector */ selector_thread_wakeup (); mono_mutex_unlock (&threadpool_io->updates_lock); }
static gboolean threadpool_start_thread (ThreadPool *tp) { gint n; guint32 stack_size; MonoInternalThread *thread; stack_size = (!tp->is_io) ? 0 : SMALL_STACK; while (!mono_runtime_is_shutting_down () && (n = tp->nthreads) < tp->max_threads) { if (InterlockedCompareExchange (&tp->nthreads, n + 1, n) == n) { #ifndef DISABLE_PERFCOUNTERS mono_perfcounter_update_value (tp->pc_nthreads, TRUE, 1); #endif if (tp->is_io) { thread = mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, stack_size); } else { mono_mutex_lock (&threads_lock); thread = mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, stack_size); g_assert (threads != NULL); g_ptr_array_add (threads, thread); mono_mutex_unlock (&threads_lock); } return TRUE; } } return FALSE; }
void mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain) { /* Called by the GC while clearing out objects of the given domain from the heap. */ /* If there are no handles-related bugs, there is nothing to do: if a * thread accessed objects from the domain it was aborted, so any * threads left alive cannot have any handles that point into the * unloading domain. However if there is a handle leak, the handle stack is not */ if (!stack) return; /* Root domain only unloaded when mono is shutting down, don't need to check anything */ if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ()) return; HandleChunk *cur = stack->bottom; HandleChunk *last = stack->top; if (!cur) return; while (cur) { for (int idx = 0; idx < cur->size; ++idx) { HandleChunkElem *elem = &cur->elems[idx]; if (!elem->o) continue; g_assert (mono_object_domain (elem->o) != domain); } if (cur == last) break; cur = cur->next; } }
static void try_steal (MonoWSQ *local_wsq, gpointer *data, gboolean retry) { int i; int ms; if (wsqs == NULL || data == NULL || *data != NULL) return; ms = 0; do { if (mono_runtime_is_shutting_down ()) return; EnterCriticalSection (&wsqs_lock); for (i = 0; wsqs != NULL && i < wsqs->len; i++) { MonoWSQ *wsq; wsq = wsqs->pdata [i]; if (wsq == local_wsq || mono_wsq_count (wsq) == 0) continue; mono_wsq_try_steal (wsqs->pdata [i], data, ms); if (*data != NULL) { LeaveCriticalSection (&wsqs_lock); return; } } LeaveCriticalSection (&wsqs_lock); ms += 10; } while (retry && ms < 11); }
static gboolean dequeue_or_steal (ThreadPool *tp, gpointer *data, MonoWSQ *local_wsq) { if (mono_runtime_is_shutting_down ()) return FALSE; mono_cq_dequeue (tp->queue, (MonoObject **) data); if (!tp->is_io && !*data) try_steal (local_wsq, data, FALSE); return (*data != NULL); }
static void wait_callback (gint fd, gint events, gpointer user_data) { if (mono_runtime_is_shutting_down ()) return; if (fd == threadpool_io->wakeup_pipes [0]) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: wke"); selector_thread_wakeup_drain_pipes (); } else { MonoGHashTable *states; MonoMList *list = NULL; gpointer k; gboolean remove_fd = FALSE; gint operations; g_assert (user_data); states = user_data; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: cal fd %3d, events = %2s | %2s | %3s", fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..", (events & EVENT_ERR) ? "ERR" : "..."); if (!mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) g_error ("wait_callback: fd %d not found in states table", fd); if (list && (events & EVENT_IN) != 0) { MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_IN); if (job) mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job); } if (list && (events & EVENT_OUT) != 0) { MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_OUT); if (job) mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job); } remove_fd = (events & EVENT_ERR) == EVENT_ERR; if (!remove_fd) { mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); operations = get_operations_for_jobs (list); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: res fd %3d, events = %2s | %2s | %2s", fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : ".."); threadpool_io->backend.register_fd (fd, operations, FALSE); } else { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: err fd %d", fd); mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); threadpool_io->backend.remove_fd (fd); } } }
static void cleanup (void) { /* we make the assumption along the code that we are * cleaning up only if the runtime is shutting down */ g_assert (mono_runtime_is_shutting_down ()); selector_thread_wakeup (); while (io_selector_running) mono_thread_info_usleep (1000); }
/* Called by msvcrt.dll when shutting down. */ void STDMETHODCALLTYPE CorExitProcess(int exitCode) { /* FIXME: This is not currently supported by the runtime. */ #if 0 if (mono_get_root_domain () && !mono_runtime_is_shutting_down ()) { mono_runtime_set_shutting_down (); mono_thread_suspend_all_other_threads (); mono_runtime_quit (); } #endif ExitProcess (exitCode); }
static void threadpool_append_jobs (ThreadPool *tp, MonoObject **jobs, gint njobs) { MonoObject *ar; gint i; if (mono_runtime_is_shutting_down ()) return; if (tp->pool_status == 0 && InterlockedCompareExchange (&tp->pool_status, 1, 0) == 0) { if (!tp->is_io) { monitor_internal_thread = mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, SMALL_STACK); monitor_internal_thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE; threadpool_start_thread (tp); } /* Create on demand up to min_threads to avoid startup penalty for apps that don't use * the threadpool that much */ if (mono_config_is_server_mode ()) { mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, tp, TRUE, SMALL_STACK); } } InterlockedAdd (&monitor_njobs, njobs); if (monitor_state == MONITOR_STATE_SLEEPING && InterlockedCompareExchange (&monitor_state, MONITOR_STATE_AWAKE, MONITOR_STATE_SLEEPING) == MONITOR_STATE_SLEEPING) MONO_SEM_POST (&monitor_sem); if (monitor_state == MONITOR_STATE_FALLING_ASLEEP) InterlockedCompareExchange (&monitor_state, MONITOR_STATE_AWAKE, MONITOR_STATE_FALLING_ASLEEP); for (i = 0; i < njobs; i++) { ar = jobs [i]; if (ar == NULL || mono_domain_is_unloading (ar->vtable->domain)) continue; /* Might happen when cleaning domain jobs */ threadpool_jobs_inc (ar); #ifndef DISABLE_PERFCOUNTERS mono_perfcounter_update_value (tp->pc_nitems, TRUE, 1); #endif if (!tp->is_io && mono_wsq_local_push (ar)) continue; mono_cq_enqueue (tp->queue, ar); } #if DEBUG InterlockedAdd (&tp->njobs, njobs); #endif for (i = 0; tp->waiting > 0 && i < MIN(njobs, tp->max_threads); i++) pulse_on_new_job (tp); }
static gboolean threadpool_start_thread (ThreadPool *tp) { gint n; guint32 stack_size; stack_size = (!tp->is_io) ? 0 : SMALL_STACK; while (!mono_runtime_is_shutting_down () && (n = tp->nthreads) < tp->max_threads) { if (InterlockedCompareExchange (&tp->nthreads, n + 1, n) == n) { mono_perfcounter_update_value (tp->pc_nthreads, TRUE, 1); mono_thread_create_internal (mono_get_root_domain (), tp->async_invoke, tp, TRUE, stack_size); return TRUE; } } return FALSE; }
static void worker_thread (gpointer data) { static MonoClass *threadpool_wait_callback_class = NULL; static MonoMethod *perform_wait_callback_method = NULL; MonoInternalThread *thread; ThreadPoolDomain *tpdomain; ThreadPoolCounter counter; gboolean retire = FALSE; g_assert (status >= STATUS_INITIALIZED); tpdomain = data; g_assert (tpdomain); g_assert (tpdomain->domain); if (mono_runtime_is_shutting_down () || mono_domain_is_unloading (tpdomain->domain)) { COUNTER_ATOMIC (counter, { counter._.active --; });
static void threadpool_append_jobs (ThreadPool *tp, MonoObject **jobs, gint njobs) { static int job_counter; MonoObject *ar; gint i; if (mono_runtime_is_shutting_down ()) return; if (tp->pool_status == 0 && InterlockedCompareExchange (&tp->pool_status, 1, 0) == 0) { if (!tp->is_io) { mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, SMALL_STACK); threadpool_start_thread (tp); } /* Create on demand up to min_threads to avoid startup penalty for apps that don't use * the threadpool that much */ if (mono_config_is_server_mode ()) { mono_thread_create_internal (mono_get_root_domain (), threadpool_start_idle_threads, tp, TRUE, SMALL_STACK); } } for (i = 0; i < njobs; i++) { ar = jobs [i]; if (ar == NULL || mono_domain_is_unloading (ar->vtable->domain)) continue; /* Might happen when cleaning domain jobs */ if (!tp->is_io && (InterlockedIncrement (&job_counter) % 10) == 0) { MonoAsyncResult *o = (MonoAsyncResult *) ar; o->add_time = mono_100ns_ticks (); } threadpool_jobs_inc (ar); #ifndef DISABLE_PERFCOUNTERS mono_perfcounter_update_value (tp->pc_nitems, TRUE, 1); #endif if (!tp->is_io && mono_wsq_local_push (ar)) continue; mono_cq_enqueue (tp->queue, ar); } for (i = 0; tp->waiting > 0 && i < MIN(njobs, tp->max_threads); i++) pulse_on_new_job (tp); }
static void socket_io_add (MonoAsyncResult *ares, MonoSocketAsyncResult *state) { MonoMList *list; SocketIOData *data = &socket_io_data; int fd; gboolean is_new; int ievt; socket_io_init (&socket_io_data); if (mono_runtime_is_shutting_down () || data->inited == 3 || data->sock_to_state == NULL) return; if (async_tp.pool_status == 2) return; MONO_OBJECT_SETREF (state, ares, ares); fd = GPOINTER_TO_INT (state->handle); EnterCriticalSection (&data->io_lock); if (data->sock_to_state == NULL) { LeaveCriticalSection (&data->io_lock); return; } list = mono_g_hash_table_lookup (data->sock_to_state, GINT_TO_POINTER (fd)); if (list == NULL) { list = mono_mlist_alloc ((MonoObject*)state); is_new = TRUE; } else { list = mono_mlist_append (list, (MonoObject*)state); is_new = FALSE; } mono_g_hash_table_replace (data->sock_to_state, state->handle, list); ievt = get_events_from_list (list); data->modify (data->event_data, fd, state->operation, ievt, is_new); LeaveCriticalSection (&data->io_lock); }
static void monitor_thread (gpointer unused) { ThreadPool *pools [2]; MonoInternalThread *thread; int i; guint32 ms; gint8 num_waiting_iterations = 0; gint16 history_size = 0, current = -1; SamplesHistory *history = malloc (sizeof (SamplesHistory) * HISTORY_SIZE); pools [0] = &async_tp; pools [1] = &async_io_tp; thread = mono_thread_internal_current (); ves_icall_System_Threading_Thread_SetName_internal (thread, mono_string_new (mono_domain_get (), "Threadpool monitor")); while (1) { ms = SAMPLES_PERIOD; i = 10; //number of spurious awakes we tolerate before doing a round of rebalancing. do { guint32 ts; ts = mono_msec_ticks (); if (SleepEx (ms, TRUE) == 0) break; ms -= (mono_msec_ticks () - ts); if (mono_runtime_is_shutting_down ()) break; if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } while (ms > 0 && i--); if (mono_runtime_is_shutting_down ()) break; if (suspended) continue; /* threadpool is cleaning up */ if (async_tp.pool_status == 2 || async_io_tp.pool_status == 2) break; switch (monitor_state) { case MONITOR_STATE_AWAKE: num_waiting_iterations = 0; break; case MONITOR_STATE_FALLING_ASLEEP: if (++num_waiting_iterations == NUM_WAITING_ITERATIONS) { if (monitor_state == MONITOR_STATE_FALLING_ASLEEP && InterlockedCompareExchange (&monitor_state, MONITOR_STATE_SLEEPING, MONITOR_STATE_FALLING_ASLEEP) == MONITOR_STATE_FALLING_ASLEEP) { MONO_SEM_WAIT (&monitor_sem); num_waiting_iterations = 0; current = -1; history_size = 0; } } break; case MONITOR_STATE_SLEEPING: g_assert_not_reached (); } for (i = 0; i < 2; i++) { ThreadPool *tp; tp = pools [i]; if (tp->is_io) { if (!tp->waiting && mono_cq_count (tp->queue) > 0) threadpool_start_thread (tp); } else { gint8 nthreads_diff = monitor_heuristic (¤t, &history_size, history, tp); if (nthreads_diff == 1) threadpool_start_thread (tp); else if (nthreads_diff == -1) threadpool_kill_thread (tp); } } } }
static void selector_thread (gpointer data) { MonoGHashTable *states; io_selector_running = TRUE; if (mono_runtime_is_shutting_down ()) { io_selector_running = FALSE; return; } states = mono_g_hash_table_new_type (g_direct_hash, g_direct_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREAD_POOL, "i/o thread pool states table"); for (;;) { gint i, j; gint res; mono_mutex_lock (&threadpool_io->updates_lock); for (i = 0; i < threadpool_io->updates_size; ++i) { ThreadPoolIOUpdate *update = &threadpool_io->updates [i]; switch (update->type) { case UPDATE_EMPTY: break; case UPDATE_ADD: { gint fd; gint operations; gpointer k; gboolean exists; MonoMList *list = NULL; MonoIOSelectorJob *job; fd = update->data.add.fd; g_assert (fd >= 0); job = update->data.add.job; g_assert (job); exists = mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list); list = mono_mlist_append (list, (MonoObject*) job); mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); operations = get_operations_for_jobs (list); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: %3s fd %3d, operations = %2s | %2s | %2s", exists ? "mod" : "add", fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : ".."); threadpool_io->backend.register_fd (fd, operations, !exists); break; } case UPDATE_REMOVE_SOCKET: { gint fd; gpointer k; MonoMList *list = NULL; fd = update->data.remove_socket.fd; g_assert (fd >= 0); if (mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) { mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); for (j = i + 1; j < threadpool_io->updates_size; ++j) { ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; if (update->type == UPDATE_ADD && update->data.add.fd == fd) memset (update, 0, sizeof (ThreadPoolIOUpdate)); } for (; list; list = mono_mlist_remove_item (list, list)) mono_threadpool_ms_enqueue_work_item (mono_object_domain (mono_mlist_get_data (list)), mono_mlist_get_data (list)); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: del fd %3d", fd); threadpool_io->backend.remove_fd (fd); } break; } case UPDATE_REMOVE_DOMAIN: { MonoDomain *domain; domain = update->data.remove_domain.domain; g_assert (domain); FilterSockaresForDomainData user_data = { .domain = domain, .states = states }; mono_g_hash_table_foreach (states, filter_jobs_for_domain, &user_data); for (j = i + 1; j < threadpool_io->updates_size; ++j) { ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; if (update->type == UPDATE_ADD && mono_object_domain (update->data.add.job) == domain) memset (update, 0, sizeof (ThreadPoolIOUpdate)); } break; } default: g_assert_not_reached (); } } mono_cond_broadcast (&threadpool_io->updates_cond); if (threadpool_io->updates_size > 0) { threadpool_io->updates_size = 0; memset (&threadpool_io->updates, 0, UPDATES_CAPACITY * sizeof (ThreadPoolIOUpdate)); } mono_mutex_unlock (&threadpool_io->updates_lock); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: wai"); res = threadpool_io->backend.event_wait (wait_callback, states); if (res == -1 || mono_runtime_is_shutting_down ()) break; } mono_g_hash_table_destroy (states); io_selector_running = FALSE; }
static void ensure_cleanedup (void) { if (status == STATUS_NOT_INITIALIZED && InterlockedCompareExchange (&status, STATUS_CLEANED_UP, STATUS_NOT_INITIALIZED) == STATUS_NOT_INITIALIZED) return; if (status == STATUS_INITIALIZING) { while (status == STATUS_INITIALIZING) mono_thread_info_yield (); } if (status == STATUS_CLEANED_UP) return; if (status == STATUS_CLEANING_UP || InterlockedCompareExchange (&status, STATUS_CLEANING_UP, STATUS_INITIALIZED) != STATUS_INITIALIZED) { while (status == STATUS_CLEANING_UP) mono_thread_info_yield (); g_assert (status == STATUS_CLEANED_UP); return; } /* we make the assumption along the code that we are * cleaning up only if the runtime is shutting down */ g_assert (mono_runtime_is_shutting_down ()); /* Unpark all worker threads */ mono_mutex_lock (&threadpool->parked_threads_lock); for (;;) { guint i; ThreadPoolCounter counter = COUNTER_READ (); if (counter._.active == 0 && counter._.parked == 0) break; if (counter._.active == 1) { MonoInternalThread *thread = mono_thread_internal_current (); if (thread->threadpool_thread) { /* if there is only one active thread * left and it's the current one */ break; } } for (i = 0; i < threadpool->parked_threads->len; ++i) { mono_cond_t *cond = (mono_cond_t*) g_ptr_array_index (threadpool->parked_threads, i); mono_cond_signal (cond); } mono_mutex_unlock (&threadpool->parked_threads_lock); usleep (1000); mono_mutex_lock (&threadpool->parked_threads_lock); } mono_mutex_unlock (&threadpool->parked_threads_lock); while (monitor_status != MONITOR_STATUS_NOT_RUNNING) usleep (1000); g_ptr_array_free (threadpool->domains, TRUE); mono_mutex_destroy (&threadpool->domains_lock); g_ptr_array_free (threadpool->parked_threads, TRUE); mono_mutex_destroy (&threadpool->parked_threads_lock); g_ptr_array_free (threadpool->working_threads, TRUE); mono_mutex_destroy (&threadpool->working_threads_lock); mono_mutex_destroy (&threadpool->heuristic_lock); g_free (threadpool->heuristic_hill_climbing.samples); g_free (threadpool->heuristic_hill_climbing.thread_counts); rand_free (threadpool->heuristic_hill_climbing.random_interval_generator); g_free (threadpool->cpu_usage_state); g_assert (threadpool); g_free (threadpool); threadpool = NULL; g_assert (!threadpool); status = STATUS_CLEANED_UP; }
static void async_invoke_thread (gpointer data) { MonoDomain *domain; MonoInternalThread *thread; MonoWSQ *wsq; ThreadPool *tp; gboolean must_die; const gchar *name; tp = data; wsq = NULL; if (!tp->is_io) wsq = add_wsq (); thread = mono_thread_internal_current (); mono_profiler_thread_start (thread->tid); name = (tp->is_io) ? "IO Threadpool worker" : "Threadpool worker"; mono_thread_set_name_internal (thread, mono_string_new (mono_domain_get (), name), FALSE); if (tp_start_func) tp_start_func (tp_hooks_user_data); data = NULL; for (;;) { MonoAsyncResult *ar; MonoClass *klass; gboolean is_io_task; gboolean is_socket; int n_naps = 0; is_io_task = FALSE; ar = (MonoAsyncResult *) data; if (ar) { InterlockedIncrement (&tp->busy_threads); domain = ((MonoObject *)ar)->vtable->domain; #ifndef DISABLE_SOCKETS klass = ((MonoObject *) data)->vtable->klass; is_io_task = !is_corlib_asyncresult (domain, klass); is_socket = FALSE; if (is_io_task) { MonoSocketAsyncResult *state = (MonoSocketAsyncResult *) data; is_socket = is_socketasyncresult (domain, klass); ar = state->ares; switch (state->operation) { case AIO_OP_RECEIVE: state->total = ICALL_RECV (state); break; case AIO_OP_SEND: state->total = ICALL_SEND (state); break; } } #endif /* worker threads invokes methods in different domains, * so we need to set the right domain here */ g_assert (domain); if (mono_domain_is_unloading (domain) || mono_runtime_is_shutting_down ()) { threadpool_jobs_dec ((MonoObject *)ar); data = NULL; ar = NULL; InterlockedDecrement (&tp->busy_threads); } else { mono_thread_push_appdomain_ref (domain); if (threadpool_jobs_dec ((MonoObject *)ar)) { data = NULL; ar = NULL; mono_thread_pop_appdomain_ref (); InterlockedDecrement (&tp->busy_threads); continue; } if (mono_domain_set (domain, FALSE)) { MonoObject *exc; if (tp_item_begin_func) tp_item_begin_func (tp_item_user_data); if (!is_io_task && ar->add_time > 0) process_idle_times (tp, ar->add_time); exc = mono_async_invoke (tp, ar); if (tp_item_end_func) tp_item_end_func (tp_item_user_data); if (exc) mono_internal_thread_unhandled_exception (exc); if (is_socket && tp->is_io) { MonoSocketAsyncResult *state = (MonoSocketAsyncResult *) data; if (state->completed && state->callback) { MonoAsyncResult *cb_ares; cb_ares = create_simple_asyncresult ((MonoObject *) state->callback, (MonoObject *) state); icall_append_job ((MonoObject *) cb_ares); } } mono_domain_set (mono_get_root_domain (), TRUE); } mono_thread_pop_appdomain_ref (); InterlockedDecrement (&tp->busy_threads); /* If the callee changes the background status, set it back to TRUE */ mono_thread_clr_state (thread , ~ThreadState_Background); if (!mono_thread_test_state (thread , ThreadState_Background)) ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); } } ar = NULL; data = NULL; must_die = should_i_die (tp); if (!must_die && (tp->is_io || !mono_wsq_local_pop (&data))) dequeue_or_steal (tp, &data, wsq); n_naps = 0; while (!must_die && !data && n_naps < 4) { gboolean res; InterlockedIncrement (&tp->waiting); // Another thread may have added a job into its wsq since the last call to dequeue_or_steal // Check all the queues again before entering the wait loop dequeue_or_steal (tp, &data, wsq); if (data) { InterlockedDecrement (&tp->waiting); break; } mono_gc_set_skip_thread (TRUE); #if defined(__OpenBSD__) while (mono_cq_count (tp->queue) == 0 && (res = mono_sem_wait (&tp->new_job, TRUE)) == -1) {// && errno == EINTR) { #else while (mono_cq_count (tp->queue) == 0 && (res = mono_sem_timedwait (&tp->new_job, 2000, TRUE)) == -1) {// && errno == EINTR) { #endif if (mono_runtime_is_shutting_down ()) break; if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } InterlockedDecrement (&tp->waiting); mono_gc_set_skip_thread (FALSE); if (mono_runtime_is_shutting_down ()) break; must_die = should_i_die (tp); dequeue_or_steal (tp, &data, wsq); n_naps++; } if (!data && !tp->is_io && !mono_runtime_is_shutting_down ()) { mono_wsq_local_pop (&data); if (data && must_die) { InterlockedCompareExchange (&tp->destroy_thread, 1, 0); pulse_on_new_job (tp); } } if (!data) { gint nt; gboolean down; while (1) { nt = tp->nthreads; down = mono_runtime_is_shutting_down (); if (!down && nt <= tp->min_threads) break; if (down || InterlockedCompareExchange (&tp->nthreads, nt - 1, nt) == nt) { mono_perfcounter_update_value (tp->pc_nthreads, TRUE, -1); if (!tp->is_io) { remove_wsq (wsq); } mono_profiler_thread_end (thread->tid); if (tp_finish_func) tp_finish_func (tp_hooks_user_data); return; } } } } g_assert_not_reached (); } void ves_icall_System_Threading_ThreadPool_GetAvailableThreads (gint *workerThreads, gint *completionPortThreads) { *workerThreads = async_tp.max_threads - async_tp.busy_threads; *completionPortThreads = async_io_tp.max_threads - async_io_tp.busy_threads; }
bool _GodotSharp::is_runtime_shutting_down() { return mono_runtime_is_shutting_down(); }