From d6582ae8bd3e5ffe526407c2b2d6fe0b572c28f0 Mon Sep 17 00:00:00 2001 From: Vincent Povirk Date: Mon, 21 Feb 2011 14:22:02 -0600 Subject: [PATCH] mscoree: Overhaul of mono runtime shutdown process. It turns out that we can't free individual domains because that tears down important data structures. Instead we must shut down the entire runtime, but only if Mono doesn't shut itself down first. And we need to do it before DLL_PROCESS_DETACH because important libraries might be gone by then. --- dlls/mscoree/corruntimehost.c | 10 +++-- dlls/mscoree/metahost.c | 68 +++++++++++++++++++++++++++++++++- dlls/mscoree/mscoree_main.c | 5 ++- dlls/mscoree/mscoree_private.h | 17 ++++++++- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c index 3c26340a82a..02547532730 100644 --- a/dlls/mscoree/corruntimehost.c +++ b/dlls/mscoree/corruntimehost.c @@ -44,7 +44,7 @@ struct RuntimeHost const struct ICorRuntimeHostVtbl *lpVtbl; const struct ICLRRuntimeHostVtbl *lpCLRHostVtbl; const CLRRuntimeInfo *version; - const loaded_mono *mono; + loaded_mono *mono; struct list domains; MonoDomain *default_domain; CRITICAL_SECTION lock; @@ -91,6 +91,8 @@ static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, MonoDomain **result) goto end; } + This->mono->is_started = TRUE; + list_add_tail(&This->domains, &entry->entry); MSCOREE_LockModule(); @@ -131,7 +133,6 @@ static void RuntimeHost_DeleteDomain(RuntimeHost *This, MonoDomain *domain) { if (entry->domain == domain) { - This->mono->mono_jit_cleanup(domain); list_remove(&entry->entry); if (This->default_domain == domain) This->default_domain = NULL; @@ -711,11 +712,13 @@ __int32 WINAPI _CorExeMain(void) HeapFree(GetProcessHeap(), 0, argv); + unload_all_runtimes(); + return exit_code; } HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, - const loaded_mono *loaded_mono, RuntimeHost** result) + loaded_mono *loaded_mono, RuntimeHost** result) { RuntimeHost *This; @@ -786,7 +789,6 @@ HRESULT RuntimeHost_Destroy(RuntimeHost *This) LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &This->domains, struct DomainEntry, entry) { - This->mono->mono_jit_cleanup(cursor->domain); list_remove(&cursor->entry); HeapFree(GetProcessHeap(), 0, cursor); } diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 2cafcb8f881..72215f03eb4 100644 --- a/dlls/mscoree/metahost.c +++ b/dlls/mscoree/metahost.c @@ -76,6 +76,8 @@ static BOOL find_mono_dll(LPCWSTR path, LPWSTR dll_path, int abi_version); static MonoAssembly* mono_assembly_search_hook_fn(MonoAssemblyName *aname, char **assemblies_path, void *user_data); +static void mono_shutdown_callback_fn(MonoProfiler *prof); + static void set_environment(LPCWSTR bin_path) { WCHAR path_env[MAX_PATH]; @@ -91,6 +93,10 @@ static void set_environment(LPCWSTR bin_path) SetEnvironmentVariableW(pathW, path_env); } +static void CDECL do_nothing(void) +{ +} + static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) { static const WCHAR bin[] = {'\\','b','i','n',0}; @@ -111,6 +117,13 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) *result = &loaded_monos[This->mono_abi_version-1]; + if ((*result)->is_shutdown) + { + ERR("Cannot load Mono after it has been shut down."); + *result = NULL; + return E_FAIL; + } + if (!(*result)->mono_handle) { strcpyW(mono_bin_path, This->mono_path); @@ -146,16 +159,17 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) LOAD_MONO_FUNCTION(mono_class_get_method_from_name); LOAD_MONO_FUNCTION(mono_domain_assembly_open); LOAD_MONO_FUNCTION(mono_install_assembly_preload_hook); - LOAD_MONO_FUNCTION(mono_jit_cleanup); LOAD_MONO_FUNCTION(mono_jit_exec); LOAD_MONO_FUNCTION(mono_jit_init); LOAD_MONO_FUNCTION(mono_jit_set_trace_options); LOAD_MONO_FUNCTION(mono_object_get_domain); LOAD_MONO_FUNCTION(mono_object_new); LOAD_MONO_FUNCTION(mono_object_unbox); + LOAD_MONO_FUNCTION(mono_profiler_install); LOAD_MONO_FUNCTION(mono_reflection_type_from_name); LOAD_MONO_FUNCTION(mono_runtime_invoke); LOAD_MONO_FUNCTION(mono_runtime_object_init); + LOAD_MONO_FUNCTION(mono_runtime_quit); LOAD_MONO_FUNCTION(mono_set_dirs); LOAD_MONO_FUNCTION(mono_stringify_assembly_name); @@ -175,6 +189,22 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result) #undef LOAD_MONO_FUNCTION +#define LOAD_OPT_VOID_MONO_FUNCTION(x) do { \ + (*result)->x = (void*)GetProcAddress((*result)->mono_handle, #x); \ + if (!(*result)->x) { \ + (*result)->x = do_nothing; \ + } \ +} while (0); + + LOAD_OPT_VOID_MONO_FUNCTION(mono_runtime_set_shutting_down); + LOAD_OPT_VOID_MONO_FUNCTION(mono_thread_pool_cleanup); + LOAD_OPT_VOID_MONO_FUNCTION(mono_thread_suspend_all_other_threads); + LOAD_OPT_VOID_MONO_FUNCTION(mono_threads_set_shutting_down); + +#undef LOAD_OPT_VOID_MONO_FUNCTION + + (*result)->mono_profiler_install((MonoProfiler*)*result, mono_shutdown_callback_fn); + (*result)->mono_set_dirs(mono_lib_path_a, mono_etc_path_a); (*result)->mono_config_parse(NULL); @@ -200,6 +230,13 @@ fail: return E_FAIL; } +static void mono_shutdown_callback_fn(MonoProfiler *prof) +{ + loaded_mono *mono = (loaded_mono*)prof; + + mono->is_shutdown = TRUE; +} + static HRESULT CLRRuntimeInfo_GetRuntimeHost(CLRRuntimeInfo *This, RuntimeHost **result) { HRESULT hr = S_OK; @@ -230,11 +267,40 @@ void unload_all_runtimes(void) { int i; + for (i=0; imono_handle && mono->is_started && !mono->is_shutdown) + { + /* Copied from Mono's ves_icall_System_Environment_Exit */ + mono->mono_threads_set_shutting_down(); + mono->mono_runtime_set_shutting_down(); + mono->mono_thread_pool_cleanup(); + mono->mono_thread_suspend_all_other_threads(); + mono->mono_runtime_quit(); + } + } + for (i=0; imono_handle && mono->is_started && !mono->is_shutdown) + { + ERR("Process exited with a Mono runtime loaded.\n"); + return; + } + } +} + static HRESULT WINAPI CLRRuntimeInfo_QueryInterface(ICLRRuntimeInfo* iface, REFIID riid, void **ppvObject) diff --git a/dlls/mscoree/mscoree_main.c b/dlls/mscoree/mscoree_main.c index a3e1ceccaf1..6901f7a6b75 100644 --- a/dlls/mscoree/mscoree_main.c +++ b/dlls/mscoree/mscoree_main.c @@ -124,7 +124,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) DisableThreadLibraryCalls(hinstDLL); break; case DLL_PROCESS_DETACH: - unload_all_runtimes(); + expect_no_runtimes(); break; } return TRUE; @@ -154,7 +154,8 @@ __int32 WINAPI _CorExeMain2(PBYTE ptrMemory, DWORD cntMemory, LPWSTR imageName, void WINAPI CorExitProcess(int exitCode) { - FIXME("(%x) stub\n", exitCode); + TRACE("(%x)\n", exitCode); + unload_all_runtimes(); ExitProcess(exitCode); } diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index 222b3caf8ba..16ae5d3825b 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -85,6 +85,7 @@ typedef struct _MonoImage MonoImage; typedef struct _MonoClass MonoClass; typedef struct _MonoObject MonoObject; typedef struct _MonoMethod MonoMethod; +typedef struct _MonoProfiler MonoProfiler; typedef enum { MONO_IMAGE_OK, @@ -95,11 +96,16 @@ typedef enum { typedef MonoAssembly* (*MonoAssemblyPreLoadFunc)(MonoAssemblyName *aname, char **assemblies_path, void *user_data); +typedef void (*MonoProfileFunc)(MonoProfiler *prof); + typedef struct loaded_mono { HMODULE mono_handle; HMODULE glib_handle; + BOOL is_started; + BOOL is_shutdown; + MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly); MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status); MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type); @@ -109,25 +115,32 @@ typedef struct loaded_mono MonoAssembly* (CDECL *mono_domain_assembly_open) (MonoDomain *domain, const char *name); void (CDECL *mono_free)(void *); void (CDECL *mono_install_assembly_preload_hook)(MonoAssemblyPreLoadFunc func, void *user_data); - void (CDECL *mono_jit_cleanup)(MonoDomain *domain); int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]); MonoDomain* (CDECL *mono_jit_init)(const char *file); int (CDECL *mono_jit_set_trace_options)(const char* options); MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj); MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass); void* (CDECL *mono_object_unbox)(MonoObject *obj); + void (CDECL *mono_profiler_install)(MonoProfiler *prof, MonoProfileFunc shutdown_callback); MonoType* (CDECL *mono_reflection_type_from_name)(char *name, MonoImage *image); MonoObject* (CDECL *mono_runtime_invoke)(MonoMethod *method, void *obj, void **params, MonoObject **exc); void (CDECL *mono_runtime_object_init)(MonoObject *this_obj); + void (CDECL *mono_runtime_quit)(void); + void (CDECL *mono_runtime_set_shutting_down)(void); void (CDECL *mono_set_dirs)(const char *assembly_dir, const char *config_dir); char* (CDECL *mono_stringify_assembly_name)(MonoAssemblyName *aname); + void (CDECL *mono_thread_pool_cleanup)(void); + void (CDECL *mono_thread_suspend_all_other_threads)(void); + void (CDECL *mono_threads_set_shutting_down)(void); } loaded_mono; /* loaded runtime interfaces */ extern void unload_all_runtimes(void); +extern void expect_no_runtimes(void); + extern HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version, - const loaded_mono *loaded_mono, RuntimeHost** result); + loaded_mono *loaded_mono, RuntimeHost** result); extern HRESULT RuntimeHost_GetInterface(RuntimeHost *This, REFCLSID clsid, REFIID riid, void **ppv);