2009-12-01 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / deployment.cpp
blobd4edc7688592a8f43b1e58a3ed9cf6e233378a79
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 #define INCLUDED_MONO_HEADERS 1
14 #include <glib.h>
16 #include <stdlib.h>
17 #include <mono/jit/jit.h>
18 #include <mono/metadata/debug-helpers.h>
19 G_BEGIN_DECLS
20 /* because this header sucks */
21 #include <mono/metadata/mono-debug.h>
22 G_END_DECLS
23 #include <mono/metadata/mono-config.h>
24 #include <mono/metadata/mono-gc.h>
25 #include <mono/metadata/threads.h>
26 #include <mono/metadata/profiler.h>
28 #include <mono/metadata/assembly.h>
29 #include <mono/metadata/appdomain.h>
31 #include "downloader.h"
32 #include "deployment.h"
33 #include "timemanager.h"
34 #include "debug.h"
35 #include "utils.h"
36 #include "security.h"
37 #include "namescope.h"
38 #include "pipeline.h"
42 * Deployment
45 gboolean Deployment::initialized = FALSE;
46 pthread_key_t Deployment::tls_key = 0;
47 pthread_mutex_t Deployment::hash_mutex;
48 GHashTable* Deployment::current_hash = NULL;
49 MonoDomain* Deployment::root_domain = NULL;
50 Deployment *Deployment::desktop_deployment = NULL;
51 gint32 Deployment::deployment_count = 0;
53 class IDownloaderNode : public List::Node {
54 public:
55 IDownloader *dl;
56 IDownloaderNode (IDownloader *dl)
58 this->dl = dl;
60 virtual ~IDownloaderNode ()
62 if (dl && !dl->IsAborted ())
63 dl->Abort ();
68 class StringNode : public List::Node {
69 public:
70 char *str;
72 StringNode (char *str) {
73 this->str = g_strdup (str);
77 bool
78 find_string (List::Node *node, void *data)
80 StringNode *tp = (StringNode*)node;
81 char *p = (char*)data;
83 return !strcmp (tp->str, p);
86 bool
87 Deployment::Initialize (const char *platform_dir, bool create_root_domain)
89 if (initialized)
90 return true;
92 initialized = true;
94 current_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
95 pthread_key_create (&tls_key, NULL);
96 pthread_mutex_init (&hash_mutex, NULL);
98 enable_vm_stack_trace ();
100 #if MONO_ENABLE_APP_DOMAIN_CONTROL
101 if (create_root_domain) {
102 const gchar *trace_options;
103 const gchar *moon_path;
104 const gchar *profiler;
105 #if DEBUG
106 const gchar *soft_debug;
107 #endif
109 #if DEBUG && SANITY
110 // Install signal handlers for crash reporting
111 // Note that this only works if mono hasn't been
112 // initialized yet (i.e. this must not be done
113 // for mopen, etc).
114 moonlight_install_signal_handlers ();
115 #endif
117 #if DEBUG
118 printf ("Moonlight: Enabling MONO_DEBUG=keep-delegates.\n");
119 g_setenv ("MONO_DEBUG", "keep-delegates", false);
120 #endif
122 mono_config_parse (NULL);
124 /* if a platform directory is provided then we're running inside the browser and CoreCLR should be enabled */
125 if (platform_dir) {
126 // confine mono itself to the platform directory, default GAC is relative to mono_assembly_getrootdir
127 // which we set to 'platform_dir' then disable (unset) any extra GAC directory
128 security_enable_coreclr (platform_dir);
129 g_setenv ("MONO_PATH", platform_dir, true);
130 g_unsetenv ("MONO_GAC_PREFIX");
131 } else {
132 moon_path = g_getenv ("MOON_PATH");
133 if (moon_path != NULL && moon_path [0] != 0) {
134 printf ("Setting moonlight root directory to: %s\n", moon_path);
135 mono_assembly_setrootdir (moon_path);
139 trace_options = g_getenv ("MOON_TRACE");
140 if (trace_options != NULL){
141 printf ("Setting trace options to: %s\n", trace_options);
142 mono_jit_set_trace_options (trace_options);
145 profiler = g_getenv ("MOON_PROFILER");
146 if (profiler != NULL) {
147 printf ("Setting profiler to: %s\n", profiler);
148 mono_profiler_load (profiler);
151 mono_set_signal_chaining (true);
153 #if DEBUG
154 soft_debug = g_getenv ("MOON_SOFT_DEBUG");
155 if (soft_debug != NULL) {
156 gchar *opt = g_strdup_printf ("--debugger-agent=%s", soft_debug);
157 mono_jit_parse_options (1, &opt);
159 g_free (opt);
161 #endif
162 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
164 root_domain = mono_jit_init_version ("Moonlight Root Domain", "moonlight");
166 LOG_DEPLOYMENT ("Deployment::Initialize (): Root domain is %p\n", root_domain);
168 else {
169 #endif
170 root_domain = mono_domain_get ();
172 Deployment::desktop_deployment = new Deployment (root_domain);
173 Deployment::SetCurrent (Deployment::desktop_deployment);
175 Application *desktop_app = new Application ();
176 desktop_deployment->SetCurrentApplication (desktop_app);
177 #if MONO_ENABLE_APP_DOMAIN_CONTROL
179 #endif
181 return true;
184 void
185 Deployment::SetSurface (Surface *surface)
187 Surface *old;
189 VERIFY_MAIN_THREAD;
191 surface_mutex.Lock ();
192 old = this->surface;
193 this->surface = surface;
194 if (this->surface)
195 this->surface->ref ();
196 surface_mutex.Unlock ();
198 if (old)
199 old->unref (); /* unref with the mutex unlocked */
202 Surface *
203 Deployment::GetSurface ()
205 VERIFY_MAIN_THREAD;
206 return surface;
209 Surface *
210 Deployment::GetSurfaceReffed ()
212 Surface *result;
214 surface_mutex.Lock ();
215 result = this->surface;
216 if (result)
217 result->ref ();
218 surface_mutex.Unlock ();
220 return result;
223 void
224 Deployment::RegisterThread (Deployment *deployment)
226 LOG_DEPLOYMENT ("Deployment::RegisterThread (): Deployment: %p Domain: %p\n", deployment, deployment->domain);
227 mono_thread_attach (deployment->domain);
230 Deployment*
231 Deployment::GetCurrent()
233 Deployment *deployment = (Deployment *) pthread_getspecific (tls_key);
234 MonoDomain *current_domain = mono_domain_get ();
237 * If we dont have a Deployment* in the TLS slot then we are in a thread created
238 * by mono. In this case we look up in the hsah table the deployment against
239 * the current appdomain
241 if (deployment == NULL && current_domain != NULL) {
242 if (current_domain != NULL) {
243 pthread_mutex_lock (&hash_mutex);
244 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
245 pthread_mutex_unlock (&hash_mutex);
246 pthread_setspecific (tls_key, deployment);
247 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Couldn't find deployment in our tls, searched current domain %p and found: %p\n", current_domain, deployment);
252 * If we have a domain mismatch, we likely got here from managed land and need
253 * to get the deployment tied to this domain
255 if (deployment) {
256 bool mismatch;
257 if (current_domain == NULL) {
258 /* this may happen for threads which are not registered with managed code (audio threads for instance). Everything ok. */
259 mismatch = false;
260 } else if (current_domain == root_domain) {
261 if (deployment->domain == NULL) {
262 /* we're in a deployment whose domain has been unloaded (but we're in the right deployment) */
263 mismatch = false;
264 } else {
265 /* something is very wrong, I can't see how this can happen */
266 //g_warning ("Deployment::GetCurrent (): Domain mismatch, but the current domain is the root domain?\n");
267 mismatch = false;
269 } else {
270 if (deployment->domain == NULL) {
271 /* we switched from a deployment whose domain has been unloaded to a normal deployment */
272 mismatch = true;
273 } else if (deployment->domain != current_domain) {
274 /* we're in the wrong deployment: our tls entry is wrong, most likely because we got here on a managed thread */
275 mismatch = true;
276 } else {
277 /* everything ok */
278 mismatch = false;
282 if (mismatch) {
283 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",
284 (int) pthread_self (), deployment, deployment->domain, current_domain, root_domain, g_hash_table_lookup (current_hash, current_domain));
285 pthread_mutex_lock (&hash_mutex);
286 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
287 pthread_mutex_unlock (&hash_mutex);
289 /* Fixup our tls entry */
290 if (deployment != NULL) {
291 pthread_setspecific (tls_key, deployment);
296 if (deployment == NULL) {
297 // Currently this happens because we end up here during libmoon initialization.
298 // The fix is to not create objects as default values for our static dependency properties.
299 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Didn't find a deployment. This should never happen.\n");
302 return deployment;
305 void
306 Deployment::SetCurrent (Deployment* deployment)
308 SetCurrent (deployment, true);
311 void
312 Deployment::SetCurrent (Deployment* deployment, bool domain)
314 #if DEBUG
315 if (deployment && mono_domain_get () != deployment->domain) {
316 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i domain mismatch, is: %p\n", deployment, (int) pthread_self (), mono_domain_get ());
317 } else if (pthread_getspecific (tls_key) != deployment) {
318 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i deployment mismatch, is: %p\n", deployment, (int) pthread_self (), pthread_getspecific (tls_key));
320 #endif
322 if (domain) {
323 if (deployment != NULL && deployment->domain != NULL) {
324 mono_domain_set (deployment->domain, TRUE);
325 } else {
326 mono_domain_set (root_domain, TRUE);
329 pthread_setspecific (tls_key, deployment);
332 Deployment::Deployment (MonoDomain *domain)
333 : DependencyObject (this, Type::DEPLOYMENT)
335 this->domain = domain;
336 InnerConstructor ();
339 Deployment::Deployment()
340 : DependencyObject (this, Type::DEPLOYMENT)
342 MonoDomain *current = mono_domain_get ();
343 #if MONO_ENABLE_APP_DOMAIN_CONTROL
344 mono_domain_set (root_domain, FALSE);
345 domain = mono_domain_create_appdomain ((char *) "Silverlight AppDomain", NULL);
347 LOG_DEPLOYMENT ("Deployment::Deployment (): Created domain %p for deployment %p\n", domain, this);
349 mono_domain_set (domain, FALSE);
350 #else
351 domain = NULL;
352 #endif
353 InnerConstructor ();
355 mono_domain_set (current, FALSE);
358 void
359 Deployment::InnerConstructor ()
361 system_windows_image = NULL;
362 system_windows_assembly = NULL;
364 moon_load_xaml = NULL;
365 moon_initialize_deployment_xap = NULL;
366 moon_initialize_deployment_xaml = NULL;
367 moon_destroy_application = NULL;
368 moon_exception = NULL;
369 moon_exception_message = NULL;
370 moon_exception_error_code = NULL;
372 surface = NULL;
373 medias = NULL;
374 is_shutting_down = false;
375 deployment_count++;
376 appdomain_unloaded = false;
377 system_windows_assembly = NULL;
378 system_windows_deployment = NULL;
379 deployment_shutdown = NULL;
380 shutdown_state = Running; /* 0 */
381 is_loaded_from_xap = false;
382 xap_location = NULL;
383 current_app = NULL;
384 pending_unrefs = NULL;
385 pending_loaded = false;
386 objects_created = 0;
387 objects_destroyed = 0;
389 types = NULL;
391 #if OBJECT_TRACKING
392 objects_alive = NULL;
393 pthread_mutex_init (&objects_alive_mutex, NULL);
394 #endif
396 pthread_setspecific (tls_key, this);
398 pthread_mutex_lock (&hash_mutex);
399 g_hash_table_insert (current_hash, domain, this);
400 pthread_mutex_unlock (&hash_mutex);
402 font_manager = new FontManager ();
403 types = new Types ();
404 types->Initialize ();
407 ErrorEventArgs *
408 Deployment::ManagedExceptionToErrorEventArgs (MonoObject *exc)
410 int errorCode = -1;
411 char* message = NULL;
413 if (mono_object_isinst (exc, mono_get_exception_class())) {
414 MonoObject *ret = mono_property_get_value (moon_exception_message, exc, NULL, NULL);
416 message = mono_string_to_utf8 ((MonoString*)ret);
418 if (mono_object_isinst (exc, moon_exception)) {
419 MonoObject *ret = mono_property_get_value (moon_exception_error_code, exc, NULL, NULL);
421 errorCode = *(int*) mono_object_unbox (ret);
424 // FIXME: we need to figure out what type of exception it is
425 // and map it to the right MoonError::ExceptionType enum
426 return new ErrorEventArgs (RuntimeError, MoonError (MoonError::EXCEPTION, errorCode, message));
429 gpointer
430 Deployment::CreateManagedXamlLoader (gpointer plugin_instance, XamlLoader* native_loader, const char *resourceBase, const char *file, const char *str)
432 MonoObject *loader;
433 MonoObject *exc = NULL;
435 if (moon_load_xaml == NULL)
436 return NULL;
438 void *params [6];
439 Surface *surface = GetSurface ();
441 Deployment::SetCurrent (this);
443 params [0] = &native_loader;
444 params [1] = &plugin_instance;
445 params [2] = &surface;
446 params [3] = resourceBase ? mono_string_new (mono_domain_get (), resourceBase) : NULL;
447 params [4] = file ? mono_string_new (mono_domain_get (), file) : NULL;
448 params [5] = str ? mono_string_new (mono_domain_get (), str) : NULL;
449 loader = mono_runtime_invoke (moon_load_xaml, NULL, params, &exc);
451 if (exc) {
452 surface->EmitError (ManagedExceptionToErrorEventArgs (exc));
453 return NULL;
456 return GUINT_TO_POINTER (mono_gchandle_new (loader, false));
459 void
460 Deployment::DestroyManagedXamlLoader (gpointer xaml_loader)
462 guint32 loader = GPOINTER_TO_UINT (xaml_loader);
463 if (loader)
464 mono_gchandle_free (loader);
467 void
468 Deployment::DestroyManagedApplication (gpointer plugin_instance)
470 if (moon_destroy_application == NULL)
471 return;
473 MonoObject *exc = NULL;
474 void *params [1];
475 params [0] = &plugin_instance;
477 Deployment::SetCurrent (this);
479 mono_runtime_invoke (moon_destroy_application, NULL, params, &exc);
481 if (exc)
482 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc));
485 bool
486 Deployment::InitializeAppDomain ()
488 bool result = false;
490 system_windows_assembly = mono_assembly_load_with_partial_name ("System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", NULL);
492 if (system_windows_assembly) {
493 MonoClass *app_launcher;
495 result = true;
497 system_windows_image = mono_assembly_get_image (system_windows_assembly);
499 LOG_DEPLOYMENT ("Assembly: %s\n", mono_image_get_filename (system_windows_image));
501 app_launcher = mono_class_from_name (system_windows_image, "Mono", "ApplicationLauncher");
502 if (!app_launcher) {
503 g_warning ("could not find ApplicationLauncher type");
504 return false;
507 moon_exception = mono_class_from_name (system_windows_image, "Mono", "MoonException");
508 if (!moon_exception) {
509 g_warning ("could not find MoonException type");
510 return false;
513 moon_load_xaml = MonoGetMethodFromName (app_launcher, "CreateXamlLoader", -1);
514 moon_initialize_deployment_xap = MonoGetMethodFromName (app_launcher, "InitializeDeployment", 4);
515 moon_initialize_deployment_xaml = MonoGetMethodFromName (app_launcher, "InitializeDeployment", 2);
516 moon_destroy_application = MonoGetMethodFromName (app_launcher, "DestroyApplication", -1);
518 if (moon_load_xaml == NULL || moon_initialize_deployment_xap == NULL || moon_initialize_deployment_xaml == NULL || moon_destroy_application == NULL) {
519 g_warning ("lookup for ApplicationLauncher methods failed");
520 result = false;
523 moon_exception_message = MonoGetPropertyFromName (mono_get_exception_class(), "Message");
524 moon_exception_error_code = MonoGetPropertyFromName (moon_exception, "ErrorCode");
526 if (moon_exception_message == NULL || moon_exception_error_code == NULL) {
527 g_warning ("lookup for MoonException properties failed");
528 result = false;
530 } else {
531 printf ("Moonlight: Plugin AppDomain Creation: could not find System.Windows.dll.\n");
534 printf ("Moonlight: Plugin AppDomain Creation: %s\n", result ? "OK" : "Failed");
536 return result;
539 bool
540 Deployment::InitializeManagedDeployment (gpointer plugin_instance, const char *file, const char *culture, const char *uiCulture)
542 if (moon_initialize_deployment_xap == NULL && moon_initialize_deployment_xaml)
543 return false;
545 void *params [4];
546 MonoObject *ret;
547 MonoObject *exc = NULL;
549 Deployment::SetCurrent (this);
551 if (file != NULL) {
552 params [0] = &plugin_instance;
553 params [1] = mono_string_new (mono_domain_get (), file);
554 params [2] = culture ? mono_string_new (mono_domain_get (), culture) : NULL;
555 params [3] = uiCulture ? mono_string_new (mono_domain_get (), uiCulture) : NULL;
556 ret = mono_runtime_invoke (moon_initialize_deployment_xap, NULL, params, &exc);
557 } else {
558 params [0] = culture ? mono_string_new (mono_domain_get (), culture) : NULL;
559 params [1] = uiCulture ? mono_string_new (mono_domain_get (), uiCulture) : NULL;
560 ret = mono_runtime_invoke (moon_initialize_deployment_xaml, NULL, params, &exc);
563 if (exc) {
564 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc));
565 return false;
568 return (bool) (*(MonoBoolean *) mono_object_unbox (ret));
571 MonoMethod *
572 Deployment::MonoGetMethodFromName (MonoClass *klass, const char *name, int narg)
574 MonoMethod *method;
575 method = mono_class_get_method_from_name (klass, name, narg);
577 if (!method)
578 printf ("Warning could not find method %s\n", name);
580 return method;
583 MonoProperty *
584 Deployment::MonoGetPropertyFromName (MonoClass *klass, const char *name)
586 MonoProperty *property;
587 property = mono_class_get_property_from_name (klass, name);
589 if (!property)
590 printf ("Warning could not find property %s\n", name);
592 return property;
595 #if OBJECT_TRACKING
596 static int
597 IdComparer (gconstpointer base1, gconstpointer base2)
599 int id1 = (*(EventObject **) base1)->GetId ();
600 int id2 = (*(EventObject **) base2)->GetId ();
602 int iddiff = id1 - id2;
604 if (iddiff == 0)
605 return 0;
606 else if (iddiff < 0)
607 return -1;
608 else
609 return 1;
612 static void
613 accumulate_last_n (gpointer key,
614 gpointer value,
615 gpointer user_data)
617 GPtrArray *last_n = (GPtrArray*)user_data;
619 g_ptr_array_insert_sorted (last_n, IdComparer, key);
621 #endif
623 Deployment::~Deployment()
625 g_free (xap_location);
627 delete font_manager;
629 LOG_DEPLOYMENT ("Deployment::~Deployment (): %p\n", this);
631 #if SANITY
632 if (pending_unrefs != NULL)
633 g_warning ("Deployment::~Deployment (): There are still pending unrefs.\n");
634 if (medias != NULL)
635 g_warning ("Deployment::~Deployment (): There are still medias waiting to get disposed.\n");
636 #endif
638 #if OBJECT_TRACKING
639 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
640 if (objects_created != objects_destroyed)
641 ReportLeaks ();
642 #elif DEBUG
643 if (objects_created != objects_destroyed) {
644 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
646 #endif
648 #if OBJECT_TRACKING
649 pthread_mutex_destroy (&objects_alive_mutex);
650 g_hash_table_destroy (objects_alive);
651 #endif
653 if (types != NULL) {
654 types->DeleteProperties ();
655 delete types;
656 types = NULL;
659 deployment_count--;
662 #if OBJECT_TRACKING
663 void
664 Deployment::ReportLeaks ()
666 printf ("Deployment leak report:\n");
667 if (objects_created == objects_destroyed) {
668 printf ("\tno leaked objects.\n");
669 } else {
670 printf ("\tObjects created: %i\n", objects_created);
671 printf ("\tObjects destroyed: %i\n", objects_destroyed);
672 printf ("\tDifference: %i (%.1f%%)\n", objects_created - objects_destroyed, (100.0 * objects_destroyed) / objects_created);
674 GPtrArray* last_n = g_ptr_array_new ();
676 pthread_mutex_lock (&objects_alive_mutex);
677 g_hash_table_foreach (objects_alive, accumulate_last_n, last_n);
678 pthread_mutex_unlock (&objects_alive_mutex);
680 guint32 counter = 10;
681 const char *counter_str = getenv ("MOONLIGHT_OBJECT_TRACKING_COUNTER");
682 if (counter_str != NULL) {
683 if (strcmp (counter_str, "all") == 0) {
684 counter = G_MAXUINT32;
685 } else {
686 counter = atoi (counter_str);
689 counter = MIN(counter, last_n->len);
690 if (counter) {
691 printf ("\tOldest %d objects alive:\n", counter);
692 for (uint i = 0; i < MIN (counter, last_n->len); i ++) {
693 EventObject* obj = (EventObject *) last_n->pdata [i];
694 printf ("\t\t%p\t%i = %s, refcount: %i\n", obj, obj->GetId (), obj->GetTypeName (), obj->GetRefCount ());
698 g_ptr_array_free (last_n, true);
701 #endif
703 void
704 Deployment::Reinitialize ()
706 downloaders.Clear (true);
707 AssemblyPartCollection * parts = new AssemblyPartCollection ();
708 SetParts (parts);
709 parts->unref ();
712 bool
713 Deployment::IsShuttingDown ()
715 VERIFY_MAIN_THREAD;
716 return is_shutting_down;
719 void
720 Deployment::Dispose ()
722 LOG_DEPLOYMENT ("Deployment::Dispose (): %p\n", this);
724 surface_mutex.Lock ();
725 if (surface) {
726 surface->unref ();
727 surface = NULL;
729 surface_mutex.Unlock ();
731 DependencyObject::Dispose ();
734 void
735 Deployment::Shutdown ()
737 LOG_DEPLOYMENT ("Deployment::Shutdown ()\n");
740 * Shutting down is a complicated process with quite a few pitfalls.
741 * The current process is as follows:
742 * - Abort all downloaders. Firefox has a habit of calling into our
743 * downloader callbacks in bad moments, aborting all downloaders
744 * will prevent this from happening.
745 * - Ensure nothing is executed on the media threadpool threads and
746 * audio threads.
747 * - Unload our appdomain. We still have code executing on separate
748 * threads (user code can have threads, and there is always the
749 * finalizer thread).
750 * - The browser plugin is freed (the plugin needs to go away after
751 * after the appdomain, since managed code has lots of pointers
752 * to the plugin instance).
753 * - By now everything should have gotten unreffed, and the final object
754 * to be deleted is the deployment (every other object references the
755 * deployment to ensure this).
758 is_shutting_down = true;
760 g_return_if_fail (!IsDisposed ());
762 Emit (ShuttingDownEvent);
764 AbortAllDownloaders ();
766 * Dispose all Media instances so that we can be sure nothing is executed
767 * on the media threadpool threads after this point.
768 * This will also stop all media from playing, so there should be no audio
769 * threads doing anything either (note that there might be both media
770 * threadpool threads and audio threads still alive, just not executing
771 * anything related to this deployment).
773 DisposeAllMedias ();
775 if (current_app != NULL) {
776 current_app->Dispose ();
777 current_app->unref ();
778 current_app = NULL;
781 StringNode *node;
782 while ((node = (StringNode *) paths.First ())) {
783 RemoveDir (node->str);
784 g_free (node->str);
785 paths.Remove (node);
788 if (GetParts ())
789 SetParts (NULL);
791 if (GetValue (NameScope::NameScopeProperty))
792 SetValue (NameScope::NameScopeProperty, NULL);
794 #if MONO_ENABLE_APP_DOMAIN_CONTROL
795 if (system_windows_assembly == NULL) {
796 /* this can happen if initialization fails, i.e. xap downloading fails for instance */
797 shutdown_state = DisposeDeployment; /* skip managed shutdown entirely, since managed code wasn't initialized */
798 } else {
799 shutdown_state = CallManagedShutdown;
801 this->ref (); /* timemanager is dead, so we need to add timeouts directly to glib */
802 g_timeout_add_full (G_PRIORITY_DEFAULT, 1, ShutdownManagedCallback, this, NULL);
803 #endif
805 if (types)
806 types->Dispose ();
809 #if MONO_ENABLE_APP_DOMAIN_CONTROL
810 gboolean
811 Deployment::ShutdownManagedCallback (gpointer user_data)
813 return ((Deployment *) user_data)->ShutdownManaged ();
816 gboolean
817 Deployment::ShutdownManaged ()
819 if (domain == root_domain) {
820 fprintf (stderr, "Moonlight: Can't unload the root domain!\n");
821 this->unref (); /* the ref taken in Shutdown */
822 return false;
825 VERIFY_MAIN_THREAD;
828 * Managed shutdown is complicated, with a few gotchas:
829 * - managed finalizers are run on a separate thread (multi-threaded issues)
830 * - after the appdomain has unloaded, we can't call into it anymore (for
831 * instance using function pointers into managed delegates).
833 * To do have a safe shutdown we have two different approaches:
835 * 1) Protect the function pointers in native code with mutex, both during
836 * callbacks and when setting them. This has the drawback of having a
837 * mutex locked during a potentially long time (the mutex is always
838 * locked while executing the callback), and the advantage that the
839 * callbacks can be executed from any thread and the cleanup can be done
840 * directly in the managed dtor.
841 * ExternalDemuxer uses this approach.
843 * 2) If the callbacks will only be executed on the main thread, we can
844 * avoid the native locks ensuring that everything related to the
845 * callbacks will be done on the main thread by doing the following:
846 * - During execution we keep a list in managed code of cleanup actions
847 * to execute upon shutdown. If a managed object is finalized during
848 * normal execution, it removes any applicable actions from the list.
849 * This list is protected with a lock, so it can be accessed from all
850 * threads (main thread + finalizer thread).
851 * - When shutdown is requested, we set a flag to disallow further
852 * additions to the list, and execute all the cleanup actions.
853 * There are two cases where the managed finalizer is executed:
854 * a) Normal execution, in this case the native object has one ref left
855 * (the one ToggleRef has), so it is guaranteed that nobody can call
856 * the callbacks anymore -> no cleanup is needed in the managed dtor.
857 * b) Shutdown, in this case the cleanup code has already been executed
858 * (by Deployment.Shutdown), which again means that no cleanup is
859 * needed in the managed dtor.
860 * This approach only works if the callbacks are only called on the main
861 * thread (since otherwise there is a race condition between calling the
862 * callbacks and cleaning them up). It also only works for ToggleReffed/
863 * refcounted objects.
864 * MultiScaleTileSource uses this approach.
867 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): shutdown_state: %i, appdomain: %p, deployment: %p\n", shutdown_state, domain, this);
869 Deployment::SetCurrent (this, true);
871 switch (shutdown_state) {
872 case Running: /* 0 */
873 /* this shouldn't happen */
874 case ShutdownFailed: /* -1 */ {
875 /* There has been an error during shutdown and we can't continue shutting down */
876 fprintf (stderr, "Moonlight: Shutdown aborted due to unexpected error(s)\n");
877 this->unref (); /* the ref taken in Shutdown */
878 return false;
880 case CallManagedShutdown: /* 1 */{
881 /* Call the managed System.Windows.Deployment:Shutdown method */
882 MonoObject *ret;
883 MonoObject *exc = NULL;
884 bool result;
886 if (system_windows_assembly == NULL) {
887 shutdown_state = ShutdownFailed;
888 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment's assembly.\n");
889 break;
892 if (system_windows_deployment == NULL) {
893 system_windows_deployment = mono_class_from_name (system_windows_image, "System.Windows", "Deployment");
894 if (system_windows_deployment == NULL) {
895 shutdown_state = ShutdownFailed;
896 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment class.\n");
897 break;
901 if (deployment_shutdown == NULL) {
902 deployment_shutdown = mono_class_get_method_from_name (system_windows_deployment, "Shutdown", 0);
903 if (deployment_shutdown == NULL) {
904 shutdown_state = ShutdownFailed;
905 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment:Shutdown method.\n");
906 break;
910 ret = mono_runtime_invoke (deployment_shutdown, NULL, NULL, &exc);
912 if (exc) {
913 shutdown_state = ShutdownFailed;
914 fprintf (stderr, "Moonlight: Exception while cleaning up managed code.\n"); // TODO: print exception message/details
915 break;
918 result = (bool) (*(MonoBoolean *) mono_object_unbox (ret));
920 if (!result) {
921 /* Managed code isn't ready to shutdown quite yet, try again later */
922 break;
925 /* Managed shutdown successfully completed */
926 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed call to Deployment:Shutdown () on domain %p succeeded.\n", domain);
928 shutdown_state = UnloadDomain;
929 /* fall through */
931 case UnloadDomain: /* 2 */ {
932 MonoException *exc = NULL;
935 * When unloading an appdomain, all threads in that appdomain are aborted.
936 * This includes the main thread. According to Paolo it's safe if we first
937 * switch to the root domain (and there are no managed frames on the stack,
938 * which is guaranteed since we're in a glib timeout).
940 mono_domain_set (root_domain, TRUE);
942 /* Unload the domain */
943 mono_domain_try_unload (domain, (MonoObject **) &exc);
945 /* Set back to our current domain while emitting AppDomainUnloadedEvent */
946 mono_domain_set (domain, TRUE);
947 appdomain_unloaded = true;
948 Emit (Deployment::AppDomainUnloadedEvent);
950 /* 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) */
951 pthread_mutex_lock (&hash_mutex);
952 g_hash_table_remove (current_hash, domain);
953 pthread_mutex_unlock (&hash_mutex);
955 /* Since the domain ptr may get reused we have to leave the root domain as the current domain */
956 mono_domain_set (root_domain, TRUE);
958 /* Clear out the domain ptr to detect any illegal uses asap */
959 /* CHECK: do we need to call mono_domain_free? */
960 domain = NULL;
962 if (exc) {
963 shutdown_state = ShutdownFailed;
964 fprintf (stderr, "Moonlight: Exception while unloading appdomain.\n"); // TODO: print exception message/details
965 break;
968 /* AppDomain successfully unloaded */
969 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): appdomain successfully unloaded.\n");
971 shutdown_state = DisposeDeployment;
972 /* fall through */
974 case DisposeDeployment: /* 3 */{
975 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed code has shutdown successfully, calling Dispose.\n");
976 Dispose ();
977 this->unref (); /* the ref taken in Shutdown */
978 return false;
982 return true; /* repeat the callback, we're not done yet */
984 #endif
986 class LoadedClosure {
987 public:
988 UIElement *obj;
989 EventHandler handler;
990 gpointer handler_data;
992 LoadedClosure (UIElement *obj, EventHandler handler, gpointer handler_data)
993 : obj (obj), handler (handler), handler_data (handler_data)
998 void
999 Deployment::delete_loaded_closure (gpointer closure)
1001 delete (LoadedClosure*)closure;
1004 bool
1005 Deployment::match_loaded_closure (EventHandler cb_handler, gpointer cb_data, gpointer data)
1007 LoadedClosure *closure_to_match = (LoadedClosure*)data;
1008 LoadedClosure *closure = (LoadedClosure*)cb_data;
1010 return (closure_to_match->obj == closure->obj &&
1011 closure_to_match->handler == closure->handler &&
1012 closure_to_match->handler_data == closure->handler_data);
1016 void
1017 Deployment::proxy_loaded_event (EventObject *sender, EventArgs *arg, gpointer closure)
1019 LoadedClosure *lclosure = (LoadedClosure*)closure;
1021 // FIXME: in a perfect world this would be all that was needed, but
1022 // there are times we don't do the tree walk to add handlers to the
1023 // deployment at all, so elements won't have their
1024 // OnLoaded/InvokeLoaded called at all.
1026 // if (!lclosure->obj->IsLoaded ())
1027 // lclosure->obj->OnLoaded ();
1029 if (lclosure->handler)
1030 lclosure->handler (lclosure->obj, new RoutedEventArgs (lclosure->obj), lclosure->handler_data);
1033 void
1034 Deployment::add_loaded_handler (EventObject *obj, EventHandler handler, gpointer handler_data, gpointer closure)
1036 Deployment *deployment = (Deployment*)closure;
1037 LoadedClosure *lclosure = new LoadedClosure ((UIElement*)obj,
1038 handler, handler_data);
1039 deployment->AddHandler (Deployment::LoadedEvent, proxy_loaded_event, lclosure, delete_loaded_closure);
1042 void
1043 Deployment::remove_loaded_handler (EventObject *obj, EventHandler handler, gpointer handler_data, gpointer closure)
1045 Deployment *deployment = (Deployment*)closure;
1046 LoadedClosure *lclosure = new LoadedClosure ((UIElement*)obj,
1047 handler, handler_data);
1048 deployment->RemoveMatchingHandlers (Deployment::LoadedEvent, match_loaded_closure, lclosure);
1049 delete lclosure;
1052 void
1053 Deployment::AddAllLoadedHandlers (UIElement *el, bool only_new)
1055 el->ForeachHandler (UIElement::LoadedEvent, only_new, add_loaded_handler, this);
1058 void
1059 Deployment::RemoveAllLoadedHandlers (UIElement *el)
1061 el->ForeachHandler (UIElement::LoadedEvent, false, remove_loaded_handler, this);
1064 void
1065 Deployment::AddLoadedHandler (UIElement *el, int token)
1067 el->ForHandler (UIElement::LoadedEvent, token, add_loaded_handler, this);
1070 void
1071 Deployment::RemoveLoadedHandler (UIElement *el, int token)
1073 el->ForHandler (UIElement::LoadedEvent, token, remove_loaded_handler, this);
1076 void
1077 Deployment::emit_delayed_loaded (EventObject *data)
1079 Deployment *deployment = (Deployment*)data;
1080 deployment->EmitLoaded ();
1083 void
1084 Deployment::PostLoaded ()
1086 if (pending_loaded)
1087 return;
1088 GetSurface()->GetTimeManager()->AddTickCall (emit_delayed_loaded, this);
1089 pending_loaded = true;
1092 void
1093 Deployment::EmitLoaded ()
1095 if (pending_loaded) {
1096 GetSurface()->GetTimeManager()->RemoveTickCall (emit_delayed_loaded, this);
1097 pending_loaded = false;
1099 Emit (Deployment::LoadedEvent, NULL, true);
1102 void
1103 Deployment::LayoutUpdated ()
1105 Emit (Deployment::LayoutUpdatedEvent);
1108 FontManager *
1109 Deployment::GetFontManager ()
1111 return font_manager;
1114 Application*
1115 Deployment::GetCurrentApplication ()
1117 return current_app;
1120 void
1121 Deployment::SetCurrentApplication (Application* value)
1123 if (current_app == value)
1124 return;
1126 if (current_app)
1127 current_app->unref ();
1129 current_app = value;
1131 if (current_app)
1132 current_app->ref ();
1135 void
1136 Deployment::RegisterDownloader (IDownloader *dl)
1138 downloaders.Append (new IDownloaderNode (dl));
1141 void
1142 Deployment::UnregisterDownloader (IDownloader *dl)
1144 IDownloaderNode *node = (IDownloaderNode *) downloaders.First ();
1145 while (node != NULL) {
1146 if (node->dl == dl) {
1147 node->dl = NULL;
1148 downloaders.Remove (node);
1149 return;
1151 node = (IDownloaderNode *) node->next;
1155 void
1156 Deployment::AbortAllDownloaders ()
1158 downloaders.Clear (true);
1161 class MediaNode : public List::Node {
1162 private:
1163 Media *media;
1165 public:
1166 MediaNode (Media *media)
1168 this->media = media;
1169 this->media->ref ();
1171 void Clear (bool dispose)
1173 if (media) {
1174 if (dispose)
1175 media->DisposeObject (media);
1176 media->unref ();
1177 media = NULL;
1180 virtual ~MediaNode ()
1182 Clear (true);
1184 Media *GetMedia () { return media; }
1187 bool
1188 Deployment::RegisterMedia (EventObject *media)
1190 bool result;
1192 LOG_DEPLOYMENT ("Deployment::RegisterMedia (%p)\n", media);
1194 medias_mutex.Lock ();
1195 if (is_shutting_down) {
1196 result = false;
1197 } else {
1198 if (medias == NULL)
1199 medias = new List ();
1200 medias->Append (new MediaNode ((Media *) media));
1201 result = true;
1203 medias_mutex.Unlock ();
1205 return result;
1208 void
1209 Deployment::UnregisterMedia (EventObject *media)
1211 MediaNode *node = NULL;
1213 LOG_DEPLOYMENT ("Deployment::UnregisterMedia (%p)\n", media);
1215 medias_mutex.Lock ();
1216 if (medias != NULL) {
1217 node = (MediaNode *) medias->First ();
1218 while (node != NULL) {
1219 if (node->GetMedia () == media) {
1220 medias->Unlink (node);
1221 break;
1223 node = (MediaNode *) node->next;
1226 medias_mutex.Unlock ();
1228 /* Don't delete with the lock held, it may reenter and dead-lock */
1229 if (node) {
1230 node->Clear (false);
1231 delete node;
1235 void
1236 Deployment::DisposeAllMedias ()
1238 List *list;
1240 medias_mutex.Lock ();
1241 list = medias;
1242 medias = NULL;
1243 medias_mutex.Unlock ();
1245 /* Don't delete with the lock held, it may reenter and dead-lock */
1246 delete list; /* the node destructor calls Dispose on the media */
1248 MediaThreadPool::WaitForCompletion (this);
1251 struct UnrefData {
1252 EventObject *obj;
1253 UnrefData *next;
1256 gboolean
1257 Deployment::DrainUnrefs (gpointer context)
1259 Deployment *deployment = (Deployment *) context;
1260 Deployment::SetCurrent (deployment);
1261 deployment->DrainUnrefs ();
1262 deployment->unref ();
1263 Deployment::SetCurrent (NULL);
1264 return false;
1267 void
1268 Deployment::DrainUnrefs ()
1270 UnrefData *list;
1271 UnrefData *next;
1273 // Get the list of objects to unref.
1274 do {
1275 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
1277 if (list == NULL)
1278 return;
1280 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, NULL));
1282 // Loop over all the objects in the list and unref them.
1283 while (list != NULL) {
1284 list->obj->unref ();
1285 next = list->next;
1286 g_free (list);
1287 list = next;
1290 #if OBJECT_TRACKING
1291 if (IsDisposed () && g_atomic_pointer_get (&pending_unrefs) == NULL && objects_destroyed != objects_created) {
1292 printf ("Moonlight: the current deployment (%p) has detected that probably no more objects will get freed on this deployment.\n", this);
1293 ReportLeaks ();
1295 #endif
1298 void
1299 Deployment::UnrefDelayed (EventObject *obj)
1301 UnrefData *list;
1302 UnrefData *item;
1304 #if SANITY
1305 if (Deployment::GetCurrent () != this)
1306 g_warning ("Deployment::UnrefDelayed (%p): The current deployment (%p) should be %p.\n", obj, Deployment::GetCurrent (), this);
1307 if (obj->GetObjectType () != Type::DEPLOYMENT && obj->GetUnsafeDeployment () != this && obj->GetUnsafeDeployment () != NULL)
1308 g_warning ("Deployment::UnrefDelayed (%p): obj's deployment %p should be %p. type: %s\n", obj, obj->GetUnsafeDeployment (), this, obj->GetTypeName ());
1309 #endif
1311 // Create the new list item
1312 item = (UnrefData *) g_malloc (sizeof (UnrefData));
1313 item->obj = obj;
1315 // Prepend the list item into the list
1316 do {
1317 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
1318 item->next = list;
1319 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, item));
1321 // If we created a new list instead of prepending to an existing one, add a idle tick call.
1322 if (list == NULL) { // don't look at item->next, item might have gotten freed already.
1323 g_idle_add (DrainUnrefs, this);
1324 ref (); // keep us alive until we've processed the unrefs.
1328 void
1329 Deployment::TrackObjectCreated (EventObject *obj)
1331 g_atomic_int_inc (&objects_created);
1333 #if OBJECT_TRACKING
1334 pthread_mutex_lock (&objects_alive_mutex);
1335 if (objects_alive == NULL)
1336 objects_alive = g_hash_table_new (g_direct_hash, g_direct_equal);
1337 g_hash_table_insert (objects_alive, obj, GINT_TO_POINTER (1));
1338 pthread_mutex_unlock (&objects_alive_mutex);
1339 #endif
1342 void
1343 Deployment::TrackObjectDestroyed (EventObject *obj)
1345 g_atomic_int_inc (&objects_destroyed);
1347 #if OBJECT_TRACKING
1348 pthread_mutex_lock (&objects_alive_mutex);
1349 g_hash_table_remove (objects_alive, obj);
1350 pthread_mutex_unlock (&objects_alive_mutex);
1352 Track ("Destroyed", "");
1353 #endif
1356 bool
1357 Deployment::IsLoadedFromXap ()
1359 return is_loaded_from_xap;
1362 void
1363 Deployment::SetIsLoadedFromXap (bool flag)
1365 is_loaded_from_xap = flag;
1368 void
1369 Deployment::SetXapLocation (const char *location)
1371 g_free (xap_location);
1372 xap_location = g_strdup (location);
1375 const char*
1376 Deployment::GetXapLocation ()
1378 return xap_location;
1381 void
1382 Deployment::TrackPath (char *path)
1384 paths.Append (new StringNode (path));
1387 void
1388 Deployment::UntrackPath (char *path)
1390 StringNode* node = (StringNode*) paths.Find (find_string, path);
1391 if (node) {
1392 g_free (node->str);
1393 paths.Remove (node);
1398 gint32
1399 Deployment::GetDeploymentCount ()
1401 return deployment_count;
1405 * AssemblyPart
1408 AssemblyPart::AssemblyPart ()
1410 SetObjectType (Type::ASSEMBLYPART);
1413 AssemblyPart::~AssemblyPart ()
1417 AssemblyPartCollection::AssemblyPartCollection ()
1419 SetObjectType (Type::ASSEMBLYPART_COLLECTION);
1422 AssemblyPartCollection::~AssemblyPartCollection ()
1427 * ExtensionPart
1430 ExtensionPart::ExtensionPart ()
1432 SetObjectType (Type::EXTENSIONPART);
1435 ExtensionPart::~ExtensionPart ()
1439 ExternalPart::ExternalPart ()
1441 SetObjectType (Type::EXTERNALPART);
1444 ExternalPart::~ExternalPart ()
1448 ExternalPartCollection::ExternalPartCollection ()
1450 SetObjectType (Type::EXTERNALPART_COLLECTION);
1453 ExternalPartCollection::~ExternalPartCollection ()
1457 /* OutOfBrowserSettings */
1458 OutOfBrowserSettings::OutOfBrowserSettings ()
1460 SetObjectType (Type::OUTOFBROWSERSETTINGS);
1463 OutOfBrowserSettings::~OutOfBrowserSettings ()
1467 /* WindowSettings */
1468 WindowSettings::WindowSettings ()
1470 SetObjectType (Type::WINDOWSETTINGS);
1473 WindowSettings::~WindowSettings ()
1477 /* Icon */
1478 Icon::Icon ()
1480 SetObjectType (Type::ICON);
1483 Icon::~Icon ()
1487 /* IconCollection */
1488 IconCollection::IconCollection ()
1490 SetObjectType (Type::ICON_COLLECTION);
1493 IconCollection::~IconCollection ()