[.net] Good use for AppDomains and Contexts?

Started by
6 comments, last by VizOne 19 years, 8 months ago
I just finished the part in my book that goes over them and I can't see why you would want to use them. Can somebody inform me more on them?
Advertisement
Application domains provide isolation, unloading, and security boundaries for executing managed code.

and to quote MSDN:

Quote:
Application domains provide a secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. You can run several application domains in a single process with the same level of isolation that would exist in separate processes, but without incurring the additional overhead of making cross-process calls or switching between processes. The ability to run multiple applications within a single process dramatically increases server scalability.


For more information, see:
Application Domains

Also see:
Chris Brumme on Application Domains

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

In my game engine I have a scripting-host (yes, what everyone does: use built-in compilers for C#/VB.NET/JScript.NET) that runs within a separate "sandbox"-AppDomain. The scripts can call back into the (fully trusted) main AppDomain to execute engine/game-specific features.

This has two major advantages:
a) I grant very few rights to the sandbox preventing malicious scriptcode
b) I can unload all script-dlls loaded into the domain (e.g. for one level) by unloading the domain.

There are only two disadvantages:
- cross AppDomain calls are slow (~100 times slower than direct calls). I'm trying to optimize this currently, e.g. instead of calling a Player.X-property a hundred times remotely I copy the current player data to the scriptdomain before invoking the script and copy all changes back to the original domain.
- higher memory usage as all used assemblies have to be loaded twice.


Regards,
Andre
Andre Loker | Personal blog on .NET
Quote:Original post by VizOne
In my game engine I have a scripting-host (yes, what everyone does: use built-in compilers for C#/VB.NET/JScript.NET) that runs within a separate "sandbox"-AppDomain. The scripts can call back into the (fully trusted) main AppDomain to execute engine/game-specific features.

This has two major advantages:
a) I grant very few rights to the sandbox preventing malicious scriptcode
b) I can unload all script-dlls loaded into the domain (e.g. for one level) by unloading the domain.


A prefect example of why to use AppDomains

Quote:
There are only two disadvantages:
- cross AppDomain calls are slow (~100 times slower than direct calls). I'm trying to optimize this currently, e.g. instead of calling a Player.X-property a hundred times remotely I copy the current player data to the scriptdomain before invoking the script and copy all changes back to the original domain.

Sounds like you may be doing something odd. AppDomains shouldn't incure such a heavy cost. I will point out that mscorlib is in another assembly, and it doesn't incure anywhere near that cost.
Quote:
- higher memory usage as all used assemblies have to be loaded twice.

Again, unless of course you are loading your assemblies twice. But in that case, consider placing your shared code in a domain neutral assembly, or ngen them.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Quote:Original post by Washu
Sounds like you may be doing something odd. AppDomains shouldn't incure such a heavy cost. I will point out that mscorlib is in another assembly, and it doesn't incure anywhere near that cost.
[...]
Again, unless of course you are loading your assemblies twice. But in that case, consider placing your shared code in a domain neutral assembly, or ngen them.


Well, yes, I'll be doing some optimization on this.

BTW, I have a second nice usage of app-domains: my plug-in system. To scan for plug-ins, I create a remote object in an additional domain. The remove-scanner checks for plug-ins e.g. in all assemblies within a given directory. This of course causes all assemblies to be loaded, regardless whether I'll use them later or not. So, I gather information on the plug-ins and pass them back to the main domain (import: I only transfer strings etc., not Type/Assembly etc. instances - they would cause the concerning assemblies to "bleed" into the main domain). The user can then pick all the plug-ins he/she/the app needs. The assemblies of those plug-ins are loaded into the main domain while the scanner-domain is dropped and therefore all unused assemblies are removed from memory.

Regards,
Andre
Andre Loker | Personal blog on .NET
I have done another measuring now and was very surprised to find remote calls to be even 1000x slower.

Here's my test-code
public class Calculator : MarshalByRefObject{    public long Add(long a, long b)    {        return a + b;    }}public class Apl{    static void Main()    {        // profiling stuff        Profiler.Init(new Timer()); // the profiler        ProfilerForm profForm = new ProfilerForm(); // form that presents profiler results        profForm.Show();        IEventDispatcher disp = new ThinEventDispatcher(); // a thin version of Application.DoEvents();            // local instance        Calculator local = new Calculator();        // remote instance        AppDomain domain = AppDomain.CreateDomain("Remote Calculator");        Calculator remote = domain.CreateInstanceAndUnwrap(typeof(Calculator).Assembly.FullName,            typeof(Calculator).FullName) as Calculator;        const int LoopCount = 1000; // number of calls per run        while(profForm.Created)        {            // local calls            long value1 = 0;            Profiler.BeginSample("Local");            for(int i = 0; i < LoopCount; ++i)            {                value1 = local.Add(value1, i);            }            Profiler.EndSample();            // remote calls            long value2 = 0;            Profiler.BeginSample("Remote");            for(int i = 0; i < LoopCount; ++i)            {                value2 = remote.Add(value2, i);            }            Profiler.EndSample();            // result should be same            if(value1 != value2)                Console.WriteLine("Mismatch - value 1: {0}, value 2: {1}", value1.ToString(), value2.ToString());            // dispatch form-events.            disp.DoEvents();        }        // clean-up        AppDomain.Unload(domain);        Profiler.Shutdown(true);    }}


"Profiler" is a profiling class I wrote, ThinEventDispatcher is a small wrapper around PeekMessage/TranslateMessage/Dispatchmessage.

A put 1000 calls per sample-run to miminize influence of the profiler itself (which is very small, however).

Here's the result after ~1000000 calls (don't mind the number-format - I'm running a German version of .NET :) ):



Any idea why it is that slow?

Regards,
Andre
Andre Loker | Personal blog on .NET
This is extremely simple to answer.

See, using a remote object (and remoting is used, even with an optimised stack) means you go through a PROXY. The Proxy has to pack up the request into a MESSAGE, pass it out to the real object (in the other appdomain), get the result data back and construct a method call out of the returned data.

All this inquires a LOT of stuff that is slow, includes some string manuipulation etc.etc.etc. - on both ends.

For security only - you CAN limit code rights without a separate appdomain, even only for certain call paths (i.e. whenever you call into the scripting code). But when you need unloading, live with it. Program for it.
RegardsThomas TomiczekTHONA Consulting Ltd.(Microsoft MVP C#/.NET)
Quote:Original post by thona
This is extremely simple to answer.

See, using a remote object (and remoting is used, even with an optimised stack) means you go through a PROXY. The Proxy has to pack up the request into a MESSAGE, pass it out to the real object (in the other appdomain), get the result data back and construct a method call out of the returned data.

All this inquires a LOT of stuff that is slow, includes some string manuipulation etc.etc.etc. - on both ends.

Yes I know, I was only wondering because Washu said it would not be that slow.

Quote:
For security only - you CAN limit code rights without a separate appdomain, even only for certain call paths (i.e. whenever you call into the scripting code).

Actually that did not work for me. Let's imagine I want to support a screenshot functionality callable by scripts. In general I want to forbid file I/O code in scripts, so I provide a Screenshot() function in the game/engine domain. The script domain is not allowed to Assert() a permission to prevent malicious code, the maindomain however is allowed to Assert() the File I/O and therefore is able to write to a file although a caller (the script) has not the necessary rights.

I cannot think of a solution how this would work with only one domain, neither with imperative CAS nor by restricting domain permissions. Either both the script and the engine are allowed to Assert() rights (bad for security) or neither are allowed (limiting functionality). That's why I'll stick to two separate domains.


Quote:But when you need unloading, live with it. Program for it.

That's what I'll do ;) Thanks for your reply (and sorry to the OP for hijacking his thread *g*).

Regards,
Andre
Andre Loker | Personal blog on .NET

This topic is closed to new replies.

Advertisement