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); }
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; } }
void ves_icall_System_GC_register_ephemeron_array (MonoObject *array) { #ifdef HAVE_SGEN_GC if (!mono_gc_ephemeron_array_add (array)) mono_raise_exception (mono_object_domain (array)->out_of_memory_ex); #endif }
static gboolean remove_sockstate_for_domain (gpointer key, gpointer value, gpointer user_data) { MonoMList *list = value; gboolean remove = FALSE; while (list) { MonoObject *data = mono_mlist_get_data (list); if (mono_object_domain (data) == user_data) { MonoClass *class = mono_object_get_class (data); remove = TRUE; mono_mlist_set_data (list, NULL); } list = mono_mlist_next (list); }
static gboolean remove_sockstate_for_domain (gpointer key, gpointer value, gpointer user_data) { MonoMList *list = value; gboolean remove = FALSE; while (list) { MonoObject *data = mono_mlist_get_data (list); if (mono_object_domain (data) == user_data) { remove = TRUE; mono_mlist_set_data (list, NULL); } list = mono_mlist_next (list); } //FIXME is there some sort of additional unregistration we need to perform here? return remove; }
static void filter_jobs_for_domain (gpointer key, gpointer value, gpointer user_data) { FilterSockaresForDomainData *data; MonoMList *list = value, *element; MonoDomain *domain; MonoGHashTable *states; g_assert (user_data); data = user_data; domain = data->domain; states = data->states; for (element = list; element; element = mono_mlist_next (element)) { MonoIOSelectorJob *job = (MonoIOSelectorJob*) mono_mlist_get_data (element); if (mono_object_domain (job) == domain) mono_mlist_set_data (element, NULL); } /* we skip all the first elements which are NULL */ for (; list; list = mono_mlist_next (list)) { if (mono_mlist_get_data (list)) break; } if (list) { g_assert (mono_mlist_get_data (list)); /* we delete all the NULL elements after the first one */ for (element = list; element;) { MonoMList *next; if (!(next = mono_mlist_next (element))) break; if (mono_mlist_get_data (next)) element = next; else mono_mlist_set_next (element, mono_mlist_next (next)); } } mono_g_hash_table_replace (states, key, list); }
MonoObject * mono_thread_pool_finish (MonoAsyncResult *ares, MonoArray **out_args, MonoObject **exc) { ASyncCall *ac; HANDLE wait_event; *exc = NULL; *out_args = NULL; /* check if already finished */ mono_monitor_enter ((MonoObject *) ares); if (ares->endinvoke_called) { *exc = (MonoObject *) mono_get_exception_invalid_operation (NULL); mono_monitor_exit ((MonoObject *) ares); return NULL; } ares->endinvoke_called = 1; /* wait until we are really finished */ if (!ares->completed) { if (ares->handle == NULL) { wait_event = CreateEvent (NULL, TRUE, FALSE, NULL); g_assert(wait_event != 0); MONO_OBJECT_SETREF (ares, handle, (MonoObject *) mono_wait_handle_new (mono_object_domain (ares), wait_event)); } else { wait_event = mono_wait_handle_get_handle ((MonoWaitHandle *) ares->handle); } mono_monitor_exit ((MonoObject *) ares); WaitForSingleObjectEx (wait_event, INFINITE, TRUE); } else { mono_monitor_exit ((MonoObject *) ares); } ac = (ASyncCall *) ares->object_data; g_assert (ac != NULL); *exc = ac->msg->exc; /* FIXME: GC add write barrier */ *out_args = ac->out_args; return ac->res; }
/* LOCKING: requires that the GC lock is held */ static int finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size, FinalizeEntryHashTable *hash_table) { FinalizeEntry **finalizable_hash = hash_table->table; mword finalizable_hash_size = hash_table->size; FinalizeEntry *entry, *prev; int i, count; if (no_finalize || !out_size || !out_array) return 0; count = 0; for (i = 0; i < finalizable_hash_size; ++i) { prev = NULL; for (entry = finalizable_hash [i]; entry;) { if (mono_object_domain (entry->object) == domain) { FinalizeEntry *next; /* remove and put in out_array */ if (prev) prev->next = entry->next; else finalizable_hash [i] = entry->next; next = entry->next; hash_table->num_registered--; out_array [count ++] = entry->object; DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, hash_table->num_registered)); entry = next; if (count == out_size) return count; continue; } prev = entry; entry = entry->next; } } return count; }
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; }
/* * 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; GSList *l, *refs = NULL; 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); refs = mono_gc_remove_weak_track_object (domain, o); mono_domain_finalizers_unlock (domain); if (!o2) /* Already finalized somehow */ return; #endif if (refs) { /* * Support for GCHandles of type WeakTrackResurrection: * * Its not exactly clear how these are supposed to work, or how their * semantics can be implemented. We only implement one crucial thing: * these handles are only cleared after the finalizer has ran. */ for (l = refs; l; l = l->next) { guint32 gchandle = GPOINTER_TO_UINT (l->data); mono_gchandle_set_target (gchandle, o); } g_slist_free (refs); } /* 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); runtime_invoke (o, NULL, &exc, NULL); if (exc) { /* fixme: do something useful */ } mono_domain_set_internal (caller_domain); }
/* * 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) { MonoError error; 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; // This function is called from the innards of the GC, so our best alternative for now is to do polling here mono_threads_safepoint (); o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data)); if (mono_do_not_finalize) { if (!mono_do_not_finalize_class_names) return; size_t namespace_len = strlen (o->vtable->klass->name_space); for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) { const char *name = mono_do_not_finalize_class_names [i]; if (strncmp (name, o->vtable->klass->name_space, namespace_len)) break; if (name [namespace_len] != '.') break; if (strcmp (name + namespace_len + 1, o->vtable->klass->name)) break; return; } } if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o); if (suspend_finalizers) return; domain = o->vtable->domain; #ifndef HAVE_SGEN_GC mono_domain_finalizers_lock (domain); o2 = (MonoObject *)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 ((MonoObject *)obj, NULL); if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o); 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. */ if (!add_thread_to_finalize (t, &error)) goto unhandled_error; 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); /* 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; } /* * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (), * create and precompile a wrapper which calls the finalize method using * a CALLVIRT. */ if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o); 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_checked (invoke, &error); mono_error_assert_ok (&error); /* expect this not to fail */ } runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke; mono_runtime_class_init_full (o->vtable, &error); if (!is_ok (&error)) goto unhandled_error; 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); } if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o); runtime_invoke (o, NULL, &exc, NULL); if (log_finalizers) g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o); unhandled_error: if (!is_ok (&error)) exc = (MonoObject*)mono_error_convert_to_exception (&error); if (exc) mono_thread_internal_unhandled_exception (exc); mono_domain_set_internal (caller_domain); }