Jun 19, 2010 at 10:28 PM
Edited Jun 19, 2010 at 10:31 PM
A separated AppDomain works well for that purpose, indeed. And it was surprisingly easy to setup and test... but then trouble came.
1. Having a second AppDomain was indeed a no brainer. As was restricting its permissions.
2. Have your host assembly being fully trusted in the 2nd AppDomain requires a strong name. Which is relatively easy, except it means all your assembly references have to be fully trusted as well, which may be more annoying if they are not.
This caused me some trouble... and in the end I realized that I didn't need my host being fully trusted in the 2nd domain! Everything happens in my host's own AppDomain :-(
3. On the other hand, you have to put the APTCA attribute on your host assembly to accept incoming calls from transparent script AppDomain. And you have to put it inside your referenced libraries as well, apparently. (don't quite get this one, but I get
4. Make the host objects exposed in the scripting environment inherit from MarshalByRefObject. This may be annoying if you need another parent class. In my case it's OK, but it took me some time to realize that those class also have to be public. I know
it's obvious but it took me a long time to figure out why none of my ("public") properties were available in Python. The Python runtime isn't the same as your host assembly! So it respects your classes being internal...
Now I have 2 major problems left. I'll expose the one related to the isolation in a second AppDomain here, and open another thread for the 2nd one.
.NET uses MarshalByRefObject to pass the shared objects between my host and scripts accross AppDomains (Serializable isn't an option, the instances have to be connected with the backing data in the host). But MarshalByRefObject means remoting, which means
Lifetime management. And that's just a big mess. It comes with a default 6 minutes time out, which of course isn't appropriate. Then there are Sponsors, but they are complicated and I'm not even sure how to do this correctly...
Of course, my use-case is simple. A "simple" cross-AppDomain garbage collection is all I need. My host MBR (MarshallByRef) objects should live as long as the script scope holds a reference to them. No less (otherwise when the user uses the console
she gets unexplicable errors), and no more (I don't want to leak memory and may be creating many of MBR during scripts execution). If the scope has permanent (globals) references, they should be kept alive until the scope itself is dimissed. That's a simple
GC scenario, but apparently the GC doesn't work across AppDomains, even in the same Process.
I should I solve this issue? It's seems to me that no time-based expiration is reasonable. Keeping things alive forever isn't good either because I would be leaking a lot of memory. I suppose the solution is going to involve some kind of Sponsor implementation,
but how should that work? How can the sponsor now if the scope still has a reference to the object itself? And what happens to the Sponsor if the scope is discarded? I think the Addin team used the MBR as its own sponsor object, that may be an idea to dig
Is there a solution built-in the DLR? This looks like a reasonably common request and the DLR has bult-in support for running in a remote AppDomain, so maybe it has something to help with MBR? I couldn't find anything using Google...