Skip to content

Inception and .Net AppDomains

January 3, 2011

Inception was recently released on BluRay and DVD. If you haven’t seen this psychological techno-thriller yet, I highly recommend it (actually I highly recommend any of Christopher Nolan’s work – he continues to hit each movie he’s involved with out of the park as far as I’m concerned). I was recently doing some extension work to a  C# project I wrote a while ago and it reminded me of Inception for some reason. Here’s the deal:

A while ago I wrote a short program to demonstrate and explore a pattern of “completely-unloadable” plugin hosting within an application. I had a project where I needed to be able to completely unload a plugin class (such that I could potentially load a different version of the plugin or load another instance from a different location), and the plugin interface (which I did not control) exposed custom status and result types on its members such that if the plugin interface was referenced these other types were referenced and loaded by mere association. Once an assembly is loaded into an AppDomain it cannot be unloaded – the containing AppDomain must be unloaded in order to truly “release” it. So in this case in order to completely unload the assembly in .Net I had to isolate it within a couple of nested AppDomains because each assembly would be loaded into any AppDomain which makes reference to its contained types, even if they are never invoked. In a nutshell, this means I needed to have a proxy class between my application and the plugin I was loading which does not expose any types specific to the plugin so that the plugin assembly was never loaded into the hosting app’s own AppDomain.

To demonstrate this, assume the following layout:

InceptionApplication.exe assembly contains:

InceptionApplication class

  • void Main(string[] arguments) –> Basically this just contains a top-level try/catch and calls Run()
  • void Run(string[] arguments) –> Main program loop, which eventually calls into RunPlugin()
  • void RunPlugin(string typeName, string assemblyPath, string[] arguments) –> Instantiates, invokes, and handles input to/output from a plugin.
Inception.PluginSupport.dll assembly contains:

PluginProxy class

  • PluginProxy(string typeName, string assemblyPath, string[] arguments)
  • void Run(string[] arguments)
Interfaces.Plugin.dll assembly contains:

enum PluginResult { Unknown, Failure, Success }

IPlugin interface

  • PluginResult Run(string[] pluginArguments);
CustomPlugins.dll assembly contains:

MyPlugin class, implementing IPlugin

Inception.exe AppDomain content “ProxyDomain” content “PluginDomain” content  
InceptionApplication (Inception.exe)      
  PluginProxy (Inception.PluginSupport.dll)    
    IPlugin (Interfaces.Plugin.dll)  
    PluginResult (Interfaces.Plugin.dll)  
    MyPlugin (CustomPlugins.dll)  
  IPlugin (Interfaces.Plugin.dll)    
  PluginResult (Interfaces.Plugin.dll)    
  MyPlugin (CustomPlugins.dll)    
PluginProxy (Inception.PluginSupport.dll)      

 

Application (and AppDomain) Lifecycle

1) InceptionApplication loads Inception.PluginSupport assembly into its own AppDomain to get a handle to the PluginProxy type.

Inception.PluginSupport is loaded into InceptionApplication’s AppDomain

2) InceptionApplication creates an instance of PluginProxy in a new child AppDomain “ProxyDomain”

New AppDomain “ProxyDomain” is created

Inception.PluginSupport is loaded into AppDomain “ProxyDomain”

3) PluginProxy instance loads Interfaces.Plugin assembly because of reference to the IPlugin interface.

Interfaces.Plugin is loaded into PluginProxy’s AppDomain, “ProxyDomain”

4) PluginProxy instance loads CustomPlugins assembly to get a handle to the MyPlugin type implementing IPlugin.

CustomPlugins is loaded into PluginProxy’s AppDomain, “ProxyDomain”

5) PluginProxy creates an instance of MyPlugin in a new child AppDomain “PluginDomain”

New AppDomain “PluginDomain” is created

CustomPlugins is loaded into this child AppDomain “PluginDomain”

Interfaces.Plugin is loaded into this child AppDomain “PluginDomain” because of reference to IPlugin interface

6) PluginProxy invokes Run() method of MyPlugin instance.

7) MyPlugin executes and returns a PluginResult instance to PluginProxy as a result.

8) PluginProxy unloads the AppDomain containing MyPlugin instance (“PluginDomain”)

“PluginDomain” AppDomain is destroyed

CustomPlugins assembly is still present in PluginProxy’s AppDomain (“ProxyDomain”)

Interfaces.Plugin assembly is still present in PluginProxy’s AppDomain (“ProxyDomain”)

9) PluginProxy.Run() completes and returns control to InceptionApplication

10) InceptionApplication unloads the AppDomain containing PluginProxy instance

“ProxyDomain” AppDomain is destroyed

CustomPlugins assembly is completely released (no further references)

Interfaces.Plugin assembly is completely released (no further references)

Inception.PluginSupport assembly is still present in InceptionApplication’s AppDomain

Anyway, after all this nesting of AppDomains I was experiencing flashbacks while watching the movie Inception – only remaining question is if code nested within an AppDomain nested within another AppDomain means everything runs slower… I think probably so…

Advertisements

From → C#, Programming

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: