Nikola.Lukovic
Nikola.Lukovic

Reputation: 1325

Memory Mapped File gets deleted from memory

For some reason, when i read from a memory mapped file a couple of times it just gets randomly deleted from memory, i don't know what's going on. Is the kernel or GC deleting it from memory? If they are, how do i prevent them from doing so?

I am serializing an object to Json and writing it to memory.

I get an exception when trying to read again after a couple of times, i get FileNotFoundException: Unable to find the specified file.

private const String Protocol = @"Global\";

Code to write to memory mapped file:

public  static  Boolean                 WriteToMemoryFile<T>(List<T> data)
        {
            try
            {
                if (data == null)
                {
                    throw new ArgumentNullException("Data cannot be null", "data");
                }

                var mapName = typeof(T).FullName.ToLower();
                var mutexName = Protocol + typeof(T).FullName.ToLower();
                var serializedData = JsonConvert.SerializeObject(data);
                var capacity = serializedData.Length + 1;

                var mmf = MemoryMappedFile.CreateOrOpen(mapName, capacity);
                var isMutexCreated = false;
                var mutex = new Mutex(true, mutexName, out isMutexCreated);
                if (!isMutexCreated)
                {
                    var isMutexOpen = false;
                    do
                    {
                        isMutexOpen = mutex.WaitOne();
                    }
                    while (!isMutexOpen);
                    var streamWriter = new StreamWriter(mmf.CreateViewStream());
                    streamWriter.WriteLine(serializedData);
                    streamWriter.Close();
                    mutex.ReleaseMutex();
                }
                else
                {
                    var streamWriter = new StreamWriter(mmf.CreateViewStream());
                    streamWriter.WriteLine(serializedData);
                    streamWriter.Close();
                    mutex.ReleaseMutex();
                }
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

Code to read from memory mapped file:

public  static  List<T>                     ReadFromMemoryFile<T>()
        {
            try
            {
                var mapName = typeof(T).FullName.ToLower();
                var mutexName = Protocol + typeof(T).FullName.ToLower();

                var mmf = MemoryMappedFile.OpenExisting(mapName);
                var mutex = Mutex.OpenExisting(mutexName);
                var isMutexOpen = false;
                do
                {
                    isMutexOpen = mutex.WaitOne();
                }
                while (!isMutexOpen);

                var streamReader = new StreamReader(mmf.CreateViewStream());
                var serializedData = streamReader.ReadLine();
                streamReader.Close();
                mutex.ReleaseMutex();
                var data = JsonConvert.DeserializeObject<List<T>>(serializedData);
                mmf.Dispose();
                return data;
            }
            catch (Exception ex)
            {
                return default(List<T>);
            }

        }

Upvotes: 7

Views: 3596

Answers (2)

Elie03
Elie03

Reputation: 1

Clearly, the problem is that the MMF loose its context as explained by Luaan. But still nobody explains how to perform it:

  1. The code 'Write to MMF file' must run on a separate async thread.
  2. The code 'Read from MMF' will notify once read completed that the MMF had been read. The notification can be a flag in a file for example.

Therefore the async thread running the 'Write to MMF file' will run as long as the MMF file is read from the second part. We have therefore created the context within which the memory mapped file is valid.

Upvotes: 0

Luaan
Luaan

Reputation: 63742

The process that created the memory mapped file must keep a reference to it for as long as you want it to live. Using CreateOrOpen is a bit tricky for exactly this reason - you don't know whether disposing the memory mapped file is going to destroy it or not.

You can easily see this at work by adding an explicit mmf.Dispose() to your WriteToMemoryFile method - it will close the file completely. The Dispose method is called from the finalizer of the mmf instance some time after all the references to it drop out of scope.

Or, to make it even more obvious that GC is the culprit, you can try invoking GC explicitly:

WriteToMemoryFile("Hi");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();  
ReadFromMemoryFile().Dump(); // Nope, the value is lost now

Note that I changed your methods slightly to work with simple strings; you really want to produce the simplest possible code that reproduces the behaviour you observe. Even just having to get JsonConverter is an unnecessary complication, and might cause people to not even try running your code :)

And as a side note, you want to check for AbandonedMutexException when you're doing Mutex.WaitOne - it's not a failure, it means you took over the mutex. Most applications handle this wrong, leading to issues with deadlocks as well as mutex ownership and lifetime :) In other words, treat AbandonedMutexException as success. Oh, and it's good idea to put stuff like Mutex.ReleaseMutex in a finally clause, to make sure it actually happens, even if you get an exception. Thread or process dead doesn't matter (that will just cause one of the other contendants to get AbandonedMutexException), but if you just get an exception that you "handle" with your return false;, the mutex will not be released until you close all your applications and start again fresh :)

Upvotes: 4

Related Questions