AppDomain address space
Asked Answered
F

2

16

First, the question: do CLR specifications guarantee that the code executing in multiple app domains within the same process will share the same address space? By "sharing the address space" I mean that pointers to memory allocated in one of the app domains will be valid for reading and writing across all app domains hosted inside the same process.

Consider this self-contained example illustrating the question: the program allocates a Worker object in a separate app domain. The Worker allocates a memory block for 10,000 integers, and fills it in with data. The program then calls across the app domain boundary to obtain the pointer to the allocated block, and verifies that it can read every one of the 10,000 items.

using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace crossapp {
    public class Worker : MarshalByRefObject {
        private readonly IntPtr myData;
        public const int DataLength = 10000;
        public Worker() {
            Console.Error.WriteLine(
                "Memory allocation happens in app domain '{0}'"
            ,   Assembly.GetExecutingAssembly().FullName
            );
            myData = Marshal.AllocHGlobal(sizeof(int) * DataLength);
            unsafe {
                var ptr = (int*) myData.ToPointer();
                for (var i = 0 ; i != DataLength ; i++) {
                    ptr[i] = 2*i + 1;
                }
            }
        }
        public IntPtr GetData() {
            return myData;
        }
    }
    class Program {
        static void Main() {
            var ad = AppDomain.CreateDomain("New domain");
            var wrk = (Worker)ad.CreateInstanceAndUnwrap(
                Assembly.GetExecutingAssembly().FullName
            ,   "crossapp.Worker"
            );
            var data = wrk.GetData();
            var badCount = 0;
            unsafe {
                var ptr = (int*)data.ToPointer();
                for (var i = 0 ; i != Worker.DataLength ; i++) {
                    var expect = 2*i + 1;
                    if (ptr[i] != expect) {
                        Console.Error.WriteLine(
                            "Mismatch in position {0}: {1} != {2}"
                        ,   i, expect, ptr[i]
                        );
                        badCount++;
                    }
                }
                if (badCount == 0) {
                    Console.Error.WriteLine(
                        "All {0} items have matched."
                    ,   Worker.DataLength
                    );
                } else {
                    Console.Error.WriteLine(
                        "Found {0} mismatches out of {1}."
                    ,   badCount
                    ,   Worker.DataLength
                    );
                }
            }
        }
    }
}

I ran this many times, and it worked every single time. Intuitively it should work: after all, app domains are within a single process, so they must share the same virtual address space. However, this feels like an exploit of a feature that Microsoft may take away at any time. Is there something in the specification of CLR that confirms or denies legitimacy of this trick?


In case you are wondering why I am asking such a strange question, I am looking for a way of passing large (in gigabytes) amounts of data across app domain boundary, with minimal overhead in both space and time. This would be my ideal solution if I could prove its legitimacy.
Fordo answered 20/8, 2012 at 20:28 Comment(2)
Marshal.AllocHGlobal doco reads: Allocates memory from the unmanaged memory of the process. This would seem to indicate that your pointers created in this manner are cross-appdomain safe as long as the appdomains exist in the same process.Gish
@downvoter What's wrong with the question to earn a downvote?Fordo
J
1

Look at: Anyone can explain the major use of MarshalByRefObject . In your scenario, you are only passing a proxy and not the actual object and the memory is not being copied.

EDIT:

  1. "legitimacy" != "trick", your hack is undermining AppDomains by not marshaling calls to data in another AppDomain. The run-time container may set/change security limitations which might break your app, e.g. is this running inside IIS? (In your case, you are not accessing an object but memory, so it is perhaps "not so bad".) Is this a product your are deploying at a customer's site?
  2. I assume that there was a performance problem with marshaling via proxy, so you resorted to IntPtr (cut out the middle man)
  3. Are the various "worker" AppDomains manipulating the blob? If so, I would be concerned that eventually the memory would be corrupted... because you are not marshaling your calls.
  4. This is unmanaged memory created in C#. If in reality, the blob is allocated by an unmanaged DLL, then you must ensure that the unmanaged DLL is not unloaded. Again, if are deploying to IIS, then you don't control your AppDomains' lifecycles, IIS does. This will break your hack.
  5. Yes, virtual memory is per process and not per AppDomain, so all AppDomain share the same virtual address space.
  6. Regardless of my reservations, it is very cool :)
Jose answered 14/10, 2013 at 13:58 Comment(3)
I did not suggest that the memory would be or should be copied. I am passing back a pointer, and it works as if it points to the same memory. My question is, is there a part of the standard that guarantees that this is what would happen?Fordo
You are not passing a "pointer", you are passing a proxy. It looks a lot like the CORBA "model": you are passing a proxy that marshals commands to an object in another AppDomain.Jose
What gets passed through the call (that does use a proxy) is a plain pointer. Look at the first line of the second unsafe block to see how it gets used. The fact that the pointer is passed using the proxy is not important: once the pointer has been passed, I access the remaining data through that pointer, bypassing the proxy, and it works. I am looking for a proof that this is not a coincidence.Fordo
G
-1

I have no direct answer for you. The existance of MarshalByRefObject might indicate that a common address space is used, but perhaps not.

You might also look into memory-mapped files

Gish answered 20/8, 2012 at 20:47 Comment(2)
The documentation of the MarshalByRefObject says at some point that "the members of the object are not usable outside the application domain where they were created", and it also says that they return "a proxy". This suggests to me that Microsoft either does some non-trivial mapping, or reserves the right to do some non-trivial mapping in the future. As for the memory-mapped files, they are heavier than the "plain" memory, because they must work across process boundaries. On top of that, there's no file: I need to pass pointers to chunks of transient data.Fordo
I had not read that part of the documentation. As for the memorymapped file, I was thinking you could first pin the memory, then write the pointer to the mmf. But on further thought, you could just pin the memory and then use your existing pointer passing function.Gish

© 2022 - 2024 — McMap. All rights reserved.