Search code examples
c#linuxmonoipcmemory-mapped-files

C# (mono) Linux Memory Mapped Files - Shared Memory (multiple processes)


I want to implement the fastest possible Inter-Process Communication (IPC) (least CPU bound) between 2 .NET Core (or even mono possible) applications on Linux (SBC).

I tried TPC (socket, loopbacks) and anonymous/named pipes which are way too slow. Now I am testing MemoryMappedFiles (shared memory) and I am observing kind of strange behavior.

The next code works for me fine:

static async Task Main(string[] args)
{
    var are = new AutoResetEvent(false);
    var masterTask = Task.Run(async () =>
    {
        using (var memoryMappedFile = MemoryMappedFile.CreateNew("test", 100_000))
        {
            using (var memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor())
            {
                 are.Set();
                 for (int i = 0; i < 100; i++)
                 {
                     Console.WriteLine($"Master: {i}");
                     memoryMappedViewAccessor.Write(0, i);
                     await Task.Delay(1000);
                 }
            }
        }
    });
    are.WaitOne();
    var slaveTask = Task.Run(async () =>
    {
        using (var memoryMappedFile = MemoryMappedFile.OpenExisting("test"))
        {
             using (var memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor())
             {
                 int number;
                 do
                 {
                     number = memoryMappedViewAccessor.ReadInt32(0);
                     Console.WriteLine($"Slave: {number}");
                     await Task.Delay(1000);
                 }
                 while (number < 99);
             }
        }
   });

   await Task.WhenAll(masterTask, slaveTask);

   Console.WriteLine("...");
   Console.ReadKey();
}

But when I split it into 2 applications:

static async Task Main(string[] args)
{
    using (var memoryMappedFile = MemoryMappedFile.CreateNew("test", 100_000))
    {
        using (var memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor())
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine($"Master: {i}");
                memoryMappedViewAccessor.Write(0, i);
                await Task.Delay(1000);
            }
        }
    }
    Console.WriteLine("...");
    Console.ReadKey();
}

and

static async Task Main(string[] args)
{
    using (var memoryMappedFile = MemoryMappedFile.OpenExisting(mapName: "test"))
    {
        using (var memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor())
        {
            int number;
            do
            {
                number = memoryMappedViewAccessor.ReadInt32(0);
                Console.WriteLine($"Slave: {number}");
                await Task.Delay(1000);
            }
            while (number < 99);
        }
    }
    Console.WriteLine("...");
    Console.ReadKey();
}

the second "client" app return:

Unhandled Exception: System.IO.FileNotFoundException: at System.IO.MemoryMappedFiles.MemoryMapImpl.OpenFile (System.String path, System.IO.FileMode mode, System.String mapName, System.Int64& capacity, System.IO.MemoryMappedFiles.MemoryMappedFileAccess access, System.IO.MemoryMappedFiles.MemoryMappedFileOptions options) [0x00065] in :0

Why is it? Could I somehow let the second app see the resources of the first one?

(Note: Not sure 100% but I can bet I was able to run such 2 apps previously - several years ago - on mono - I wish it was true and there is a way).

Another thought brings me to the idea of whether there is any C++ tool to do so, that can be wrapped from c# to do so. Is it possible?


Solution

  • We are f....d as developers guys:) https://chat.openai.com/chat helped me to solve this promptly.

    I.e. on Linux, there is a folder /dev/shm where all MMF files are stored (some kind of RAM disk - tmpfs). So modify first process

    MemoryMappedFile.CreateFromFile("/dev/shm/test", System.IO.FileMode.OpenOrCreate, "test", 100_000)

    and the second to

    MemoryMappedFile.CreateFromFile("/dev/shm/test", FileMode.Open)

    and voilá. And it is incredibly fast in data volume transition from one process to another.