Search code examples
c#.netshared-memorymemory-mapped-filescer

Can a managed process get terminated while writing to shared memory?


I have several (managed / .NET) processes communicating over a ring buffer which is held in shared memory via the MemoryMappedFile class (just memory no file mapped). I know from the SafeBuffer reference source that writing a struct to that memory is guarded by a CER (Constrained Execution Region) but what if the writing process gets abnormally terminated by the OS while doing so? Can it happen that this leads to the struct being written only partially?


    struct MyStruct
    {
      public int A;
      public int B;
      public float C;
    }

    static void Main(string[] args)
    {
      var mappedFile = MemoryMappedFile.CreateOrOpen("MyName", 10224);
      var accessor = mappedFile.CreateViewAccessor(0, 1024);
      MyStruct myStruct;
      myStruct.A = 10;
      myStruct.B = 20;
      myStruct.C = 42f;
      // Assuming the process gets terminated during the following write operation.
      // Is that even possible? If it is possible what are the guarantees   
      // in regards to data consistency? Transactional? Partially written?
      accessor.Write(0, ref myStruct); 
      DoOtherStuff(); ...
    }

It is hard to simulate / test whether this problem really exists since writing to memory is extremly fast. However, it would certainly lead to a severe inconsistency in my shared memory layout and would make it necessary to approach this with for example checksums or some sort of page flipping.

Update:

Looking at Line 1053 in

https://referencesource.microsoft.com/#mscorlib/system/io/unmanagedmemoryaccessor.cs,7632fe79d4a8ae4c

it basically comes down to the question whether a process is protected from abnormal termination while executing code in a CER block (having the Consistency.WillNotCorruptState flag set).


Solution

  • Yes a process can be stopped at any moment.

    The SafeBuffer<T>.Write method finally calls into

    [MethodImpl(MethodImplOptions.InternalCall)]
    [ResourceExposure(ResourceScope.None)]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    private static extern void StructureToPtrNative(/*ref T*/ TypedReference structure, byte* ptr, uint sizeofT);
    

    which will do basically a memcpy(ptr, structure, sizeofT). Since unaligned writes are never atomic except for bytes you will run into issues if your process is terminated in the middle while writing a value.

    When a process is terminated the hard way via TerminateProcess or an unhandled exception no CERs or something related is ever executed. There is no graceful managed shutdown happening in that case and your application can be stopped right in the middle of an important transaction. Your shared memory data structures will be left in an orphaned state and any locks you might have taken will return the next waiter in WaitForSingleObject WAIT_ABANDONED. That way Windows tells you that a process has died while it had taken the lock and you need to recover the changes done by the last writer.