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.
14 #include "downloader.h"
15 #include "deployment.h"
16 #include "timemanager.h"
20 #include "namescope.h"
24 #include <mono/jit/jit.h>
25 #include <mono/metadata/debug-helpers.h>
27 /* because this header sucks */
28 #include <mono/metadata/mono-debug.h>
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>
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
{
52 IDownloaderNode (IDownloader
*dl
)
56 virtual ~IDownloaderNode ()
58 if (dl
&& !dl
->IsAborted ())
64 class StringNode
: public List::Node
{
68 StringNode (char *str
) {
69 this->str
= g_strdup (str
);
74 find_string (List::Node
*node
, void *data
)
76 StringNode
*tp
= (StringNode
*)node
;
77 char *p
= (char*)data
;
79 return !strcmp (tp
->str
, p
);
83 Deployment::Initialize (const char *platform_dir
, bool create_root_domain
)
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
;
102 const gchar
*soft_debug
;
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
110 moonlight_install_signal_handlers ();
114 printf ("Moonlight: Enabling MONO_DEBUG=keep-delegates.\n");
115 g_setenv ("MONO_DEBUG", "keep-delegates", false);
118 mono_config_parse (NULL
);
120 /* if a platform directory is provided then we're running inside the browser and CoreCLR should be enabled */
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");
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);
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
);
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
);
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
181 Deployment::RegisterThread (Deployment
*deployment
)
183 LOG_DEPLOYMENT ("Deployment::RegisterThread (): Deployment: %p Domain: %p\n", deployment
, deployment
->domain
);
184 mono_thread_attach (deployment
->domain
);
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
214 if (current_domain
== NULL
) {
215 /* this may happen for threads which are not registered with managed code (audio threads for instance). Everything ok. */
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) */
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");
227 if (deployment
->domain
== NULL
) {
228 /* we switched from a deployment whose domain has been unloaded to a normal deployment */
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 */
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");
263 Deployment::SetCurrent (Deployment
* deployment
)
265 SetCurrent (deployment
, true);
269 Deployment::SetCurrent (Deployment
* deployment
, bool domain
)
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
));
280 if (deployment
!= NULL
&& deployment
->domain
!= NULL
) {
281 mono_domain_set (deployment
->domain
, TRUE
);
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
;
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
);
312 mono_domain_set (current
, FALSE
);
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
;
330 is_shutting_down
= false;
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;
340 pending_unrefs
= NULL
;
341 pending_loaded
= false;
343 objects_destroyed
= 0;
348 objects_alive
= NULL
;
349 pthread_mutex_init (&objects_alive_mutex
, NULL
);
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 ();
364 Deployment::ManagedExceptionToErrorEventArgs (MonoObject
*exc
)
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
));
386 Deployment::CreateManagedXamlLoader (gpointer plugin_instance
, XamlLoader
* native_loader
, const char *resourceBase
, const char *file
, const char *str
)
389 MonoObject
*exc
= NULL
;
391 if (moon_load_xaml
== NULL
)
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
);
408 surface
->EmitError (ManagedExceptionToErrorEventArgs (exc
));
412 return GUINT_TO_POINTER (mono_gchandle_new (loader
, false));
416 Deployment::DestroyManagedXamlLoader (gpointer xaml_loader
)
418 guint32 loader
= GPOINTER_TO_UINT (xaml_loader
);
420 mono_gchandle_free (loader
);
424 Deployment::DestroyManagedApplication (gpointer plugin_instance
)
426 if (moon_destroy_application
== NULL
)
429 MonoObject
*exc
= NULL
;
431 params
[0] = &plugin_instance
;
433 Deployment::SetCurrent (this);
435 mono_runtime_invoke (moon_destroy_application
, NULL
, params
, &exc
);
438 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
442 Deployment::InitializeAppDomain ()
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
;
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");
459 g_warning ("could not find ApplicationLauncher type");
463 moon_exception
= mono_class_from_name (system_windows_image
, "Mono", "MoonException");
464 if (!moon_exception
) {
465 g_warning ("could not find MoonException type");
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");
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");
487 printf ("Moonlight: Plugin AppDomain Creation: could not find System.Windows.dll.\n");
490 printf ("Moonlight: Plugin AppDomain Creation: %s\n", result
? "OK" : "Failed");
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
)
503 MonoObject
*exc
= NULL
;
505 Deployment::SetCurrent (this);
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
);
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
);
520 GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
524 return (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
528 Deployment::MonoGetMethodFromName (MonoClass
*klass
, const char *name
, int narg
)
531 method
= mono_class_get_method_from_name (klass
, name
, narg
);
534 printf ("Warning could not find method %s\n", name
);
540 Deployment::MonoGetPropertyFromName (MonoClass
*klass
, const char *name
)
542 MonoProperty
*property
;
543 property
= mono_class_get_property_from_name (klass
, name
);
546 printf ("Warning could not find property %s\n", name
);
553 IdComparer (gconstpointer base1
, gconstpointer base2
)
555 int id1
= (*(EventObject
**) base1
)->GetId ();
556 int id2
= (*(EventObject
**) base2
)->GetId ();
558 int iddiff
= id1
- id2
;
569 accumulate_last_n (gpointer key
,
573 GPtrArray
*last_n
= (GPtrArray
*)user_data
;
575 g_ptr_array_insert_sorted (last_n
, IdComparer
, key
);
579 Deployment::~Deployment()
581 g_free (xap_location
);
585 LOG_DEPLOYMENT ("Deployment::~Deployment (): %p\n", this);
588 if (pending_unrefs
!= NULL
)
589 g_warning ("Deployment::~Deployment (): There are still pending unrefs.\n");
591 g_warning ("Deployment::~Deployment (): There are still medias waiting to get disposed.\n");
595 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created
- objects_destroyed
);
596 if (objects_created
!= objects_destroyed
)
599 if (objects_created
!= objects_destroyed
) {
600 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created
- objects_destroyed
);
605 pthread_mutex_destroy (&objects_alive_mutex
);
606 g_hash_table_destroy (objects_alive
);
610 types
->DeleteProperties ();
620 Deployment::ReportLeaks ()
622 printf ("Deployment leak report:\n");
623 if (objects_created
== objects_destroyed
) {
624 printf ("\tno leaked objects.\n");
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
);
637 counter
= MIN(counter
, last_n
->len
);
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);
652 Deployment::Reinitialize ()
654 downloaders
.Clear (true);
655 AssemblyPartCollection
* parts
= new AssemblyPartCollection ();
661 Deployment::IsShuttingDown ()
664 return is_shutting_down
;
668 Deployment::Dispose ()
670 LOG_DEPLOYMENT ("Deployment::Dispose (): %p\n", this);
672 DependencyObject::Dispose ();
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
688 * - Unload our appdomain. We still have code executing on separate
689 * threads (user code can have threads, and there is always the
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).
716 if (current_app
!= NULL
) {
717 current_app
->Dispose ();
718 current_app
->unref ();
723 while ((node
= (StringNode
*) paths
.First ())) {
724 RemoveDir (node
->str
);
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 */
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
);
750 #if MONO_ENABLE_APP_DOMAIN_CONTROL
752 Deployment::ShutdownManagedCallback (gpointer user_data
)
754 return ((Deployment
*) user_data
)->ShutdownManaged ();
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 */
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 */
821 case CallManagedShutdown
: /* 1 */{
822 /* Call the managed System.Windows.Deployment:Shutdown method */
824 MonoObject
*exc
= NULL
;
827 if (system_windows_assembly
== NULL
) {
828 shutdown_state
= ShutdownFailed
;
829 fprintf (stderr
, "Moonlight: Can't find the System.Windows.Deployment's assembly.\n");
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");
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");
851 ret
= mono_runtime_invoke (deployment_shutdown
, NULL
, NULL
, &exc
);
854 shutdown_state
= ShutdownFailed
;
855 fprintf (stderr
, "Moonlight: Exception while cleaning up managed code.\n"); // TODO: print exception message/details
859 result
= (bool) (*(MonoBoolean
*) mono_object_unbox (ret
));
862 /* Managed code isn't ready to shutdown quite yet, try again later */
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
;
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? */
904 shutdown_state
= ShutdownFailed
;
905 fprintf (stderr
, "Moonlight: Exception while unloading appdomain.\n"); // TODO: print exception message/details
909 /* AppDomain successfully unloaded */
910 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): appdomain successfully unloaded.\n");
912 shutdown_state
= DisposeDeployment
;
915 case DisposeDeployment
: /* 3 */{
916 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed code has shutdown successfully, calling Dispose.\n");
918 this->unref (); /* the ref taken in Shutdown */
923 return true; /* repeat the callback, we're not done yet */
927 class LoadedClosure
{
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
)
940 Deployment::delete_loaded_closure (gpointer closure
)
942 delete (LoadedClosure
*)closure
;
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
);
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
);
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
);
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
);
994 Deployment::AddAllLoadedHandlers (UIElement
*el
, bool only_new
)
996 el
->ForeachHandler (UIElement::LoadedEvent
, only_new
, add_loaded_handler
, this);
1000 Deployment::RemoveAllLoadedHandlers (UIElement
*el
)
1002 el
->ForeachHandler (UIElement::LoadedEvent
, false, remove_loaded_handler
, this);
1006 Deployment::AddLoadedHandler (UIElement
*el
, int token
)
1008 el
->ForHandler (UIElement::LoadedEvent
, token
, add_loaded_handler
, this);
1012 Deployment::RemoveLoadedHandler (UIElement
*el
, int token
)
1014 el
->ForHandler (UIElement::LoadedEvent
, token
, remove_loaded_handler
, this);
1018 Deployment::emit_delayed_loaded (EventObject
*data
)
1020 Deployment
*deployment
= (Deployment
*)data
;
1021 deployment
->EmitLoaded ();
1025 Deployment::PostLoaded ()
1029 GetSurface()->GetTimeManager()->AddTickCall (emit_delayed_loaded
, this);
1030 pending_loaded
= true;
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);
1044 Deployment::LayoutUpdated ()
1046 Emit (Deployment::LayoutUpdatedEvent
);
1050 Deployment::GetFontManager ()
1052 return font_manager
;
1056 Deployment::GetCurrentApplication ()
1062 Deployment::SetCurrentApplication (Application
* value
)
1064 if (current_app
== value
)
1068 current_app
->unref ();
1070 current_app
= value
;
1073 current_app
->ref ();
1077 Deployment::RegisterDownloader (IDownloader
*dl
)
1079 downloaders
.Append (new IDownloaderNode (dl
));
1083 Deployment::UnregisterDownloader (IDownloader
*dl
)
1085 IDownloaderNode
*node
= (IDownloaderNode
*) downloaders
.First ();
1086 while (node
!= NULL
) {
1087 if (node
->dl
== dl
) {
1089 downloaders
.Remove (node
);
1092 node
= (IDownloaderNode
*) node
->next
;
1097 Deployment::AbortAllDownloaders ()
1099 downloaders
.Clear (true);
1102 class MediaNode
: public List::Node
{
1107 MediaNode (Media
*media
)
1109 this->media
= media
;
1110 this->media
->ref ();
1112 void Clear (bool dispose
)
1116 media
->DisposeObject (media
);
1121 virtual ~MediaNode ()
1125 Media
*GetMedia () { return media
; }
1129 Deployment::RegisterMedia (EventObject
*media
)
1133 LOG_DEPLOYMENT ("Deployment::RegisterMedia (%p)\n", media
);
1135 medias_mutex
.Lock ();
1136 if (is_shutting_down
) {
1140 medias
= new List ();
1141 medias
->Append (new MediaNode ((Media
*) media
));
1144 medias_mutex
.Unlock ();
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
);
1164 node
= (MediaNode
*) node
->next
;
1167 medias_mutex
.Unlock ();
1169 /* Don't delete with the lock held, it may reenter and dead-lock */
1171 node
->Clear (false);
1177 Deployment::DisposeAllMedias ()
1181 medias_mutex
.Lock ();
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);
1198 Deployment::DrainUnrefs (gpointer context
)
1200 Deployment
*deployment
= (Deployment
*) context
;
1201 Deployment::SetCurrent (deployment
);
1202 deployment
->DrainUnrefs ();
1203 deployment
->unref ();
1204 Deployment::SetCurrent (NULL
);
1209 Deployment::DrainUnrefs ()
1214 // Get the list of objects to unref.
1216 list
= (UnrefData
*) g_atomic_pointer_get (&pending_unrefs
);
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 ();
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);
1240 Deployment::UnrefDelayed (EventObject
*obj
)
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 ());
1252 // Create the new list item
1253 item
= (UnrefData
*) g_malloc (sizeof (UnrefData
));
1256 // Prepend the list item into the list
1258 list
= (UnrefData
*) g_atomic_pointer_get (&pending_unrefs
);
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.
1270 Deployment::TrackObjectCreated (EventObject
*obj
)
1272 g_atomic_int_inc (&objects_created
);
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
);
1284 Deployment::TrackObjectDestroyed (EventObject
*obj
)
1286 g_atomic_int_inc (&objects_destroyed
);
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", "");
1298 Deployment::IsLoadedFromXap ()
1300 return is_loaded_from_xap
;
1304 Deployment::SetIsLoadedFromXap (bool flag
)
1306 is_loaded_from_xap
= flag
;
1310 Deployment::SetXapLocation (const char *location
)
1312 g_free (xap_location
);
1313 xap_location
= g_strdup (location
);
1317 Deployment::GetXapLocation ()
1319 return xap_location
;
1323 Deployment::TrackPath (char *path
)
1325 paths
.Append (new StringNode (path
));
1329 Deployment::UntrackPath (char *path
)
1331 StringNode
* node
= (StringNode
*) paths
.Find (find_string
, path
);
1334 paths
.Remove (node
);
1340 Deployment::GetDeploymentCount ()
1342 return deployment_count
;
1349 AssemblyPart::AssemblyPart ()
1351 SetObjectType (Type::ASSEMBLYPART
);
1354 AssemblyPart::~AssemblyPart ()
1358 AssemblyPartCollection::AssemblyPartCollection ()
1360 SetObjectType (Type::ASSEMBLYPART_COLLECTION
);
1363 AssemblyPartCollection::~AssemblyPartCollection ()
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 ()
1421 SetObjectType (Type::ICON
);
1428 /* IconCollection */
1429 IconCollection::IconCollection ()
1431 SetObjectType (Type::ICON_COLLECTION
);
1434 IconCollection::~IconCollection ()