Sign in to follow this  
Ekim_Gram

[.net] Good use for AppDomains and Contexts?

Recommended Posts

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this