1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
12 #define INCLUDED_MONO_HEADERS 1
17 #include <mono/jit/jit.h>
18 #include <mono/metadata/debug-helpers.h>
20 /* because this header sucks */
21 #include <mono/metadata/mono-debug.h>
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"
37 #include "namescope.h"
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
{
56 IDownloaderNode (IDownloader
*dl
)
60 virtual ~IDownloaderNode ()
62 if (dl
&& !dl
->IsAborted ())
68 class StringNode
: public List::Node
{
72 StringNode (char *str
) {
73 this->str
= g_strdup (str
);
78 find_string (List::Node
*node
, void *data
)
80 StringNode
*tp
= (StringNode
*)node
;
81 char *p
= (char*)data
;
83 return !strcmp (tp
->str
, p
);
87 Deployment::Initialize (const char *platform_dir
, bool create_root_domain
)
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
;
106 const gchar
*soft_debug
;
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
114 moonlight_install_signal_handlers ();
118 printf ("Moonlight: Enabling MONO_DEBUG=keep-delegates.\n");
119 g_setenv ("MONO_DEBUG", "keep-delegates", false);
122 mono_config_parse (NULL
);
124 /* if a platform directory is provided then we're running inside the browser and CoreCLR should be enabled */
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");
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);
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
);
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
);
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
185 Deployment::SetSurface (Surface
*surface
)
191 surface_mutex
.Lock ();
193 this->surface
= surface
;
195 this->surface
->ref ();
196 surface_mutex
.Unlock ();
199 old
->unref (); /* unref with the mutex unlocked */
203 Deployment::GetSurface ()
210 Deployment::GetSurfaceReffed ()
214 surface_mutex
.Lock ();
215 result
= this->surface
;
218 surface_mutex
.Unlock ();
224 Deployment::RegisterThread (Deployment
*deployment
)
226 LOG_DEPLOYMENT ("Deployment::RegisterThread (): Deployment: %p Domain: %p\n", deployment
, deployment
->domain
);
227 mono_thread_attach (deployment
->domain
);
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
257 if (current_domain
== NULL
) {
258 /* this may happen for threads which are not registered with managed code (audio threads for instance). Everything ok. */
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) */
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");
270 if (deployment
->domain
== NULL
) {
271 /* we switched from a deployment whose domain has been unloaded to a normal deployment */
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 */
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");
306 Deployment::SetCurrent (Deployment
* deployment
)
308 SetCurrent (deployment
, true);
312 Deployment::SetCurrent (Deployment
* deployment
, bool domain
)
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
));
323 if (deployment
!= NULL
&& deployment
->domain
!= NULL
) {
324 mono_domain_set (deployment
->domain
, TRUE
);
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
;
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
);
355 mono_domain_set (current
, FALSE
);
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
;
374 is_shutting_down
= false;
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;
384 pending_unrefs
= NULL
;
385 pending_loaded
= false;
387 objects_destroyed
= 0;
392 objects_alive
= NULL
;
393 pthread_mutex_init (&objects_alive_mutex
, NULL
);
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 ();
408 Deployment::ManagedExceptionToErrorEventArgs (MonoObject
*exc
)
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
));
430 Deployment::CreateManagedXamlLoader (gpointer plugin_instance
, XamlLoader
* native_loader
, const char *resourceBase
, const char *file
, const char *str
)
433 MonoObject
*exc
= NULL
;
435 if (moon_load_xaml
== NULL
)
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
);
452 surface
->EmitError (ManagedExceptionToErrorEventArgs (exc
));
456 return GUINT_TO_POINTER (mono_gchandle_new (loader
, false));
460 Deployment::DestroyManagedXamlLoader (gpointer xaml_loader
)
462 guint32 loader
= GPOINTER_TO_UINT (xaml_loader
);
464 mono_gchandle_free (loader
);
468 Deployment::DestroyManagedApplication (gpointer plugin_instance
)
470 if (moon_destroy_application
== NULL
)
473 MonoObject
*exc
= NULL
;
475 params
[0] = &plugin_instance
;
477 Deployment::SetCurrent (this);
479 mono_runtime_invoke (moon_destroy_application
, NULL
, params
, &exc
);
482 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
486 Deployment::InitializeAppDomain ()
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
;
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");
503 g_warning ("could not find ApplicationLauncher type");
507 moon_exception
= mono_class_from_name (system_windows_image
, "Mono", "MoonException");
508 if (!moon_exception
) {
509 g_warning ("could not find MoonException type");
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");
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");
531 printf ("Moonlight: Plugin AppDomain Creation: could not find System.Windows.dll.\n");
534 printf ("Moonlight: Plugin AppDomain Creation: %s\n", result
? "OK" : "Failed");
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
)
547 MonoObject
*exc
= NULL
;
549 Deployment::SetCurrent (this);
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
);
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
);
564 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
568 return (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
572 Deployment::MonoGetMethodFromName (MonoClass
*klass
, const char *name
, int narg
)
575 method
= mono_class_get_method_from_name (klass
, name
, narg
);
578 printf ("Warning could not find method %s\n", name
);
584 Deployment::MonoGetPropertyFromName (MonoClass
*klass
, const char *name
)
586 MonoProperty
*property
;
587 property
= mono_class_get_property_from_name (klass
, name
);
590 printf ("Warning could not find property %s\n", name
);
597 IdComparer (gconstpointer base1
, gconstpointer base2
)
599 int id1
= (*(EventObject
**) base1
)->GetId ();
600 int id2
= (*(EventObject
**) base2
)->GetId ();
602 int iddiff
= id1
- id2
;
613 accumulate_last_n (gpointer key
,
617 GPtrArray
*last_n
= (GPtrArray
*)user_data
;
619 g_ptr_array_insert_sorted (last_n
, IdComparer
, key
);
623 Deployment::~Deployment()
625 g_free (xap_location
);
629 LOG_DEPLOYMENT ("Deployment::~Deployment (): %p\n", this);
632 if (pending_unrefs
!= NULL
)
633 g_warning ("Deployment::~Deployment (): There are still pending unrefs.\n");
635 g_warning ("Deployment::~Deployment (): There are still medias waiting to get disposed.\n");
639 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created
- objects_destroyed
);
640 if (objects_created
!= objects_destroyed
)
643 if (objects_created
!= objects_destroyed
) {
644 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created
- objects_destroyed
);
649 pthread_mutex_destroy (&objects_alive_mutex
);
650 g_hash_table_destroy (objects_alive
);
654 types
->DeleteProperties ();
664 Deployment::ReportLeaks ()
666 printf ("Deployment leak report:\n");
667 if (objects_created
== objects_destroyed
) {
668 printf ("\tno leaked objects.\n");
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
;
686 counter
= atoi (counter_str
);
689 counter
= MIN(counter
, last_n
->len
);
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);
704 Deployment::Reinitialize ()
706 downloaders
.Clear (true);
707 AssemblyPartCollection
* parts
= new AssemblyPartCollection ();
713 Deployment::IsShuttingDown ()
716 return is_shutting_down
;
720 Deployment::Dispose ()
722 LOG_DEPLOYMENT ("Deployment::Dispose (): %p\n", this);
724 surface_mutex
.Lock ();
729 surface_mutex
.Unlock ();
731 DependencyObject::Dispose ();
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
747 * - Unload our appdomain. We still have code executing on separate
748 * threads (user code can have threads, and there is always the
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).
775 if (current_app
!= NULL
) {
776 current_app
->Dispose ();
777 current_app
->unref ();
782 while ((node
= (StringNode
*) paths
.First ())) {
783 RemoveDir (node
->str
);
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 */
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
);
809 #if MONO_ENABLE_APP_DOMAIN_CONTROL
811 Deployment::ShutdownManagedCallback (gpointer user_data
)
813 return ((Deployment
*) user_data
)->ShutdownManaged ();
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 */
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 */
880 case CallManagedShutdown
: /* 1 */{
881 /* Call the managed System.Windows.Deployment:Shutdown method */
883 MonoObject
*exc
= NULL
;
886 if (system_windows_assembly
== NULL
) {
887 shutdown_state
= ShutdownFailed
;
888 fprintf (stderr
, "Moonlight: Can't find the System.Windows.Deployment's assembly.\n");
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");
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");
910 ret
= mono_runtime_invoke (deployment_shutdown
, NULL
, NULL
, &exc
);
913 shutdown_state
= ShutdownFailed
;
914 fprintf (stderr
, "Moonlight: Exception while cleaning up managed code.\n"); // TODO: print exception message/details
918 result
= (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
921 /* Managed code isn't ready to shutdown quite yet, try again later */
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
;
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? */
963 shutdown_state
= ShutdownFailed
;
964 fprintf (stderr
, "Moonlight: Exception while unloading appdomain.\n"); // TODO: print exception message/details
968 /* AppDomain successfully unloaded */
969 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): appdomain successfully unloaded.\n");
971 shutdown_state
= DisposeDeployment
;
974 case DisposeDeployment
: /* 3 */{
975 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed code has shutdown successfully, calling Dispose.\n");
977 this->unref (); /* the ref taken in Shutdown */
982 return true; /* repeat the callback, we're not done yet */
986 class LoadedClosure
{
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
)
999 Deployment::delete_loaded_closure (gpointer closure
)
1001 delete (LoadedClosure
*)closure
;
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
);
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
);
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
);
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
);
1053 Deployment::AddAllLoadedHandlers (UIElement
*el
, bool only_new
)
1055 el
->ForeachHandler (UIElement::LoadedEvent
, only_new
, add_loaded_handler
, this);
1059 Deployment::RemoveAllLoadedHandlers (UIElement
*el
)
1061 el
->ForeachHandler (UIElement::LoadedEvent
, false, remove_loaded_handler
, this);
1065 Deployment::AddLoadedHandler (UIElement
*el
, int token
)
1067 el
->ForHandler (UIElement::LoadedEvent
, token
, add_loaded_handler
, this);
1071 Deployment::RemoveLoadedHandler (UIElement
*el
, int token
)
1073 el
->ForHandler (UIElement::LoadedEvent
, token
, remove_loaded_handler
, this);
1077 Deployment::emit_delayed_loaded (EventObject
*data
)
1079 Deployment
*deployment
= (Deployment
*)data
;
1080 deployment
->EmitLoaded ();
1084 Deployment::PostLoaded ()
1088 GetSurface()->GetTimeManager()->AddTickCall (emit_delayed_loaded
, this);
1089 pending_loaded
= true;
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);
1103 Deployment::LayoutUpdated ()
1105 Emit (Deployment::LayoutUpdatedEvent
);
1109 Deployment::GetFontManager ()
1111 return font_manager
;
1115 Deployment::GetCurrentApplication ()
1121 Deployment::SetCurrentApplication (Application
* value
)
1123 if (current_app
== value
)
1127 current_app
->unref ();
1129 current_app
= value
;
1132 current_app
->ref ();
1136 Deployment::RegisterDownloader (IDownloader
*dl
)
1138 downloaders
.Append (new IDownloaderNode (dl
));
1142 Deployment::UnregisterDownloader (IDownloader
*dl
)
1144 IDownloaderNode
*node
= (IDownloaderNode
*) downloaders
.First ();
1145 while (node
!= NULL
) {
1146 if (node
->dl
== dl
) {
1148 downloaders
.Remove (node
);
1151 node
= (IDownloaderNode
*) node
->next
;
1156 Deployment::AbortAllDownloaders ()
1158 downloaders
.Clear (true);
1161 class MediaNode
: public List::Node
{
1166 MediaNode (Media
*media
)
1168 this->media
= media
;
1169 this->media
->ref ();
1171 void Clear (bool dispose
)
1175 media
->DisposeObject (media
);
1180 virtual ~MediaNode ()
1184 Media
*GetMedia () { return media
; }
1188 Deployment::RegisterMedia (EventObject
*media
)
1192 LOG_DEPLOYMENT ("Deployment::RegisterMedia (%p)\n", media
);
1194 medias_mutex
.Lock ();
1195 if (is_shutting_down
) {
1199 medias
= new List ();
1200 medias
->Append (new MediaNode ((Media
*) media
));
1203 medias_mutex
.Unlock ();
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
);
1223 node
= (MediaNode
*) node
->next
;
1226 medias_mutex
.Unlock ();
1228 /* Don't delete with the lock held, it may reenter and dead-lock */
1230 node
->Clear (false);
1236 Deployment::DisposeAllMedias ()
1240 medias_mutex
.Lock ();
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);
1257 Deployment::DrainUnrefs (gpointer context
)
1259 Deployment
*deployment
= (Deployment
*) context
;
1260 Deployment::SetCurrent (deployment
);
1261 deployment
->DrainUnrefs ();
1262 deployment
->unref ();
1263 Deployment::SetCurrent (NULL
);
1268 Deployment::DrainUnrefs ()
1273 // Get the list of objects to unref.
1275 list
= (UnrefData
*) g_atomic_pointer_get (&pending_unrefs
);
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 ();
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);
1299 Deployment::UnrefDelayed (EventObject
*obj
)
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 ());
1311 // Create the new list item
1312 item
= (UnrefData
*) g_malloc (sizeof (UnrefData
));
1315 // Prepend the list item into the list
1317 list
= (UnrefData
*) g_atomic_pointer_get (&pending_unrefs
);
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.
1329 Deployment::TrackObjectCreated (EventObject
*obj
)
1331 g_atomic_int_inc (&objects_created
);
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
);
1343 Deployment::TrackObjectDestroyed (EventObject
*obj
)
1345 g_atomic_int_inc (&objects_destroyed
);
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", "");
1357 Deployment::IsLoadedFromXap ()
1359 return is_loaded_from_xap
;
1363 Deployment::SetIsLoadedFromXap (bool flag
)
1365 is_loaded_from_xap
= flag
;
1369 Deployment::SetXapLocation (const char *location
)
1371 g_free (xap_location
);
1372 xap_location
= g_strdup (location
);
1376 Deployment::GetXapLocation ()
1378 return xap_location
;
1382 Deployment::TrackPath (char *path
)
1384 paths
.Append (new StringNode (path
));
1388 Deployment::UntrackPath (char *path
)
1390 StringNode
* node
= (StringNode
*) paths
.Find (find_string
, path
);
1393 paths
.Remove (node
);
1399 Deployment::GetDeploymentCount ()
1401 return deployment_count
;
1408 AssemblyPart::AssemblyPart ()
1410 SetObjectType (Type::ASSEMBLYPART
);
1413 AssemblyPart::~AssemblyPart ()
1417 AssemblyPartCollection::AssemblyPartCollection ()
1419 SetObjectType (Type::ASSEMBLYPART_COLLECTION
);
1422 AssemblyPartCollection::~AssemblyPartCollection ()
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 ()
1480 SetObjectType (Type::ICON
);
1487 /* IconCollection */
1488 IconCollection::IconCollection ()
1490 SetObjectType (Type::ICON_COLLECTION
);
1493 IconCollection::~IconCollection ()