One of the things I like most about .NET is that there's so little difference between Release- and Debug-builds. Anyone who's ever attempted to debug a Release-mode only problem (or, for that matter, a Debug-only problem) in unmanaged code knows what I'm talking about.
But this past week saw not one, but two release-mode only bugs in a development effort I've been leading. Perhaps the GC gods are angry with me, because they overheard me disparage my working set size... or, more likely: my false sense of security has finally caught up with me!
Traditionally when I encounter a "Release-mode only bug", I look for three things: subtle timing problems in my application code, differences in memory layout/initialization, or (lastly) true compiler/optimizer bugs.
One bug I ran into this week was definitely of the first variety: a foolish developer on my team depending on two tightly-spaced events to always fire in the same order that they do on my... err, I mean "his", dev box. Bad, bad developer!
It's a little early to say with certainty, but the other bug is looking like it belongs in the latter category -- maybe not incorrect JIT code, per se, but definitely a noticable difference in run-time behavior. (Bad JIT code is something I've yet to run into with .NET... anyone?)
A developer on my team was attempting to use a Mutex (declared as a local variable in the app's Main method) to ensure that only one instance of the app would be run, in a given session. Simple enough, and it worked like a charm in Debug mode -- but not in Release! That is, two or more instance of a Release build were able to start, simultaneously, while only one instance of a Debug build was able to do so. Even stranger: wrapping the Mutex variable with a C# "using" block eradicated the bug. (Making the Mutex variable a static member of the class also worked... or a well-placed call to GC.KeepAlive... yadda yadda.)
So naturally I began to suspect that this was a GC problem: in Debug-mode, the lifetime of the Mutex was lasting for the entire duration of the Main function, but in Release-mode, the Mutex was being finalized earlier. Wow. Sure, this kind of thing is easy to work around, when you know what to look for (and when you notice it, at all!) but it can also be very frustrating, for those poor souls who don't.
I'm curious to learn what other people are experiencing, in this regard: has anyone encountered a bug of the second category -- a Debug/Release bug due to the difference in memory layout, or initialization, in the CLR?
Share your horror stories in the comments!