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 * 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); mono_perfcounter_update_value (tp->pc_nitems, TRUE, 1); 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 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; }
static void process_idle_times (ThreadPool *tp, gint64 t) { gint64 ticks; gint64 avg; gboolean compute_avg; gint new_threads; gint64 per1; if (tp->ignore_times || t <= 0) return; compute_avg = FALSE; ticks = mono_100ns_ticks (); t = ticks - t; SPIN_LOCK (tp->sp_lock); if (tp->ignore_times) { SPIN_UNLOCK (tp->sp_lock); return; } tp->time_sum += t; tp->n_sum++; if (tp->last_check == 0) tp->last_check = ticks; else if (tp->last_check > 0 && (ticks - tp->last_check) > 5000000) { tp->ignore_times = 1; compute_avg = TRUE; } SPIN_UNLOCK (tp->sp_lock); if (!compute_avg) return; //printf ("Items: %d Time elapsed: %.3fs\n", tp->n_sum, (ticks - tp->last_check) / 10000.0); tp->last_check = ticks; new_threads = 0; avg = tp->time_sum / tp->n_sum; if (tp->averages [1] == 0) { tp->averages [1] = avg; } else { per1 = ((100 * (ABS (avg - tp->averages [1]))) / tp->averages [1]); if (per1 > 5) { if (avg > tp->averages [1]) { if (tp->averages [1] < tp->averages [0]) { new_threads = -1; } else { new_threads = 1; } } else if (avg < tp->averages [1] && tp->averages [1] < tp->averages [0]) { new_threads = 1; } } else { int min, n; min = tp->min_threads; n = tp->nthreads; if ((n - min) < min && tp->busy_threads == n) new_threads = 1; } /* if (new_threads != 0) { printf ("n: %d per1: %lld avg=%lld avg1=%lld avg0=%lld\n", new_threads, per1, avg, tp->averages [1], tp->averages [0]); } */ } tp->time_sum = 0; tp->n_sum = 0; tp->averages [0] = tp->averages [1]; tp->averages [1] = avg; tp->ignore_times = 0; if (new_threads == -1) { if (tp->destroy_thread == 0 && InterlockedCompareExchange (&tp->destroy_thread, 1, 0) == 0) pulse_on_new_job (tp); } }
static void threadpool_kill_thread (ThreadPool *tp) { if (tp->destroy_thread == 0 && InterlockedCompareExchange (&tp->destroy_thread, 1, 0) == 0) pulse_on_new_job (tp); }