2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / deployment.cpp
blob0913427bf8e47761a40ccd4da47b810cc19c6582
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * deployment.cpp: Deployment Class support
5 * Copyright 2008 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
8 */
10 #include <config.h>
12 #include <glib.h>
14 #include "downloader.h"
15 #include "deployment.h"
16 #include "timemanager.h"
17 #include "debug.h"
18 #include "utils.h"
19 #include "security.h"
20 #include "namescope.h"
21 #include "pipeline.h"
23 #include <stdlib.h>
24 #include <mono/jit/jit.h>
25 #include <mono/metadata/debug-helpers.h>
26 G_BEGIN_DECLS
27 /* because this header sucks */
28 #include <mono/metadata/mono-debug.h>
29 G_END_DECLS
30 #include <mono/metadata/mono-config.h>
31 #include <mono/metadata/mono-gc.h>
32 #include <mono/metadata/threads.h>
33 #include <mono/metadata/profiler.h>
35 #include <mono/metadata/appdomain.h>
38 * Deployment
41 gboolean Deployment::initialized = FALSE;
42 pthread_key_t Deployment::tls_key = 0;
43 pthread_mutex_t Deployment::hash_mutex;
44 GHashTable* Deployment::current_hash = NULL;
45 MonoDomain* Deployment::root_domain = NULL;
46 Deployment *Deployment::desktop_deployment = NULL;
47 gint32 Deployment::deployment_count = 0;
49 class IDownloaderNode : public List::Node {
50 public:
51 IDownloader *dl;
52 IDownloaderNode (IDownloader *dl)
54 this->dl = dl;
56 virtual ~IDownloaderNode ()
58 if (dl && !dl->IsAborted ())
59 dl->Abort ();
64 class StringNode : public List::Node {
65 public:
66 char *str;
68 StringNode (char *str) {
69 this->str = g_strdup (str);
73 bool
74 find_string (List::Node *node, void *data)
76 StringNode *tp = (StringNode*)node;
77 char *p = (char*)data;
79 return !strcmp (tp->str, p);
82 bool
83 Deployment::Initialize (const char *platform_dir, bool create_root_domain)
85 if (initialized)
86 return true;
88 initialized = true;
90 current_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
91 pthread_key_create (&tls_key, NULL);
92 pthread_mutex_init (&hash_mutex, NULL);
94 enable_vm_stack_trace ();
96 #if MONO_ENABLE_APP_DOMAIN_CONTROL
97 if (create_root_domain) {
98 const gchar *trace_options;
99 const gchar *moon_path;
100 const gchar *profiler;
101 #if DEBUG
102 const gchar *soft_debug;
103 #endif
105 #if DEBUG && SANITY
106 // Install signal handlers for crash reporting
107 // Note that this only works if mono hasn't been
108 // initialized yet (i.e. this must not be done
109 // for mopen, etc).
110 moonlight_install_signal_handlers ();
111 #endif
113 #if DEBUG
114 printf ("Moonlight: Enabling MONO_DEBUG=keep-delegates.\n");
115 g_setenv ("MONO_DEBUG", "keep-delegates", false);
116 #endif
118 mono_config_parse (NULL);
120 /* if a platform directory is provided then we're running inside the browser and CoreCLR should be enabled */
121 if (platform_dir) {
122 security_enable_coreclr (platform_dir);
124 /* XXX confine mono itself to the platform directory XXX incomplete */
125 g_setenv ("MONO_PATH", platform_dir, true);
126 g_unsetenv ("MONO_GAC_PREFIX");
127 } else {
128 moon_path = g_getenv ("MOON_PATH");
129 if (moon_path != NULL && moon_path [0] != 0) {
130 printf ("Setting moonlight root directory to: %s\n", moon_path);
131 mono_assembly_setrootdir (moon_path);
135 trace_options = g_getenv ("MOON_TRACE");
136 if (trace_options != NULL){
137 printf ("Setting trace options to: %s\n", trace_options);
138 mono_jit_set_trace_options (trace_options);
141 profiler = g_getenv ("MOON_PROFILER");
142 if (profiler != NULL) {
143 printf ("Setting profiler to: %s\n", profiler);
144 mono_profiler_load (profiler);
147 mono_set_signal_chaining (true);
149 #if DEBUG
150 soft_debug = g_getenv ("MOON_SOFT_DEBUG");
151 if (soft_debug != NULL) {
152 gchar *opt = g_strdup_printf ("--debugger-agent=%s", soft_debug);
153 mono_jit_parse_options (1, &opt);
155 g_free (opt);
157 #endif
158 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
160 root_domain = mono_jit_init_version ("Moonlight Root Domain", "moonlight");
162 LOG_DEPLOYMENT ("Deployment::Initialize (): Root domain is %p\n", root_domain);
164 else {
165 #endif
166 root_domain = mono_domain_get ();
168 Deployment::desktop_deployment = new Deployment (root_domain);
169 Deployment::SetCurrent (Deployment::desktop_deployment);
171 Application *desktop_app = new Application ();
172 desktop_deployment->SetCurrentApplication (desktop_app);
173 #if MONO_ENABLE_APP_DOMAIN_CONTROL
175 #endif
177 return true;
180 void
181 Deployment::RegisterThread (Deployment *deployment)
183 LOG_DEPLOYMENT ("Deployment::RegisterThread (): Deployment: %p Domain: %p\n", deployment, deployment->domain);
184 mono_thread_attach (deployment->domain);
187 Deployment*
188 Deployment::GetCurrent()
190 Deployment *deployment = (Deployment *) pthread_getspecific (tls_key);
191 MonoDomain *current_domain = mono_domain_get ();
194 * If we dont have a Deployment* in the TLS slot then we are in a thread created
195 * by mono. In this case we look up in the hsah table the deployment against
196 * the current appdomain
198 if (deployment == NULL && current_domain != NULL) {
199 if (current_domain != NULL) {
200 pthread_mutex_lock (&hash_mutex);
201 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
202 pthread_mutex_unlock (&hash_mutex);
203 pthread_setspecific (tls_key, deployment);
204 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Couldn't find deployment in our tls, searched current domain %p and found: %p\n", current_domain, deployment);
209 * If we have a domain mismatch, we likely got here from managed land and need
210 * to get the deployment tied to this domain
212 if (deployment) {
213 bool mismatch;
214 if (current_domain == NULL) {
215 /* this may happen for threads which are not registered with managed code (audio threads for instance). Everything ok. */
216 mismatch = false;
217 } else if (current_domain == root_domain) {
218 if (deployment->domain == NULL) {
219 /* we're in a deployment whose domain has been unloaded (but we're in the right deployment) */
220 mismatch = false;
221 } else {
222 /* something is very wrong, I can't see how this can happen */
223 //g_warning ("Deployment::GetCurrent (): Domain mismatch, but the current domain is the root domain?\n");
224 mismatch = false;
226 } else {
227 if (deployment->domain == NULL) {
228 /* we switched from a deployment whose domain has been unloaded to a normal deployment */
229 mismatch = true;
230 } else if (deployment->domain != current_domain) {
231 /* we're in the wrong deployment: our tls entry is wrong, most likely because we got here on a managed thread */
232 mismatch = true;
233 } else {
234 /* everything ok */
235 mismatch = false;
239 if (mismatch) {
240 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Domain mismatch, thread %u, (tls) deployment: %p, deployment->domain: %p, (mono_domain_get) current_domain: %p, root_domain: %p, hash deployment: %p\n",
241 (int) pthread_self (), deployment, deployment->domain, current_domain, root_domain, g_hash_table_lookup (current_hash, current_domain));
242 pthread_mutex_lock (&hash_mutex);
243 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
244 pthread_mutex_unlock (&hash_mutex);
246 /* Fixup our tls entry */
247 if (deployment != NULL) {
248 pthread_setspecific (tls_key, deployment);
253 if (deployment == NULL) {
254 // Currently this happens because we end up here during libmoon initialization.
255 // The fix is to not create objects as default values for our static dependency properties.
256 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Didn't find a deployment. This should never happen.\n");
259 return deployment;
262 void
263 Deployment::SetCurrent (Deployment* deployment)
265 SetCurrent (deployment, true);
268 void
269 Deployment::SetCurrent (Deployment* deployment, bool domain)
271 #if DEBUG
272 if (deployment && mono_domain_get () != deployment->domain) {
273 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i domain mismatch, is: %p\n", deployment, (int) pthread_self (), mono_domain_get ());
274 } else if (pthread_getspecific (tls_key) != deployment) {
275 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i deployment mismatch, is: %p\n", deployment, (int) pthread_self (), pthread_getspecific (tls_key));
277 #endif
279 if (domain) {
280 if (deployment != NULL && deployment->domain != NULL) {
281 mono_domain_set (deployment->domain, TRUE);
282 } else {
283 mono_domain_set (root_domain, TRUE);
286 pthread_setspecific (tls_key, deployment);
289 Deployment::Deployment (MonoDomain *domain)
290 : DependencyObject (this, Type::DEPLOYMENT)
292 this->domain = domain;
293 InnerConstructor ();
296 Deployment::Deployment()
297 : DependencyObject (this, Type::DEPLOYMENT)
299 MonoDomain *current = mono_domain_get ();
300 #if MONO_ENABLE_APP_DOMAIN_CONTROL
301 mono_domain_set (root_domain, FALSE);
302 domain = mono_domain_create_appdomain ((char *) "Silverlight AppDomain", NULL);
304 LOG_DEPLOYMENT ("Deployment::Deployment (): Created domain %p for deployment %p\n", domain, this);
306 mono_domain_set (domain, FALSE);
307 #else
308 domain = NULL;
309 #endif
310 InnerConstructor ();
312 mono_domain_set (current, FALSE);
315 void
316 Deployment::InnerConstructor ()
318 system_windows_image = NULL;
319 system_windows_assembly = NULL;
321 moon_load_xaml = NULL;
322 moon_initialize_deployment_xap = NULL;
323 moon_initialize_deployment_xaml = NULL;
324 moon_destroy_application = NULL;
325 moon_exception = NULL;
326 moon_exception_message = NULL;
327 moon_exception_error_code = NULL;
329 medias = NULL;
330 is_shutting_down = false;
331 deployment_count++;
332 appdomain_unloaded = false;
333 system_windows_assembly = NULL;
334 system_windows_deployment = NULL;
335 deployment_shutdown = NULL;
336 shutdown_state = Running; /* 0 */
337 is_loaded_from_xap = false;
338 xap_location = NULL;
339 current_app = NULL;
340 pending_unrefs = NULL;
341 pending_loaded = false;
342 objects_created = 0;
343 objects_destroyed = 0;
345 types = NULL;
347 #if OBJECT_TRACKING
348 objects_alive = NULL;
349 pthread_mutex_init (&objects_alive_mutex, NULL);
350 #endif
352 pthread_setspecific (tls_key, this);
354 pthread_mutex_lock (&hash_mutex);
355 g_hash_table_insert (current_hash, domain, this);
356 pthread_mutex_unlock (&hash_mutex);
358 font_manager = new FontManager ();
359 types = new Types ();
360 types->Initialize ();
363 ErrorEventArgs *
364 Deployment::ManagedExceptionToErrorEventArgs (MonoObject *exc)
366 int errorCode = -1;
367 char* message = NULL;
369 if (mono_object_isinst (exc, mono_get_exception_class())) {
370 MonoObject *ret = mono_property_get_value (moon_exception_message, exc, NULL, NULL);
372 message = mono_string_to_utf8 ((MonoString*)ret);
374 if (mono_object_isinst (exc, moon_exception)) {
375 MonoObject *ret = mono_property_get_value (moon_exception_error_code, exc, NULL, NULL);
377 errorCode = *(int*) mono_object_unbox (ret);
380 // FIXME: we need to figure out what type of exception it is
381 // and map it to the right MoonError::ExceptionType enum
382 return new ErrorEventArgs (RuntimeError, MoonError (MoonError::EXCEPTION, errorCode, message));
385 gpointer
386 Deployment::CreateManagedXamlLoader (gpointer plugin_instance, XamlLoader* native_loader, const char *resourceBase, const char *file, const char *str)
388 MonoObject *loader;
389 MonoObject *exc = NULL;
391 if (moon_load_xaml == NULL)
392 return NULL;
394 void *params [6];
395 Surface *surface = GetSurface ();
397 Deployment::SetCurrent (this);
399 params [0] = &native_loader;
400 params [1] = &plugin_instance;
401 params [2] = &surface;
402 params [3] = resourceBase ? mono_string_new (mono_domain_get (), resourceBase) : NULL;
403 params [4] = file ? mono_string_new (mono_domain_get (), file) : NULL;
404 params [5] = str ? mono_string_new (mono_domain_get (), str) : NULL;
405 loader = mono_runtime_invoke (moon_load_xaml, NULL, params, &exc);
407 if (exc) {
408 surface->EmitError (ManagedExceptionToErrorEventArgs (exc));
409 return NULL;
412 return GUINT_TO_POINTER (mono_gchandle_new (loader, false));
415 void
416 Deployment::DestroyManagedXamlLoader (gpointer xaml_loader)
418 guint32 loader = GPOINTER_TO_UINT (xaml_loader);
419 if (loader)
420 mono_gchandle_free (loader);
423 void
424 Deployment::DestroyManagedApplication (gpointer plugin_instance)
426 if (moon_destroy_application == NULL)
427 return;
429 MonoObject *exc = NULL;
430 void *params [1];
431 params [0] = &plugin_instance;
433 Deployment::SetCurrent (this);
435 mono_runtime_invoke (moon_destroy_application, NULL, params, &exc);
437 if (exc)
438 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc));
441 bool
442 Deployment::InitializeAppDomain ()
444 bool result = false;
446 system_windows_assembly = mono_assembly_load_with_partial_name ("System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", NULL);
448 if (system_windows_assembly) {
449 MonoClass *app_launcher;
451 result = true;
453 system_windows_image = mono_assembly_get_image (system_windows_assembly);
455 LOG_DEPLOYMENT ("Assembly: %s\n", mono_image_get_filename (system_windows_image));
457 app_launcher = mono_class_from_name (system_windows_image, "Mono", "ApplicationLauncher");
458 if (!app_launcher) {
459 g_warning ("could not find ApplicationLauncher type");
460 return false;
463 moon_exception = mono_class_from_name (system_windows_image, "Mono", "MoonException");
464 if (!moon_exception) {
465 g_warning ("could not find MoonException type");
466 return false;
469 moon_load_xaml = MonoGetMethodFromName (app_launcher, "CreateXamlLoader", -1);
470 moon_initialize_deployment_xap = MonoGetMethodFromName (app_launcher, "InitializeDeployment", 4);
471 moon_initialize_deployment_xaml = MonoGetMethodFromName (app_launcher, "InitializeDeployment", 2);
472 moon_destroy_application = MonoGetMethodFromName (app_launcher, "DestroyApplication", -1);
474 if (moon_load_xaml == NULL || moon_initialize_deployment_xap == NULL || moon_initialize_deployment_xaml == NULL || moon_destroy_application == NULL) {
475 g_warning ("lookup for ApplicationLauncher methods failed");
476 result = false;
479 moon_exception_message = MonoGetPropertyFromName (mono_get_exception_class(), "Message");
480 moon_exception_error_code = MonoGetPropertyFromName (moon_exception, "ErrorCode");
482 if (moon_exception_message == NULL || moon_exception_error_code == NULL) {
483 g_warning ("lookup for MoonException properties failed");
484 result = false;
486 } else {
487 printf ("Moonlight: Plugin AppDomain Creation: could not find System.Windows.dll.\n");
490 printf ("Moonlight: Plugin AppDomain Creation: %s\n", result ? "OK" : "Failed");
492 return result;
495 bool
496 Deployment::InitializeManagedDeployment (gpointer plugin_instance, const char *file, const char *culture, const char *uiCulture)
498 if (moon_initialize_deployment_xap == NULL && moon_initialize_deployment_xaml)
499 return false;
501 void *params [4];
502 MonoObject *ret;
503 MonoObject *exc = NULL;
505 Deployment::SetCurrent (this);
507 if (file != NULL) {
508 params [0] = &plugin_instance;
509 params [1] = mono_string_new (mono_domain_get (), file);
510 params [2] = culture ? mono_string_new (mono_domain_get (), culture) : NULL;
511 params [3] = uiCulture ? mono_string_new (mono_domain_get (), uiCulture) : NULL;
512 ret = mono_runtime_invoke (moon_initialize_deployment_xap, NULL, params, &exc);
513 } else {
514 params [0] = culture ? mono_string_new (mono_domain_get (), culture) : NULL;
515 params [1] = uiCulture ? mono_string_new (mono_domain_get (), uiCulture) : NULL;
516 ret = mono_runtime_invoke (moon_initialize_deployment_xaml, NULL, params, &exc);
519 if (exc) {
520 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc));
521 return false;
524 return (bool) (*(MonoBoolean *) mono_object_unbox (ret));
527 MonoMethod *
528 Deployment::MonoGetMethodFromName (MonoClass *klass, const char *name, int narg)
530 MonoMethod *method;
531 method = mono_class_get_method_from_name (klass, name, narg);
533 if (!method)
534 printf ("Warning could not find method %s\n", name);
536 return method;
539 MonoProperty *
540 Deployment::MonoGetPropertyFromName (MonoClass *klass, const char *name)
542 MonoProperty *property;
543 property = mono_class_get_property_from_name (klass, name);
545 if (!property)
546 printf ("Warning could not find property %s\n", name);
548 return property;
551 #if OBJECT_TRACKING
552 static int
553 IdComparer (gconstpointer base1, gconstpointer base2)
555 int id1 = (*(EventObject **) base1)->GetId ();
556 int id2 = (*(EventObject **) base2)->GetId ();
558 int iddiff = id1 - id2;
560 if (iddiff == 0)
561 return 0;
562 else if (iddiff < 0)
563 return -1;
564 else
565 return 1;
568 static void
569 accumulate_last_n (gpointer key,
570 gpointer value,
571 gpointer user_data)
573 GPtrArray *last_n = (GPtrArray*)user_data;
575 g_ptr_array_insert_sorted (last_n, IdComparer, key);
577 #endif
579 Deployment::~Deployment()
581 g_free (xap_location);
583 delete font_manager;
585 LOG_DEPLOYMENT ("Deployment::~Deployment (): %p\n", this);
587 #if SANITY
588 if (pending_unrefs != NULL)
589 g_warning ("Deployment::~Deployment (): There are still pending unrefs.\n");
590 if (medias != NULL)
591 g_warning ("Deployment::~Deployment (): There are still medias waiting to get disposed.\n");
592 #endif
594 #if OBJECT_TRACKING
595 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
596 if (objects_created != objects_destroyed)
597 ReportLeaks ();
598 #elif DEBUG
599 if (objects_created != objects_destroyed) {
600 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
602 #endif
604 #if OBJECT_TRACKING
605 pthread_mutex_destroy (&objects_alive_mutex);
606 g_hash_table_destroy (objects_alive);
607 #endif
609 if (types != NULL) {
610 types->DeleteProperties ();
611 delete types;
612 types = NULL;
615 deployment_count--;
618 #if OBJECT_TRACKING
619 void
620 Deployment::ReportLeaks ()
622 printf ("Deployment leak report:\n");
623 if (objects_created == objects_destroyed) {
624 printf ("\tno leaked objects.\n");
625 } else {
626 printf ("\tObjects created: %i\n", objects_created);
627 printf ("\tObjects destroyed: %i\n", objects_destroyed);
628 printf ("\tDifference: %i (%.1f%%)\n", objects_created - objects_destroyed, (100.0 * objects_destroyed) / objects_created);
630 GPtrArray* last_n = g_ptr_array_new ();
632 pthread_mutex_lock (&objects_alive_mutex);
633 g_hash_table_foreach (objects_alive, accumulate_last_n, last_n);
634 pthread_mutex_unlock (&objects_alive_mutex);
636 uint counter = 10;
637 counter = MIN(counter, last_n->len);
638 if (counter) {
639 printf ("\tOldest %d objects alive:\n", counter);
640 for (uint i = 0; i < MIN (counter, last_n->len); i ++) {
641 EventObject* obj = (EventObject *) last_n->pdata [i];
642 printf ("\t\t%p\t%i = %s, refcount: %i\n", obj, obj->GetId (), obj->GetTypeName (), obj->GetRefCount ());
646 g_ptr_array_free (last_n, true);
649 #endif
651 void
652 Deployment::Reinitialize ()
654 downloaders.Clear (true);
655 AssemblyPartCollection * parts = new AssemblyPartCollection ();
656 SetParts (parts);
657 parts->unref ();
660 bool
661 Deployment::IsShuttingDown ()
663 VERIFY_MAIN_THREAD;
664 return is_shutting_down;
667 void
668 Deployment::Dispose ()
670 LOG_DEPLOYMENT ("Deployment::Dispose (): %p\n", this);
672 DependencyObject::Dispose ();
675 void
676 Deployment::Shutdown ()
678 LOG_DEPLOYMENT ("Deployment::Shutdown ()\n");
681 * Shutting down is a complicated process with quite a few pitfalls.
682 * The current process is as follows:
683 * - Abort all downloaders. Firefox has a habit of calling into our
684 * downloader callbacks in bad moments, aborting all downloaders
685 * will prevent this from happening.
686 * - Ensure nothing is executed on the media threadpool threads and
687 * audio threads.
688 * - Unload our appdomain. We still have code executing on separate
689 * threads (user code can have threads, and there is always the
690 * finalizer thread).
691 * - The browser plugin is freed (the plugin needs to go away after
692 * after the appdomain, since managed code has lots of pointers
693 * to the plugin instance).
694 * - By now everything should have gotten unreffed, and the final object
695 * to be deleted is the deployment (every other object references the
696 * deployment to ensure this).
699 is_shutting_down = true;
701 g_return_if_fail (!IsDisposed ());
703 Emit (ShuttingDownEvent);
705 AbortAllDownloaders ();
707 * Dispose all Media instances so that we can be sure nothing is executed
708 * on the media threadpool threads after this point.
709 * This will also stop all media from playing, so there should be no audio
710 * threads doing anything either (note that there might be both media
711 * threadpool threads and audio threads still alive, just not executing
712 * anything related to this deployment).
714 DisposeAllMedias ();
716 if (current_app != NULL) {
717 current_app->Dispose ();
718 current_app->unref ();
719 current_app = NULL;
722 StringNode *node;
723 while ((node = (StringNode *) paths.First ())) {
724 RemoveDir (node->str);
725 g_free (node->str);
726 paths.Remove (node);
729 if (GetParts ())
730 SetParts (NULL);
732 if (GetValue (NameScope::NameScopeProperty))
733 SetValue (NameScope::NameScopeProperty, NULL);
735 #if MONO_ENABLE_APP_DOMAIN_CONTROL
736 if (system_windows_assembly == NULL) {
737 /* this can happen if initialization fails, i.e. xap downloading fails for instance */
738 shutdown_state = DisposeDeployment; /* skip managed shutdown entirely, since managed code wasn't initialized */
739 } else {
740 shutdown_state = CallManagedShutdown;
742 this->ref (); /* timemanager is dead, so we need to add timeouts directly to glib */
743 g_timeout_add_full (G_PRIORITY_DEFAULT, 1, ShutdownManagedCallback, this, NULL);
744 #endif
746 if (types)
747 types->Dispose ();
750 #if MONO_ENABLE_APP_DOMAIN_CONTROL
751 gboolean
752 Deployment::ShutdownManagedCallback (gpointer user_data)
754 return ((Deployment *) user_data)->ShutdownManaged ();
757 gboolean
758 Deployment::ShutdownManaged ()
760 if (domain == root_domain) {
761 fprintf (stderr, "Moonlight: Can't unload the root domain!\n");
762 this->unref (); /* the ref taken in Shutdown */
763 return false;
766 VERIFY_MAIN_THREAD;
769 * Managed shutdown is complicated, with a few gotchas:
770 * - managed finalizers are run on a separate thread (multi-threaded issues)
771 * - after the appdomain has unloaded, we can't call into it anymore (for
772 * instance using function pointers into managed delegates).
774 * To do have a safe shutdown we have two different approaches:
776 * 1) Protect the function pointers in native code with mutex, both during
777 * callbacks and when setting them. This has the drawback of having a
778 * mutex locked during a potentially long time (the mutex is always
779 * locked while executing the callback), and the advantage that the
780 * callbacks can be executed from any thread and the cleanup can be done
781 * directly in the managed dtor.
782 * ExternalDemuxer uses this approach.
784 * 2) If the callbacks will only be executed on the main thread, we can
785 * avoid the native locks ensuring that everything related to the
786 * callbacks will be done on the main thread by doing the following:
787 * - During execution we keep a list in managed code of cleanup actions
788 * to execute upon shutdown. If a managed object is finalized during
789 * normal execution, it removes any applicable actions from the list.
790 * This list is protected with a lock, so it can be accessed from all
791 * threads (main thread + finalizer thread).
792 * - When shutdown is requested, we set a flag to disallow further
793 * additions to the list, and execute all the cleanup actions.
794 * There are two cases where the managed finalizer is executed:
795 * a) Normal execution, in this case the native object has one ref left
796 * (the one ToggleRef has), so it is guaranteed that nobody can call
797 * the callbacks anymore -> no cleanup is needed in the managed dtor.
798 * b) Shutdown, in this case the cleanup code has already been executed
799 * (by Deployment.Shutdown), which again means that no cleanup is
800 * needed in the managed dtor.
801 * This approach only works if the callbacks are only called on the main
802 * thread (since otherwise there is a race condition between calling the
803 * callbacks and cleaning them up). It also only works for ToggleReffed/
804 * refcounted objects.
805 * MultiScaleTileSource uses this approach.
808 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): shutdown_state: %i, appdomain: %p, deployment: %p\n", shutdown_state, domain, this);
810 Deployment::SetCurrent (this, true);
812 switch (shutdown_state) {
813 case Running: /* 0 */
814 /* this shouldn't happen */
815 case ShutdownFailed: /* -1 */ {
816 /* There has been an error during shutdown and we can't continue shutting down */
817 fprintf (stderr, "Moonlight: Shutdown aborted due to unexpected error(s)\n");
818 this->unref (); /* the ref taken in Shutdown */
819 return false;
821 case CallManagedShutdown: /* 1 */{
822 /* Call the managed System.Windows.Deployment:Shutdown method */
823 MonoObject *ret;
824 MonoObject *exc = NULL;
825 bool result;
827 if (system_windows_assembly == NULL) {
828 shutdown_state = ShutdownFailed;
829 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment's assembly.\n");
830 break;
833 if (system_windows_deployment == NULL) {
834 system_windows_deployment = mono_class_from_name (system_windows_image, "System.Windows", "Deployment");
835 if (system_windows_deployment == NULL) {
836 shutdown_state = ShutdownFailed;
837 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment class.\n");
838 break;
842 if (deployment_shutdown == NULL) {
843 deployment_shutdown = mono_class_get_method_from_name (system_windows_deployment, "Shutdown", 0);
844 if (deployment_shutdown == NULL) {
845 shutdown_state = ShutdownFailed;
846 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment:Shutdown method.\n");
847 break;
851 ret = mono_runtime_invoke (deployment_shutdown, NULL, NULL, &exc);
853 if (exc) {
854 shutdown_state = ShutdownFailed;
855 fprintf (stderr, "Moonlight: Exception while cleaning up managed code.\n"); // TODO: print exception message/details
856 break;
859 result = (bool) (*(MonoBoolean *) mono_object_unbox (ret));
861 if (!result) {
862 /* Managed code isn't ready to shutdown quite yet, try again later */
863 break;
866 /* Managed shutdown successfully completed */
867 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed call to Deployment:Shutdown () on domain %p succeeded.\n", domain);
869 shutdown_state = UnloadDomain;
870 /* fall through */
872 case UnloadDomain: /* 2 */ {
873 MonoException *exc = NULL;
876 * When unloading an appdomain, all threads in that appdomain are aborted.
877 * This includes the main thread. According to Paolo it's safe if we first
878 * switch to the root domain (and there are no managed frames on the stack,
879 * which is guaranteed since we're in a glib timeout).
881 mono_domain_set (root_domain, TRUE);
883 /* Unload the domain */
884 mono_domain_try_unload (domain, (MonoObject **) &exc);
886 /* Set back to our current domain while emitting AppDomainUnloadedEvent */
887 mono_domain_set (domain, TRUE);
888 appdomain_unloaded = true;
889 Emit (Deployment::AppDomainUnloadedEvent);
891 /* Remove the domain from the hash table (since from now on the same ptr may get reused in subsquent calls to mono_domain_create_appdomain) */
892 pthread_mutex_lock (&hash_mutex);
893 g_hash_table_remove (current_hash, domain);
894 pthread_mutex_unlock (&hash_mutex);
896 /* Since the domain ptr may get reused we have to leave the root domain as the current domain */
897 mono_domain_set (root_domain, TRUE);
899 /* Clear out the domain ptr to detect any illegal uses asap */
900 /* CHECK: do we need to call mono_domain_free? */
901 domain = NULL;
903 if (exc) {
904 shutdown_state = ShutdownFailed;
905 fprintf (stderr, "Moonlight: Exception while unloading appdomain.\n"); // TODO: print exception message/details
906 break;
909 /* AppDomain successfully unloaded */
910 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): appdomain successfully unloaded.\n");
912 shutdown_state = DisposeDeployment;
913 /* fall through */
915 case DisposeDeployment: /* 3 */{
916 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed code has shutdown successfully, calling Dispose.\n");
917 Dispose ();
918 this->unref (); /* the ref taken in Shutdown */
919 return false;
923 return true; /* repeat the callback, we're not done yet */
925 #endif
927 class LoadedClosure {
928 public:
929 UIElement *obj;
930 EventHandler handler;
931 gpointer handler_data;
933 LoadedClosure (UIElement *obj, EventHandler handler, gpointer handler_data)
934 : obj (obj), handler (handler), handler_data (handler_data)
939 void
940 Deployment::delete_loaded_closure (gpointer closure)
942 delete (LoadedClosure*)closure;
945 bool
946 Deployment::match_loaded_closure (EventHandler cb_handler, gpointer cb_data, gpointer data)
948 LoadedClosure *closure_to_match = (LoadedClosure*)data;
949 LoadedClosure *closure = (LoadedClosure*)cb_data;
951 return (closure_to_match->obj == closure->obj &&
952 closure_to_match->handler == closure->handler &&
953 closure_to_match->handler_data == closure->handler_data);
957 void
958 Deployment::proxy_loaded_event (EventObject *sender, EventArgs *arg, gpointer closure)
960 LoadedClosure *lclosure = (LoadedClosure*)closure;
962 // FIXME: in a perfect world this would be all that was needed, but
963 // there are times we don't do the tree walk to add handlers to the
964 // deployment at all, so elements won't have their
965 // OnLoaded/InvokeLoaded called at all.
967 // if (!lclosure->obj->IsLoaded ())
968 // lclosure->obj->OnLoaded ();
970 if (lclosure->handler)
971 lclosure->handler (lclosure->obj, new RoutedEventArgs (lclosure->obj), lclosure->handler_data);
974 void
975 Deployment::add_loaded_handler (EventObject *obj, EventHandler handler, gpointer handler_data, gpointer closure)
977 Deployment *deployment = (Deployment*)closure;
978 LoadedClosure *lclosure = new LoadedClosure ((UIElement*)obj,
979 handler, handler_data);
980 deployment->AddHandler (Deployment::LoadedEvent, proxy_loaded_event, lclosure, delete_loaded_closure);
983 void
984 Deployment::remove_loaded_handler (EventObject *obj, EventHandler handler, gpointer handler_data, gpointer closure)
986 Deployment *deployment = (Deployment*)closure;
987 LoadedClosure *lclosure = new LoadedClosure ((UIElement*)obj,
988 handler, handler_data);
989 deployment->RemoveMatchingHandlers (Deployment::LoadedEvent, match_loaded_closure, lclosure);
990 delete lclosure;
993 void
994 Deployment::AddAllLoadedHandlers (UIElement *el, bool only_new)
996 el->ForeachHandler (UIElement::LoadedEvent, only_new, add_loaded_handler, this);
999 void
1000 Deployment::RemoveAllLoadedHandlers (UIElement *el)
1002 el->ForeachHandler (UIElement::LoadedEvent, false, remove_loaded_handler, this);
1005 void
1006 Deployment::AddLoadedHandler (UIElement *el, int token)
1008 el->ForHandler (UIElement::LoadedEvent, token, add_loaded_handler, this);
1011 void
1012 Deployment::RemoveLoadedHandler (UIElement *el, int token)
1014 el->ForHandler (UIElement::LoadedEvent, token, remove_loaded_handler, this);
1017 void
1018 Deployment::emit_delayed_loaded (EventObject *data)
1020 Deployment *deployment = (Deployment*)data;
1021 deployment->EmitLoaded ();
1024 void
1025 Deployment::PostLoaded ()
1027 if (pending_loaded)
1028 return;
1029 GetSurface()->GetTimeManager()->AddTickCall (emit_delayed_loaded, this);
1030 pending_loaded = true;
1033 void
1034 Deployment::EmitLoaded ()
1036 if (pending_loaded) {
1037 GetSurface()->GetTimeManager()->RemoveTickCall (emit_delayed_loaded, this);
1038 pending_loaded = false;
1040 Emit (Deployment::LoadedEvent, NULL, true);
1043 void
1044 Deployment::LayoutUpdated ()
1046 Emit (Deployment::LayoutUpdatedEvent);
1049 FontManager *
1050 Deployment::GetFontManager ()
1052 return font_manager;
1055 Application*
1056 Deployment::GetCurrentApplication ()
1058 return current_app;
1061 void
1062 Deployment::SetCurrentApplication (Application* value)
1064 if (current_app == value)
1065 return;
1067 if (current_app)
1068 current_app->unref ();
1070 current_app = value;
1072 if (current_app)
1073 current_app->ref ();
1076 void
1077 Deployment::RegisterDownloader (IDownloader *dl)
1079 downloaders.Append (new IDownloaderNode (dl));
1082 void
1083 Deployment::UnregisterDownloader (IDownloader *dl)
1085 IDownloaderNode *node = (IDownloaderNode *) downloaders.First ();
1086 while (node != NULL) {
1087 if (node->dl == dl) {
1088 node->dl = NULL;
1089 downloaders.Remove (node);
1090 return;
1092 node = (IDownloaderNode *) node->next;
1096 void
1097 Deployment::AbortAllDownloaders ()
1099 downloaders.Clear (true);
1102 class MediaNode : public List::Node {
1103 private:
1104 Media *media;
1106 public:
1107 MediaNode (Media *media)
1109 this->media = media;
1110 this->media->ref ();
1112 void Clear (bool dispose)
1114 if (media) {
1115 if (dispose)
1116 media->DisposeObject (media);
1117 media->unref ();
1118 media = NULL;
1121 virtual ~MediaNode ()
1123 Clear (true);
1125 Media *GetMedia () { return media; }
1128 bool
1129 Deployment::RegisterMedia (EventObject *media)
1131 bool result;
1133 LOG_DEPLOYMENT ("Deployment::RegisterMedia (%p)\n", media);
1135 medias_mutex.Lock ();
1136 if (is_shutting_down) {
1137 result = false;
1138 } else {
1139 if (medias == NULL)
1140 medias = new List ();
1141 medias->Append (new MediaNode ((Media *) media));
1142 result = true;
1144 medias_mutex.Unlock ();
1146 return result;
1149 void
1150 Deployment::UnregisterMedia (EventObject *media)
1152 MediaNode *node = NULL;
1154 LOG_DEPLOYMENT ("Deployment::UnregisterMedia (%p)\n", media);
1156 medias_mutex.Lock ();
1157 if (medias != NULL) {
1158 node = (MediaNode *) medias->First ();
1159 while (node != NULL) {
1160 if (node->GetMedia () == media) {
1161 medias->Unlink (node);
1162 break;
1164 node = (MediaNode *) node->next;
1167 medias_mutex.Unlock ();
1169 /* Don't delete with the lock held, it may reenter and dead-lock */
1170 if (node) {
1171 node->Clear (false);
1172 delete node;
1176 void
1177 Deployment::DisposeAllMedias ()
1179 List *list;
1181 medias_mutex.Lock ();
1182 list = medias;
1183 medias = NULL;
1184 medias_mutex.Unlock ();
1186 /* Don't delete with the lock held, it may reenter and dead-lock */
1187 delete list; /* the node destructor calls Dispose on the media */
1189 MediaThreadPool::WaitForCompletion (this);
1192 struct UnrefData {
1193 EventObject *obj;
1194 UnrefData *next;
1197 gboolean
1198 Deployment::DrainUnrefs (gpointer context)
1200 Deployment *deployment = (Deployment *) context;
1201 Deployment::SetCurrent (deployment);
1202 deployment->DrainUnrefs ();
1203 deployment->unref ();
1204 Deployment::SetCurrent (NULL);
1205 return false;
1208 void
1209 Deployment::DrainUnrefs ()
1211 UnrefData *list;
1212 UnrefData *next;
1214 // Get the list of objects to unref.
1215 do {
1216 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
1218 if (list == NULL)
1219 return;
1221 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, NULL));
1223 // Loop over all the objects in the list and unref them.
1224 while (list != NULL) {
1225 list->obj->unref ();
1226 next = list->next;
1227 g_free (list);
1228 list = next;
1231 #if OBJECT_TRACKING
1232 if (IsDisposed () && g_atomic_pointer_get (&pending_unrefs) == NULL && objects_destroyed != objects_created) {
1233 printf ("Moonlight: the current deployment (%p) has detected that probably no more objects will get freed on this deployment.\n", this);
1234 ReportLeaks ();
1236 #endif
1239 void
1240 Deployment::UnrefDelayed (EventObject *obj)
1242 UnrefData *list;
1243 UnrefData *item;
1245 #if SANITY
1246 if (Deployment::GetCurrent () != this)
1247 g_warning ("Deployment::UnrefDelayed (%p): The current deployment (%p) should be %p.\n", obj, Deployment::GetCurrent (), this);
1248 if (obj->GetObjectType () != Type::DEPLOYMENT && obj->GetUnsafeDeployment () != this && obj->GetUnsafeDeployment () != NULL)
1249 g_warning ("Deployment::UnrefDelayed (%p): obj's deployment %p should be %p. type: %s\n", obj, obj->GetUnsafeDeployment (), this, obj->GetTypeName ());
1250 #endif
1252 // Create the new list item
1253 item = (UnrefData *) g_malloc (sizeof (UnrefData));
1254 item->obj = obj;
1256 // Prepend the list item into the list
1257 do {
1258 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
1259 item->next = list;
1260 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, item));
1262 // If we created a new list instead of prepending to an existing one, add a idle tick call.
1263 if (list == NULL) { // don't look at item->next, item might have gotten freed already.
1264 g_idle_add (DrainUnrefs, this);
1265 ref (); // keep us alive until we've processed the unrefs.
1269 void
1270 Deployment::TrackObjectCreated (EventObject *obj)
1272 g_atomic_int_inc (&objects_created);
1274 #if OBJECT_TRACKING
1275 pthread_mutex_lock (&objects_alive_mutex);
1276 if (objects_alive == NULL)
1277 objects_alive = g_hash_table_new (g_direct_hash, g_direct_equal);
1278 g_hash_table_insert (objects_alive, obj, GINT_TO_POINTER (1));
1279 pthread_mutex_unlock (&objects_alive_mutex);
1280 #endif
1283 void
1284 Deployment::TrackObjectDestroyed (EventObject *obj)
1286 g_atomic_int_inc (&objects_destroyed);
1288 #if OBJECT_TRACKING
1289 pthread_mutex_lock (&objects_alive_mutex);
1290 g_hash_table_remove (objects_alive, obj);
1291 pthread_mutex_unlock (&objects_alive_mutex);
1293 Track ("Destroyed", "");
1294 #endif
1297 bool
1298 Deployment::IsLoadedFromXap ()
1300 return is_loaded_from_xap;
1303 void
1304 Deployment::SetIsLoadedFromXap (bool flag)
1306 is_loaded_from_xap = flag;
1309 void
1310 Deployment::SetXapLocation (const char *location)
1312 g_free (xap_location);
1313 xap_location = g_strdup (location);
1316 const char*
1317 Deployment::GetXapLocation ()
1319 return xap_location;
1322 void
1323 Deployment::TrackPath (char *path)
1325 paths.Append (new StringNode (path));
1328 void
1329 Deployment::UntrackPath (char *path)
1331 StringNode* node = (StringNode*) paths.Find (find_string, path);
1332 if (node) {
1333 g_free (node->str);
1334 paths.Remove (node);
1339 gint32
1340 Deployment::GetDeploymentCount ()
1342 return deployment_count;
1346 * AssemblyPart
1349 AssemblyPart::AssemblyPart ()
1351 SetObjectType (Type::ASSEMBLYPART);
1354 AssemblyPart::~AssemblyPart ()
1358 AssemblyPartCollection::AssemblyPartCollection ()
1360 SetObjectType (Type::ASSEMBLYPART_COLLECTION);
1363 AssemblyPartCollection::~AssemblyPartCollection ()
1368 * ExtensionPart
1371 ExtensionPart::ExtensionPart ()
1373 SetObjectType (Type::EXTENSIONPART);
1376 ExtensionPart::~ExtensionPart ()
1380 ExternalPart::ExternalPart ()
1382 SetObjectType (Type::EXTERNALPART);
1385 ExternalPart::~ExternalPart ()
1389 ExternalPartCollection::ExternalPartCollection ()
1391 SetObjectType (Type::EXTERNALPART_COLLECTION);
1394 ExternalPartCollection::~ExternalPartCollection ()
1398 /* OutOfBrowserSettings */
1399 OutOfBrowserSettings::OutOfBrowserSettings ()
1401 SetObjectType (Type::OUTOFBROWSERSETTINGS);
1404 OutOfBrowserSettings::~OutOfBrowserSettings ()
1408 /* WindowSettings */
1409 WindowSettings::WindowSettings ()
1411 SetObjectType (Type::WINDOWSETTINGS);
1414 WindowSettings::~WindowSettings ()
1418 /* Icon */
1419 Icon::Icon ()
1421 SetObjectType (Type::ICON);
1424 Icon::~Icon ()
1428 /* IconCollection */
1429 IconCollection::IconCollection ()
1431 SetObjectType (Type::ICON_COLLECTION);
1434 IconCollection::~IconCollection ()