title: C# call to optimized C++ dll function behavior
body: <p>After a week long debugging session and google-ing the problem, I asked my friend for his opinion and luckily he had a similar problem so he pinpointed me towards something which lead me to figure out where the problem is and in the end I managed to find a stable (I hope) fix to it but I'm still unsure what the problem is (I have few guesses but later on that).</p>
  <p>First of all, I have an unmanaged C++ dll which I use from C#. The infrastructure (this is a dummy solution I recreated just to recreate and debug the problem I had without other code colliding) looks like this:</p>
  <p><strong>C++ code:</strong></p>
  <pre><code>    ===== device.h: =====
  
  #pragma once
  
  #include &lt;cinttypes&gt;
  
  namespace abstraction
  {
      class device final
      {
          public: uint32_t activity;
          public: uint32_t activities;
          
          public: device(uint32_t limit);
          
          public: bool execute();
      };
  }
  
      ===== device.cpp =====
  
  #include &quot;device.h&quot;
  
  namespace abstraction
  {
      device::device(uint32_t limit)
      {
          activity = 1u;
          activities = limit;
      }
  
      bool device::execute()
      {
          if (activity &gt;= activities) return false;
  
          ++activity;
  
          return true;
      }
  }
  </code></pre>
  <p><strong>C++ dll API:</strong></p>
  <pre><code>        ===== api.h: =====
  
  #pragma once
  
  #include &lt;abstraction/device.h&gt; // it's the device above
  
  #ifdef __TEST_API__
      #define __dll __declspec(dllexport)
  #else
      #define __dll __declspec(dllimport)
  #endif
  
  #define dynamic extern &quot;C&quot; __dll
  
  struct __dll Hndl
  {
      uint64_t ptr;
  };
  
  // RC = ReinterpretCast
  template &lt;typename T&gt; inline Hndl RC(T* obj) { return { reinterpret_cast&lt;uint64_t&gt;(obj) }; }
  template &lt;typename T&gt; inline Hndl RC(T&amp; obj) { return RC(&amp;obj); }
  template &lt;typename T&gt; inline T* RC(Hndl hndl) { return reinterpret_cast&lt;T*&gt;(hndl.ptr); }
  
  // ===== Device API =====
  dynamic Hndl CreateDevice(uint32_t limit);
  dynamic void DisposeDevice(Hndl _device);
  dynamic uint32_t DeviceActivity(Hndl _device);
  dynamic uint32_t DeviceActivities(Hndl _device);
  dynamic bool DeviceExecute(Hndl _device);
  
          ===== api.cpp: =====
  
  #include &quot;api.h&quot;
  
  using namespace abstraction;
  
  // ===== Device API =====
  dynamic Hndl CreateDevice(uint32_t limit)
  {
      return RC(new device(limit));
  }
  dynamic void DisposeDevice(Hndl _device)
  {
      delete RC&lt;device&gt;(_device);
  }
  dynamic uint32_t DeviceActivity(Hndl _device)
  {
      return RC&lt;device&gt;(_device)-&gt;activity;
  }
  dynamic uint32_t DeviceActivities(Hndl _device)
  {
      return RC&lt;device&gt;(_device)-&gt;activities;
  }
  dynamic bool DeviceExecute(Hndl _device)
  {
      return RC&lt;device&gt;(_device)-&gt;execute();
  }
  </code></pre>
  <p><strong>C# API:</strong></p>
  <pre><code>using System.Runtime.InteropServices;
  
  namespace CSDllProject
  {
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      public struct Hndl
      {
          public static Hndl Invalid = new Hndl(0ul);
          private ulong ptr;
          public ulong Ptr { get =&gt; ptr; internal set =&gt; ptr = value; }
          public bool ValidHandle =&gt; Ptr != 0ul;
          internal Hndl(ulong ptr) { this.ptr = ptr; }
          public override bool Equals(object obj) =&gt; base.Equals(obj);
          public override int GetHashCode() =&gt; base.GetHashCode();
          public static bool Equal(Hndl h1, Hndl h2) =&gt; h1.Ptr == h2.Ptr;
          public static bool operator ==(Hndl h1, Hndl h2) =&gt; Equal(h1, h2);
          public static bool operator !=(Hndl h1, Hndl h2) =&gt; !Equal(h1, h2);
      }
  
      public static class API
      {
  #if DEBUG
          private const string TestApi = @&quot;&lt;path_to_debug_c++_dll&gt;&quot;;
  #else
          private const string TestApi = @&quot;&lt;path_to_release_c++_dll&gt;&quot;;
  #endif
  
          // ===== Device API =====
          [DllImport(TestApi, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
          public static extern Hndl CreateDevice(uint limit);
          [DllImport(TestApi, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
          public static extern void DisposeDevice(Hndl _device);
          [DllImport(TestApi, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
          public static extern uint DeviceActivity(Hndl _device);
          [DllImport(TestApi, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
          public static extern uint DeviceActivities(Hndl _device);
          [DllImport(TestApi, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
          public static extern bool DeviceExecute(Hndl _device);
      }
  }
  </code></pre>
  <p><strong>C# Wrapper:</strong></p>
  <pre><code>using System;
  
  namespace CSDllProject
  {
      public class Device : IDisposable
      {
          public Hndl Handle { get; private set; }
  
          public uint Activity =&gt; API.DeviceActivity(Handle);
          public uint Activities =&gt; API.DeviceActivities(Handle);
  
          public Device(uint limit) =&gt; Handle = API.CreateDevice(limit);
          ~Device() =&gt; Dispose();
  
          public bool Execute() =&gt; API.DeviceExecute(Handle);
  
          public void Dispose()
          {
              API.DisposeDevice(Handle);
              Handle = Hndl.Invalid;
          }
      }
  }
  </code></pre>
  <p><strong>C# Unit Test:</strong></p>
  <pre><code>using Microsoft.VisualStudio.TestTools.UnitTesting;
  
  using CSDllProject;
  
  namespace UnitTest.NET
  {
      [TestClass] public class DeviceTests
      {
          [TestMethod] public void Test1()
          {
              using (var device = new Device(100u)) // [*]
              {
                  do
                  {
                      Extension.LogLine($&quot;Device Activity: {device.Activity:0000}&quot;); // just outputs this string to a log file, nothing fancy
                  }
                  while (device.Execute());
              }
          }
      }
  }
  </code></pre>
  <p>The purpose of this small example is next. Upon creating the &quot;device&quot; (again, this is a scratch pad so no code has real meaning), you give it a limit to how many activities it can do (marked by [*]). In the example, it is <code>100u</code>. Then, when you call <code>.execute()</code>, it checks if it has exceeded the number of <code>activities</code> it can do. If it has, it returns <code>false</code>. Else, it would increment the <code>activity</code> (then in the original project it would actually do something) and return <code>true</code>, meaning it &quot;did&quot; something meaningful. In the caller (C#) code, I introduce a <code>do-while</code> loop and inside it's condition I simply call the <code>.execute()</code> method. Now, it all works fine, and the log looks like next:</p>
  <pre><code>Device Activity: 0001
  Device Activity: 0002
  Device Activity: 0003
  Device Activity: 0004
  ...
  Device Activity: 0097
  Device Activity: 0098
  Device Activity: 0099
  Device Activity: 0100
  </code></pre>
  <p>which is expected. However, if I change the limit to activities (the <code>[*]</code> line) to <code>1000u</code>, something weird happens. I run the unit test and it <strong>never</strong> stops. So, I forcibly stop it and check the log. The log looks like this now:</p>
  <pre><code>Device Activity: 0001
  Device Activity: 0002
  Device Activity: 0003
  Device Activity: 0004
  ...
  Device Activity: 0997
  Device Activity: 0998
  Device Activity: 0999
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  Device Activity: 1000
  </code></pre>
  <p>At first I did not know what to think... It did a good job till 1000, but after that it seemed like it didn't get out of the loop (which was the reason why unit test never finished), however, it also did not increment the value (and no, the log has no problem since it's very inefficiently implemented, to open the file, write to it then immediately close it on each call, on purpose like that so it will 100% work).</p>
  <p>I've tested the C++ code with another C++ project (the <code>device</code> class on it's own without the dll since it's actually inside a shared project) and there were no errors (the calling code was the same as the one in C# unit test and it did a good job for both <code>100u</code> and <code>1000u</code> limit). C# literally has simple calls so there is nothing to test there in particular. In my original project, I also have some differences in calling this piece of code on debug vs. on release mode. This smelled that some optimizations which compilers might do might mess up the code.</p>
  <p>Speaking to my friend, he said that at work he had similar problems and that one time he had to ship things to production compiled on debug. Again, more reasons to think that optimizations may be the problem. I've tried many things and in the end, I added some logging inside the C++ dll api:</p>
  <pre><code>dynamic bool DeviceExecute(Hndl _device)
  {
      device* dev = RC&lt;device&gt;(_device);
      // log code that logs memory address of dev
      bool result = dev-&gt;execute();
      // log code that logs the result
      return result;
  }
  </code></pre>
  <p>This code now worked just fine for <code>1000u</code> limit. I tried commenting out the log lines and leaving the rest of the code as is, then it stopped working again. This was all on release. Switching this to debug mode had different outcome. Both with and without the log lines worked. At this point I definitely was sure that compiler does optimizations on release so that having:</p>
  <pre><code>dynamic bool DeviceExecute(Hndl _device)
  {
      device* dev = RC&lt;device&gt;(_device);
      bool result = dev-&gt;execute();
      return result;
  }
  </code></pre>
  <p>would end up as:</p>
  <pre><code>dynamic bool DeviceExecute(Hndl _device)
  {
      return RC&lt;device&gt;(_device)-&gt;execute();
  }
  </code></pre>
  <p>anyway since he can conclude that the results of previous expressions are only used in one and hence convert it to the second variant. In the end, the way I fixed the issue is by introducing <code>volatile</code> to stop the compiler from making such optimization, and ended up with:</p>
  <pre><code>dynamic bool DeviceExecute(Hndl _device)
  {
      volatile bool result = RC&lt;device&gt;(_device)-&gt;execute();
      return result;
  }
  </code></pre>
  <p>This now works fine on both debug and release for <code>100u</code> and <code>1000u</code> limit in activities. My friend told me that there is some problem with the optimizations, the stack, the way .NET calls the unmanaged dll, etc. but not even he knew exactly what it was, but having some values on the stack (like local variables) could help resolve the issue. This is my question, what exactly is going on here?</p>