/* * actually, we might want to queue the finalize requests in a separate thread, * but we need to be careful about the execution domain of the thread... */ void mono_gc_run_finalize (void *obj, void *data) { MonoObject *exc = NULL; MonoObject *o; #ifndef HAVE_SGEN_GC MonoObject *o2; #endif MonoMethod* finalizer = NULL; MonoDomain *caller_domain = mono_domain_get (); MonoDomain *domain; RuntimeInvokeFunction runtime_invoke; o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); if (suspend_finalizers) return; domain = o->vtable->domain; #ifndef HAVE_SGEN_GC mono_domain_finalizers_lock (domain); o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o); mono_domain_finalizers_unlock (domain); if (!o2) /* Already finalized somehow */ return; #endif /* make sure the finalizer is not called again if the object is resurrected */ object_register_finalizer (obj, NULL); if (o->vtable->klass == mono_defaults.internal_thread_class) { MonoInternalThread *t = (MonoInternalThread*)o; if (mono_gc_is_finalizer_internal_thread (t)) /* Avoid finalizing ourselves */ return; if (t->threadpool_thread && finalizing_root_domain) { /* Don't finalize threadpool threads when shutting down - they're finalized when the threadpool shuts down. */ add_thread_to_finalize (t); return; } } if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) { /* * These can't be finalized during unloading/shutdown, since that would * free the native code which can still be referenced by other * finalizers. * FIXME: This is not perfect, objects dying at the same time as * dynamic methods can still reference them even when !shutdown. */ return; } if (mono_runtime_get_no_exec ()) return; /* speedup later... and use a timeout */ /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */ /* Use _internal here, since this thread can enter a doomed appdomain */ mono_domain_set_internal (mono_object_domain (o)); /* delegates that have a native function pointer allocated are * registered for finalization, but they don't have a Finalize * method, because in most cases it's not needed and it's just a waste. */ if (o->vtable->klass->delegate) { MonoDelegate* del = (MonoDelegate*)o; if (del->delegate_trampoline) mono_delegate_free_ftnptr ((MonoDelegate*)o); mono_domain_set_internal (caller_domain); return; } finalizer = mono_class_get_finalizer (o->vtable->klass); #ifndef DISABLE_COM /* If object has a CCW but has no finalizer, it was only * registered for finalization in order to free the CCW. * Else it needs the regular finalizer run. * FIXME: what to do about ressurection and suppression * of finalizer on object with CCW. */ if (mono_marshal_free_ccw (o) && !finalizer) { mono_domain_set_internal (caller_domain); return; } #endif /* * To avoid the locking plus the other overhead of mono_runtime_invoke (), * create and precompile a wrapper which calls the finalize method using * a CALLVIRT. */ if (!domain->finalize_runtime_invoke) { MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE); domain->finalize_runtime_invoke = mono_compile_method (invoke); } runtime_invoke = domain->finalize_runtime_invoke; mono_runtime_class_init (o->vtable); if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) { MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o), o->vtable->klass->name_space, o->vtable->klass->name); } runtime_invoke (o, NULL, &exc, NULL); if (exc) mono_internal_thread_unhandled_exception (exc); mono_domain_set_internal (caller_domain); }
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; }