2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / deployment.cpp
blob911d5887575a40eb56d189f4a73386105202691a
1 /*
2 * deployment.cpp: Deployment Class support
4 * Copyright 2008 Novell, Inc. (http://www.novell.com)
6 * See the LICENSE file included with the distribution for details.
7 */
9 #include <config.h>
11 #include <glib.h>
13 #include "downloader.h"
14 #include "deployment.h"
15 #include "debug.h"
16 #include "utils.h"
17 #include "security.h"
18 #include "namescope.h"
19 #include "pipeline.h"
21 #include <stdlib.h>
22 #include <mono/jit/jit.h>
23 #include <mono/metadata/debug-helpers.h>
24 G_BEGIN_DECLS
25 /* because this header sucks */
26 #include <mono/metadata/mono-debug.h>
27 G_END_DECLS
28 #include <mono/metadata/mono-config.h>
29 #include <mono/metadata/mono-gc.h>
30 #include <mono/metadata/threads.h>
31 #include <mono/metadata/profiler.h>
33 #include <mono/metadata/appdomain.h>
36 * Deployment
39 gboolean Deployment::initialized = FALSE;
40 pthread_key_t Deployment::tls_key = 0;
41 pthread_mutex_t Deployment::hash_mutex;
42 GHashTable* Deployment::current_hash = NULL;
43 MonoDomain* Deployment::root_domain = NULL;
44 Deployment *Deployment::desktop_deployment = NULL;
45 gint32 Deployment::deployment_count = 0;
47 class IDownloaderNode : public List::Node {
48 public:
49 IDownloader *dl;
50 IDownloaderNode (IDownloader *dl)
52 this->dl = dl;
54 virtual ~IDownloaderNode ()
56 if (dl && !dl->IsAborted ())
57 dl->Abort ();
62 class StringNode : public List::Node {
63 public:
64 char *str;
66 StringNode (char *str) {
67 this->str = g_strdup (str);
71 bool
72 find_string (List::Node *node, void *data)
74 StringNode *tp = (StringNode*)node;
75 char *p = (char*)data;
77 return !strcmp (tp->str, p);
80 bool
81 Deployment::Initialize (const char *platform_dir, bool create_root_domain)
83 if (initialized)
84 return true;
86 initialized = true;
88 current_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
89 pthread_key_create (&tls_key, NULL);
90 pthread_mutex_init (&hash_mutex, NULL);
92 enable_vm_stack_trace ();
94 #if MONO_ENABLE_APP_DOMAIN_CONTROL
95 if (create_root_domain) {
96 const gchar *trace_options;
97 const gchar *moon_path;
98 const gchar *profiler;
100 #if DEBUG && SANITY
101 // Install signal handlers for crash reporting
102 // Note that this only works if mono hasn't been
103 // initialized yet (i.e. this must not be done
104 // for mopen, etc).
105 moonlight_install_signal_handlers ();
106 #endif
108 #if DEBUG
109 printf ("Moonlight: Enabling MONO_DEBUG=keep-delegates.\n");
110 g_setenv ("MONO_DEBUG", "keep-delegates", false);
111 #endif
113 mono_config_parse (NULL);
115 /* if a platform directory is provided then we're running inside the browser and CoreCLR should be enabled */
116 if (platform_dir) {
117 security_enable_coreclr (platform_dir);
119 /* XXX confine mono itself to the platform directory XXX incomplete */
120 g_setenv ("MONO_PATH", platform_dir, true);
121 g_unsetenv ("MONO_GAC_PREFIX");
122 } else {
123 moon_path = g_getenv ("MOON_PATH");
124 if (moon_path != NULL && moon_path [0] != 0) {
125 printf ("Setting moonlight root directory to: %s\n", moon_path);
126 mono_assembly_setrootdir (moon_path);
130 trace_options = g_getenv ("MOON_TRACE");
131 if (trace_options != NULL){
132 printf ("Setting trace options to: %s\n", trace_options);
133 mono_jit_set_trace_options (trace_options);
136 profiler = g_getenv ("MOON_PROFILER");
137 if (profiler != NULL) {
138 printf ("Setting profiler to: %s\n", profiler);
139 mono_profiler_load (profiler);
142 mono_set_signal_chaining (true);
143 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
145 root_domain = mono_jit_init_version ("Moonlight Root Domain", "moonlight");
147 LOG_DEPLOYMENT ("Deployment::Initialize (): Root domain is %p\n", root_domain);
149 else {
150 #endif
151 root_domain = mono_domain_get ();
153 Deployment::desktop_deployment = new Deployment (root_domain);
154 Deployment::SetCurrent (Deployment::desktop_deployment);
156 Application *desktop_app = new Application ();
157 desktop_deployment->SetCurrentApplication (desktop_app);
158 desktop_app->unref ();
159 #if MONO_ENABLE_APP_DOMAIN_CONTROL
161 #endif
163 return true;
166 void
167 Deployment::RegisterThread (Deployment *deployment)
169 LOG_DEPLOYMENT ("Deployment::RegisterThread (): Deployment: %p Domain: %p\n", deployment, deployment->domain);
170 mono_thread_attach (deployment->domain);
173 Deployment*
174 Deployment::GetCurrent()
176 Deployment *deployment = (Deployment *) pthread_getspecific (tls_key);
177 MonoDomain *current_domain = mono_domain_get ();
180 * If we dont have a Deployment* in the TLS slot then we are in a thread created
181 * by mono. In this case we look up in the hsah table the deployment against
182 * the current appdomain
184 if (deployment == NULL && current_domain != NULL) {
185 if (current_domain != NULL) {
186 pthread_mutex_lock (&hash_mutex);
187 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
188 pthread_mutex_unlock (&hash_mutex);
189 pthread_setspecific (tls_key, deployment);
190 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Couldn't find deployment in our tls, searched current domain %p and found: %p\n", current_domain, deployment);
195 * If we have a domain mismatch, we likely got here from managed land and need
196 * to get the deployment tied to this domain
198 if (deployment) {
199 bool mismatch;
200 if (current_domain == NULL) {
201 /* this may happen for threads which are not registered with managed code (audio threads for instance). Everything ok. */
202 mismatch = false;
203 } else if (current_domain == root_domain) {
204 if (deployment->domain == NULL) {
205 /* we're in a deployment whose domain has been unloaded (but we're in the right deployment) */
206 mismatch = false;
207 } else {
208 /* something is very wrong, I can't see how this can happen */
209 g_warning ("Deployment::GetCurrent (): Domain mismatch, but the current domain is the root domain?\n");
210 mismatch = false;
212 } else {
213 if (deployment->domain == NULL) {
214 /* we switched from a deployment whose domain has been unloaded to a normal deployment */
215 mismatch = true;
216 } else if (deployment->domain != current_domain) {
217 /* we're in the wrong deployment: our tls entry is wrong, most likely because we got here on a managed thread */
218 mismatch = true;
219 } else {
220 /* everything ok */
221 mismatch = false;
225 if (mismatch) {
226 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",
227 (int) pthread_self (), deployment, deployment->domain, current_domain, root_domain, g_hash_table_lookup (current_hash, current_domain));
228 pthread_mutex_lock (&hash_mutex);
229 deployment = (Deployment *) g_hash_table_lookup (current_hash, current_domain);
230 pthread_mutex_unlock (&hash_mutex);
232 /* Fixup our tls entry */
233 if (deployment != NULL) {
234 pthread_setspecific (tls_key, deployment);
239 if (deployment == NULL) {
240 // Currently this happens because we end up here during libmoon initialization.
241 // The fix is to not create objects as default values for our static dependency properties.
242 LOG_DEPLOYMENT ("Deployment::GetCurrent (): Didn't find a deployment. This should never happen.\n");
245 return deployment;
248 void
249 Deployment::SetCurrent (Deployment* deployment)
251 SetCurrent (deployment, true);
254 void
255 Deployment::SetCurrent (Deployment* deployment, bool domain)
257 #if DEBUG
258 if (deployment && mono_domain_get () != deployment->domain) {
259 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i domain mismatch, is: %p\n", deployment, (int) pthread_self (), mono_domain_get ());
260 } else if (pthread_getspecific (tls_key) != deployment) {
261 LOG_DEPLOYMENT ("Deployment::SetCurrent (%p), thread: %i deployment mismatch, is: %p\n", deployment, (int) pthread_self (), pthread_getspecific (tls_key));
263 #endif
265 if (domain) {
266 if (deployment != NULL && deployment->domain != NULL) {
267 mono_domain_set (deployment->domain, TRUE);
268 } else {
269 mono_domain_set (root_domain, TRUE);
272 pthread_setspecific (tls_key, deployment);
275 Deployment::Deployment (MonoDomain *domain)
276 : DependencyObject (this, Type::DEPLOYMENT)
278 this->domain = domain;
279 InnerConstructor ();
282 Deployment::Deployment()
283 : DependencyObject (this, Type::DEPLOYMENT)
285 MonoDomain *current = mono_domain_get ();
286 #if MONO_ENABLE_APP_DOMAIN_CONTROL
287 char *domain_name = g_strdup_printf ("moonlight-%p", this);
288 mono_domain_set (root_domain, FALSE);
289 domain = mono_domain_create_appdomain (domain_name, NULL);
290 g_free (domain_name);
292 LOG_DEPLOYMENT ("Deployment::Deployment (): Created domain %p for deployment %p\n", domain, this);
294 mono_domain_set (domain, FALSE);
295 #else
296 domain = NULL;
297 #endif
298 InnerConstructor ();
300 mono_domain_set (current, FALSE);
303 void
304 Deployment::InnerConstructor ()
306 medias = NULL;
307 is_shutting_down = false;
308 deployment_count++;
309 appdomain_unloaded = false;
310 system_windows_assembly = NULL;
311 system_windows_deployment = NULL;
312 deployment_shutdown = NULL;
313 shutdown_state = Running; /* 0 */
314 is_loaded_from_xap = false;
315 xap_location = NULL;
316 current_app = NULL;
317 pending_unrefs = NULL;
318 objects_created = 0;
319 objects_destroyed = 0;
321 types = NULL;
323 #if OBJECT_TRACKING
324 objects_alive = NULL;
325 pthread_mutex_init (&objects_alive_mutex, NULL);
326 #endif
328 pthread_setspecific (tls_key, this);
330 pthread_mutex_lock (&hash_mutex);
331 g_hash_table_insert (current_hash, domain, this);
332 pthread_mutex_unlock (&hash_mutex);
334 font_manager = new FontManager ();
335 types = new Types ();
336 types->Initialize ();
339 #if OBJECT_TRACKING
340 static int
341 IdComparer (gconstpointer base1, gconstpointer base2)
343 int id1 = (*(EventObject **) base1)->GetId ();
344 int id2 = (*(EventObject **) base2)->GetId ();
346 int iddiff = id1 - id2;
348 if (iddiff == 0)
349 return 0;
350 else if (iddiff < 0)
351 return -1;
352 else
353 return 1;
356 static void
357 accumulate_last_n (gpointer key,
358 gpointer value,
359 gpointer user_data)
361 GPtrArray *last_n = (GPtrArray*)user_data;
363 g_ptr_array_insert_sorted (last_n, IdComparer, key);
365 #endif
367 Deployment::~Deployment()
369 g_free (xap_location);
371 delete font_manager;
373 LOG_DEPLOYMENT ("Deployment::~Deployment (): %p\n", this);
375 #if SANITY
376 if (pending_unrefs != NULL)
377 g_warning ("Deployment::~Deployment (): There are still pending unrefs.\n");
378 if (medias != NULL)
379 g_warning ("Deployment::~Deployment (): There are still medias waiting to get disposed.\n");
380 #endif
382 #if OBJECT_TRACKING
383 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
384 if (objects_created != objects_destroyed)
385 ReportLeaks ();
386 #elif DEBUG
387 if (objects_created != objects_destroyed) {
388 printf ("Deployment destroyed, with %i leaked EventObjects.\n", objects_created - objects_destroyed);
390 #endif
392 #if OBJECT_TRACKING
393 pthread_mutex_destroy (&objects_alive_mutex);
394 g_hash_table_destroy (objects_alive);
395 #endif
397 if (types != NULL) {
398 types->DeleteProperties ();
399 delete types;
400 types = NULL;
403 deployment_count--;
406 #if OBJECT_TRACKING
407 void
408 Deployment::ReportLeaks ()
410 printf ("Deployment leak report:\n");
411 if (objects_created == objects_destroyed) {
412 printf ("\tno leaked objects.\n");
413 } else {
414 printf ("\tObjects created: %i\n", objects_created);
415 printf ("\tObjects destroyed: %i\n", objects_destroyed);
416 printf ("\tDifference: %i (%.1f%%)\n", objects_created - objects_destroyed, (100.0 * objects_destroyed) / objects_created);
418 GPtrArray* last_n = g_ptr_array_new ();
420 pthread_mutex_lock (&objects_alive_mutex);
421 g_hash_table_foreach (objects_alive, accumulate_last_n, last_n);
422 pthread_mutex_unlock (&objects_alive_mutex);
424 uint counter = 10;
425 counter = MIN(counter, last_n->len);
426 if (counter) {
427 printf ("\tOldest %d objects alive:\n", counter);
428 for (uint i = 0; i < MIN (counter, last_n->len); i ++) {
429 EventObject* obj = (EventObject *) last_n->pdata [i];
430 printf ("\t\t%p\t%i = %s, refcount: %i\n", obj, obj->GetId (), obj->GetTypeName (), obj->GetRefCount ());
434 g_ptr_array_free (last_n, true);
437 #endif
439 void
440 Deployment::Reinitialize ()
442 downloaders.Clear (true);
443 AssemblyPartCollection * parts = new AssemblyPartCollection ();
444 SetParts (parts);
445 parts->unref ();
448 bool
449 Deployment::IsShuttingDown ()
451 VERIFY_MAIN_THREAD;
452 return is_shutting_down;
455 void
456 Deployment::Dispose ()
458 LOG_DEPLOYMENT ("Deployment::Dispose (): %p\n", this);
460 DependencyObject::Dispose ();
463 void
464 Deployment::Shutdown (MonoImage *system_windows_assembly)
466 LOG_DEPLOYMENT ("Deployment::Shutdown ()\n");
469 * Shutting down is a complicated process with quite a few pitfalls.
470 * The current process is as follows:
471 * - Abort all downloaders. Firefox has a habit of calling into our
472 * downloader callbacks in bad moments, aborting all downloaders
473 * will prevent this from happening.
474 * - Ensure nothing is executed on the media threadpool threads and
475 * audio threads.
476 * - Unload our appdomain. We still have code executing on separate
477 * threads (user code can have threads, and there is always the
478 * finalizer thread).
479 * - The browser plugin is freed (the plugin needs to go away after
480 * after the appdomain, since managed code has lots of pointers
481 * to the plugin instance).
482 * - By now everything should have gotten unreffed, and the final object
483 * to be deleted is the deployment (every other object references the
484 * deployment to ensure this).
487 is_shutting_down = true;
489 g_return_if_fail (!IsDisposed ());
491 Emit (ShuttingDownEvent);
493 AbortAllDownloaders ();
495 * Dispose all Media instances so that we can be sure nothing is executed
496 * on the media threadpool threads after this point.
497 * This will also stop all media from playing, so there should be no audio
498 * threads doing anything either (note that there might be both media
499 * threadpool threads and audio threads still alive, just not executing
500 * anything related to this deployment).
502 DisposeAllMedias ();
504 if (current_app != NULL) {
505 current_app->Dispose ();
506 current_app->unref ();
507 current_app = NULL;
510 StringNode *node;
511 while ((node = (StringNode *) paths.First ())) {
512 RemoveDir (node->str);
513 g_free (node->str);
514 paths.Remove (node);
517 if (GetParts ())
518 SetParts (NULL);
520 if (GetValue (NameScope::NameScopeProperty))
521 SetValue (NameScope::NameScopeProperty, NULL);
523 #if MONO_ENABLE_APP_DOMAIN_CONTROL
524 if (system_windows_assembly == NULL) {
525 /* this can happen if initialization fails, i.e. xap downloading fails for instance */
526 shutdown_state = DisposeDeployment; /* skip managed shutdown entirely, since managed code wasn't initialized */
527 } else {
528 shutdown_state = CallManagedShutdown;
529 this->system_windows_assembly = system_windows_assembly;
531 this->ref (); /* timemanager is dead, so we need to add timeouts directly to glib */
532 g_timeout_add_full (G_PRIORITY_DEFAULT, 1, ShutdownManagedCallback, this, NULL);
533 #endif
535 if (types)
536 types->Dispose ();
539 #if MONO_ENABLE_APP_DOMAIN_CONTROL
540 gboolean
541 Deployment::ShutdownManagedCallback (gpointer user_data)
543 return ((Deployment *) user_data)->ShutdownManaged ();
546 gboolean
547 Deployment::ShutdownManaged ()
549 if (domain == root_domain) {
550 fprintf (stderr, "Moonlight: Can't unload the root domain!\n");
551 this->unref (); /* the ref taken in Shutdown */
552 return false;
555 VERIFY_MAIN_THREAD;
558 * Managed shutdown is complicated, with a few gotchas:
559 * - managed finalizers are run on a separate thread (multi-threaded issues)
560 * - after the appdomain has unloaded, we can't call into it anymore (for
561 * instance using function pointers into managed delegates).
563 * To do have a safe shutdown we have two different approaches:
565 * 1) Protect the function pointers in native code with mutex, both during
566 * callbacks and when setting them. This has the drawback of having a
567 * mutex locked during a potentially long time (the mutex is always
568 * locked while executing the callback), and the advantage that the
569 * callbacks can be executed from any thread and the cleanup can be done
570 * directly in the managed dtor.
571 * ExternalDemuxer uses this approach.
573 * 2) If the callbacks will only be executed on the main thread, we can
574 * avoid the native locks ensuring that everything related to the
575 * callbacks will be done on the main thread by doing the following:
576 * - During execution we keep a list in managed code of cleanup actions
577 * to execute upon shutdown. If a managed object is finalized during
578 * normal execution, it removes any applicable actions from the list.
579 * This list is protected with a lock, so it can be accessed from all
580 * threads (main thread + finalizer thread).
581 * - When shutdown is requested, we set a flag to disallow further
582 * additions to the list, and execute all the cleanup actions.
583 * There are two cases where the managed finalizer is executed:
584 * a) Normal execution, in this case the native object has one ref left
585 * (the one ToggleRef has), so it is guaranteed that nobody can call
586 * the callbacks anymore -> no cleanup is needed in the managed dtor.
587 * b) Shutdown, in this case the cleanup code has already been executed
588 * (by Deployment.Shutdown), which again means that no cleanup is
589 * needed in the managed dtor.
590 * This approach only works if the callbacks are only called on the main
591 * thread (since otherwise there is a race condition between calling the
592 * callbacks and cleaning them up). It also only works for ToggleReffed/
593 * refcounted objects.
594 * MultiScaleTileSource uses this approach.
597 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): shutdown_state: %i, appdomain: %p, deployment: %p\n", shutdown_state, domain, this);
599 Deployment::SetCurrent (this, true);
601 switch (shutdown_state) {
602 case Running: /* 0 */
603 /* this shouldn't happen */
604 case ShutdownFailed: /* -1 */ {
605 /* There has been an error during shutdown and we can't continue shutting down */
606 fprintf (stderr, "Moonlight: Shutdown aborted due to unexpected error(s)\n");
607 this->unref (); /* the ref taken in Shutdown */
608 return false;
610 case CallManagedShutdown: /* 1 */{
611 /* Call the managed System.Windows.Deployment:Shutdown method */
612 MonoObject *ret;
613 MonoObject *exc = NULL;
614 bool result;
616 if (system_windows_assembly == NULL) {
617 shutdown_state = ShutdownFailed;
618 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment's assembly.\n");
619 break;
622 if (system_windows_deployment == NULL) {
623 system_windows_deployment = mono_class_from_name (system_windows_assembly, "System.Windows", "Deployment");
624 if (system_windows_deployment == NULL) {
625 shutdown_state = ShutdownFailed;
626 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment class.\n");
627 break;
631 if (deployment_shutdown == NULL) {
632 deployment_shutdown = mono_class_get_method_from_name (system_windows_deployment, "Shutdown", 0);
633 if (deployment_shutdown == NULL) {
634 shutdown_state = ShutdownFailed;
635 fprintf (stderr, "Moonlight: Can't find the System.Windows.Deployment:Shutdown method.\n");
636 break;
640 ret = mono_runtime_invoke (deployment_shutdown, NULL, NULL, &exc);
642 if (exc) {
643 shutdown_state = ShutdownFailed;
644 fprintf (stderr, "Moonlight: Exception while cleaning up managed code.\n"); // TODO: print exception message/details
645 break;
648 result = (bool) (*(MonoBoolean *) mono_object_unbox (ret));
650 if (!result) {
651 /* Managed code isn't ready to shutdown quite yet, try again later */
652 break;
655 /* Managed shutdown successfully completed */
656 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed call to Deployment:Shutdown () on domain %p succeeded.\n", domain);
658 shutdown_state = UnloadDomain;
659 /* fall through */
661 case UnloadDomain: /* 2 */ {
662 MonoException *exc = NULL;
665 * When unloading an appdomain, all threads in that appdomain are aborted.
666 * This includes the main thread. According to Paolo it's safe if we first
667 * switch to the root domain (and there are no managed frames on the stack,
668 * which is guaranteed since we're in a glib timeout).
670 mono_domain_set (root_domain, TRUE);
672 /* Unload the domain */
673 mono_domain_try_unload (domain, (MonoObject **) &exc);
675 /* Set back to our current domain while emitting AppDomainUnloadedEvent */
676 mono_domain_set (domain, TRUE);
677 appdomain_unloaded = true;
678 Emit (Deployment::AppDomainUnloadedEvent);
680 /* 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) */
681 pthread_mutex_lock (&hash_mutex);
682 g_hash_table_remove (current_hash, domain);
683 pthread_mutex_unlock (&hash_mutex);
685 /* Since the domain ptr may get reused we have to leave the root domain as the current domain */
686 mono_domain_set (root_domain, TRUE);
688 /* Clear out the domain ptr to detect any illegal uses asap */
689 /* CHECK: do we need to call mono_domain_free? */
690 domain = NULL;
692 if (exc) {
693 shutdown_state = ShutdownFailed;
694 fprintf (stderr, "Moonlight: Exception while unloading appdomain.\n"); // TODO: print exception message/details
695 break;
698 /* AppDomain successfully unloaded */
699 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): appdomain successfully unloaded.\n");
701 shutdown_state = DisposeDeployment;
702 /* fall through */
704 case DisposeDeployment: /* 3 */{
705 LOG_DEPLOYMENT ("Deployment::ShutdownManaged (): managed code has shutdown successfully, calling Dispose.\n");
706 Dispose ();
707 this->unref (); /* the ref taken in Shutdown */
708 return false;
712 return true; /* repeat the callback, we're not done yet */
714 #endif
716 Types*
717 Deployment::GetTypes ()
719 return types;
722 FontManager *
723 Deployment::GetFontManager ()
725 return font_manager;
728 Application*
729 Deployment::GetCurrentApplication ()
731 return current_app;
734 void
735 Deployment::SetCurrentApplication (Application* value)
737 if (current_app == value)
738 return;
740 if (current_app)
741 current_app->unref ();
743 current_app = value;
745 if (current_app)
746 current_app->ref ();
749 void
750 Deployment::RegisterDownloader (IDownloader *dl)
752 downloaders.Append (new IDownloaderNode (dl));
755 void
756 Deployment::UnregisterDownloader (IDownloader *dl)
758 IDownloaderNode *node = (IDownloaderNode *) downloaders.First ();
759 while (node != NULL) {
760 if (node->dl == dl) {
761 node->dl = NULL;
762 downloaders.Remove (node);
763 return;
765 node = (IDownloaderNode *) node->next;
769 void
770 Deployment::AbortAllDownloaders ()
772 downloaders.Clear (true);
775 class MediaNode : public List::Node {
776 private:
777 Media *media;
779 public:
780 MediaNode (Media *media)
782 this->media = media;
783 this->media->ref ();
785 void Clear (bool dispose)
787 if (media) {
788 if (dispose)
789 media->DisposeObject (media);
790 media->unref ();
791 media = NULL;
794 virtual ~MediaNode ()
796 Clear (true);
798 Media *GetMedia () { return media; }
801 bool
802 Deployment::RegisterMedia (EventObject *media)
804 bool result;
806 LOG_DEPLOYMENT ("Deployment::RegisterMedia (%p)\n", media);
808 medias_mutex.Lock ();
809 if (is_shutting_down) {
810 result = false;
811 } else {
812 if (medias == NULL)
813 medias = new List ();
814 medias->Append (new MediaNode ((Media *) media));
815 result = true;
817 medias_mutex.Unlock ();
819 return result;
822 void
823 Deployment::UnregisterMedia (EventObject *media)
825 MediaNode *node = NULL;
827 LOG_DEPLOYMENT ("Deployment::UnregisterMedia (%p)\n", media);
829 medias_mutex.Lock ();
830 if (medias != NULL) {
831 node = (MediaNode *) medias->First ();
832 while (node != NULL) {
833 if (node->GetMedia () == media) {
834 medias->Unlink (node);
835 break;
837 node = (MediaNode *) node->next;
840 medias_mutex.Unlock ();
842 /* Don't delete with the lock held, it may reenter and dead-lock */
843 if (node) {
844 node->Clear (false);
845 delete node;
849 void
850 Deployment::DisposeAllMedias ()
852 List *list;
854 medias_mutex.Lock ();
855 list = medias;
856 medias = NULL;
857 medias_mutex.Unlock ();
859 /* Don't delete with the lock held, it may reenter and dead-lock */
860 delete medias; /* the node destructor calls Dispose on the media */
862 MediaThreadPool::WaitForCompletion (this);
865 struct UnrefData {
866 EventObject *obj;
867 UnrefData *next;
870 gboolean
871 Deployment::DrainUnrefs (gpointer context)
873 Deployment *deployment = (Deployment *) context;
874 Deployment::SetCurrent (deployment);
875 deployment->DrainUnrefs ();
876 deployment->unref ();
877 Deployment::SetCurrent (NULL);
878 return false;
881 void
882 Deployment::DrainUnrefs ()
884 UnrefData *list;
885 UnrefData *next;
887 // Get the list of objects to unref.
888 do {
889 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
891 if (list == NULL)
892 return;
894 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, NULL));
896 // Loop over all the objects in the list and unref them.
897 while (list != NULL) {
898 list->obj->unref ();
899 next = list->next;
900 g_free (list);
901 list = next;
904 #if OBJECT_TRACKING
905 if (IsDisposed () && g_atomic_pointer_get (&pending_unrefs) == NULL && objects_destroyed != objects_created) {
906 printf ("Moonlight: the current deployment (%p) has detected that probably no more objects will get freed on this deployment.\n", this);
907 ReportLeaks ();
909 #endif
912 void
913 Deployment::UnrefDelayed (EventObject *obj)
915 UnrefData *list;
916 UnrefData *item;
918 #if SANITY
919 if (Deployment::GetCurrent () != this)
920 g_warning ("Deployment::UnrefDelayed (%p): The current deployment (%p) should be %p.\n", obj, Deployment::GetCurrent (), this);
921 if (obj->GetObjectType () != Type::DEPLOYMENT && obj->GetUnsafeDeployment () != this && obj->GetUnsafeDeployment () != NULL)
922 g_warning ("Deployment::UnrefDelayed (%p): obj's deployment %p should be %p. type: %s\n", obj, obj->GetUnsafeDeployment (), this, obj->GetTypeName ());
923 #endif
925 // Create the new list item
926 item = (UnrefData *) g_malloc (sizeof (UnrefData));
927 item->obj = obj;
929 // Prepend the list item into the list
930 do {
931 list = (UnrefData *) g_atomic_pointer_get (&pending_unrefs);
932 item->next = list;
933 } while (!g_atomic_pointer_compare_and_exchange (&pending_unrefs, list, item));
935 // If we created a new list instead of prepending to an existing one, add a idle tick call.
936 if (list == NULL) { // don't look at item->next, item might have gotten freed already.
937 g_idle_add (DrainUnrefs, this);
938 ref (); // keep us alive until we've processed the unrefs.
942 void
943 Deployment::TrackObjectCreated (EventObject *obj)
945 g_atomic_int_inc (&objects_created);
947 #if OBJECT_TRACKING
948 pthread_mutex_lock (&objects_alive_mutex);
949 if (objects_alive == NULL)
950 objects_alive = g_hash_table_new (g_direct_hash, g_direct_equal);
951 g_hash_table_insert (objects_alive, obj, GINT_TO_POINTER (1));
952 pthread_mutex_unlock (&objects_alive_mutex);
953 #endif
956 void
957 Deployment::TrackObjectDestroyed (EventObject *obj)
959 g_atomic_int_inc (&objects_destroyed);
961 #if OBJECT_TRACKING
962 pthread_mutex_lock (&objects_alive_mutex);
963 g_hash_table_remove (objects_alive, obj);
964 pthread_mutex_unlock (&objects_alive_mutex);
966 Track ("Destroyed", "");
967 #endif
970 bool
971 Deployment::IsLoadedFromXap ()
973 return is_loaded_from_xap;
976 void
977 Deployment::SetIsLoadedFromXap (bool flag)
979 is_loaded_from_xap = flag;
982 void
983 Deployment::SetXapLocation (const char *location)
985 g_free (xap_location);
986 xap_location = g_strdup (location);
989 const char*
990 Deployment::GetXapLocation ()
992 return xap_location;
995 void
996 Deployment::TrackPath (char *path)
998 paths.Append (new StringNode (path));
1001 void
1002 Deployment::UntrackPath (char *path)
1004 StringNode* node = (StringNode*) paths.Find (find_string, path);
1005 if (node) {
1006 g_free (node->str);
1007 paths.Remove (node);
1012 gint32
1013 Deployment::GetDeploymentCount ()
1015 return deployment_count;
1019 * AssemblyPart
1022 AssemblyPart::AssemblyPart ()
1024 SetObjectType (Type::ASSEMBLYPART);
1027 AssemblyPart::~AssemblyPart ()
1031 AssemblyPartCollection::AssemblyPartCollection ()
1033 SetObjectType (Type::ASSEMBLYPART_COLLECTION);
1036 AssemblyPartCollection::~AssemblyPartCollection ()
1041 * ExtensionPart
1044 ExtensionPart::ExtensionPart ()
1046 SetObjectType (Type::EXTENSIONPART);
1049 ExtensionPart::~ExtensionPart ()
1053 ExternalPart::ExternalPart ()
1055 SetObjectType (Type::EXTERNALPART);
1058 ExternalPart::~ExternalPart ()
1062 ExternalPartCollection::ExternalPartCollection ()
1064 SetObjectType (Type::EXTERNALPART_COLLECTION);
1067 ExternalPartCollection::~ExternalPartCollection ()
1071 /* OutOfBrowserSettings */
1072 OutOfBrowserSettings::OutOfBrowserSettings ()
1074 SetObjectType (Type::OUTOFBROWSERSETTINGS);
1077 OutOfBrowserSettings::~OutOfBrowserSettings ()