0

I'm trying to create a .NET Core application and am getting a little stuck on IPC.

I have data coming in (let's say, from a socket, or a file, some sort of streaming interface), via executable 1. Now, I want this data to be read by executable 2. So, I've created a MMF, where executable 1 writes data, and executable 2 reads data. All is well.

However, I would really like to skip the "copy" step here. If I have a message coming in via a socket, I need to read the message (ergo; store it in some byte array), and then copy it to the appropriate Memory Mapped File.

Is there a way (especially now, with the new Memory, Span etc) to have them use the same memory?

This code almost seems to work, but not completely:

const int bufferSize = 1024;

var mappedFile = MemoryMappedFile.CreateNew("IPCFile", bufferSize);
var fileAccessor = mappedFile.CreateViewAccessor(0, bufferSize, MemoryMappedFileAccess.ReadWrite);

// THere now exists a region of byte[] * bufferSize somewhere. I want to write to that.

byte[] bufferMem = new byte[bufferSize];

// I know the memory address where this area is, and I know the size of it:
unsafe
{
    byte* startOfRegion = (byte*)0;
    fileAccessor.SafeMemoryMappedViewHandle.AcquirePointer(ref startOfRegion);
    // But how do I "assign" this region to the managed object?

    // This throws "System.MissingMethodException: 'No parameterless constructor defined for type 'System.Byte[]'."
    //bufferMem = Marshal.PtrToStructure<byte[]>(new IntPtr(startOfRegion));

    // This almost looks like it works, but bufferMem remains null. Does not give any errors though.
    bufferMem = Unsafe.AsRef<byte[]>(startOfRegion);
}

// For a shorter example, just using a file stream
var incomingData = File.OpenRead(@"C:\Temp\plaatje.png");

// Pass in the "reserved" memory region. But StreamPipeReaderOptions wants a MemoryPool<byte>, not a byte[].
// How would I cast that? MemoryPool is abstract, so can't even be instantiated
var reader = PipeReader.Create(incomingData, new StreamPipeReaderOptions(bufferMem));
ReadResult readResult;

// Actually read data
while (true)
{
    readResult = await reader.ReadAsync();
    if (readResult.IsCompleted || readResult.IsCanceled)
        break;

    reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
}

// Now signal the other process to read the contents of the MMF and continue

This question seems to ask a similar thing, but does not have an answer, and is from 2013.

Rainmaker
  • 173
  • 3
  • 13
  • You can copy directly from the socket buffer to the MMF mapping. I don't think you'd *want* to make the socket buffer be the same memory region as the MMF mapping, even if that were possible. – Stephen Cleary Dec 28 '19 at 02:10
  • Yes, copying works. I would however like to have it zero-copy to limit the memory and GC cycles. Why wouldn't I want to expose the same region? As long as I manually control access to it with a mutex or something, shouldn't this "work"? – Rainmaker Dec 28 '19 at 07:37
  • Socket read buffers can be partially filled, so you'd need some kind of control channel where exe 2 can know how much of the buffer has valid data, and then let exe 1 know it can issue the next read. You'd end up reimplementing the sockets API using shared memory - very confusing, and it's not clear at that point why exe 1 would exist at all. – Stephen Cleary Dec 28 '19 at 22:02
  • Yes, that's exactly what I'd like to achieve. Signalling is easy with a WaitEventHandle, so I can let exe 2 know there's data to be read. The reason why there are multiple executables is so that I can only have 1 process that handles the network code. This means there's always only 1 process having listening ports, to limit the amount of configuration that needs to be done in a software firewall. I.e.: the windows firewall would only need to allow this one executable. The application is intended to be run together with production apps, so I'd like to limit the impact of it as much as possible. – Rainmaker Dec 29 '19 at 08:48

0 Answers0