A site devoted to discussing techniques that promote quality and ethical practices in software development.

Tuesday, May 13, 2014

CLR and Mono Runtime difference - System.Diagnostics.DefaultTraceListener.AssertUiEnabled

This one to some is a very contentious issue and is present in the Mono runtime running in Mint-15.

While this property is not well known to many as rarely one would need to touch this in CLR, it is intimately related to the well-known method System.Diagnostics.Debug.Assert().

The runtime behaviour of Debug.Assert() depends on the behaviour of the trace listener(s) that have been loaded at that time. When the expression becomes false, Debug.Assert() calls the System.Diagnostics.TraceListener.Fail().

At start up time, CLR & Mono would load a number of System.Diagnostics.TraceListener-derived class(es). If there is no overriding specification in the application's config file, the runtime loads the System.Diagnostics.DefaultTraceListener and is identified in the collection of TraceListeners by the name "Default".
In CLR for most runtime environment, such as Console, WinForm, WPF, WCF, Debug.Assert() by default will alert the user with a user-interface that allows the user to abort or ignore. This behaviour has not changed since the first release of the .Net framework and runtime. Of course this behaviour is entirely configurable.

If System.Diagnostics.DefaultTraceListener is the default TraceListener, the default behavour of alerting the developer is by means of a user-interface to inform of the unmet condition. This is the desired behaviour for most situations during development. This is because the System.Diagnostics.DefaultTraceListener.AssertUiEnabled is default to true.

However, in Mono, the System.Diagnostics.DefaultTraceListener.AssertUiEnabled is by default initialized (or lack of explicit initialization in DefaultTraceListener class) to false.

This discrepancy in the default value often leads to developer's gripe and  incorrectly accusing the Mono's System.Diagnostics.Debug.Assert() failure to catch the unmet condition; by defaut Mono just does not alert the user loudly. The Internet has plenty of Linux/Unix ways of 'fixing' this problem. But I will present here the .Net ways of fixing this discrepancy in the spirit of maintaining cross-platform runtime consistency.

It is nothing more annoying when one writes defensive code using Debug.Assert() liberally only to be silently scuttled by a difference in default value in another class. I have lost count of the number of hours trying to find some problems that should have been caught by Debug.Assert() but flew past me silently!

How to fix this?

To convince yourself you can report out the default value for DefaultTraceListener.AssertUiEnabled by writing a simple console application like this that can run successfully in both Mono and CLR:

class MainClass
  public static void Main (string[] args)
    Console.WriteLine ("Default value of AssertUiEnabled = {0}",
      (Debug.Listeners ["Default"] as DefaultTraceListener).AssertUiEnabled);

You should get true when run in CLR and false when in Mono.

The reason for assuming false in Mono could be historical where Mono initially did not have any GUI support; this has changed and the code for DefaultTraceListener.Fail() contains code to invoke user-interface.

However, even in the absence of rich user-interface it should adopt the approach used by other languages, such as Java's assert keyword, assert() in C/C++ language, where it aborts the execution reporting the point of failure. Not reporting to a developer loudly by default is dangerous, considering the usage promoted by Debug.Assert(). If a crude way of alerting the user is unsuitable, the .Net has ways to allow user to alter the behaviour and that is a user-initiated process and that the user then know where to look for violations. This also makes the runtime behaviour consistent with CLR. Not telling misleads the developers of fault. The current situation is like asking Java developers to make the JRE's behaviour of java -ea different in Windows and in Linux!

Without recompilation - use Application Configuration File

This is by far the most convenient way to fix this problem and that it works for both CLR and Mono.

You simply include the following fragment in your application configuration (if none, create one) to add an assert element to force the assertuienabled to true:

       <!-- This changes the DefaultTraceListener.AssertUiEnabled from false to true -->
       <assert assertuienabled="true" />

With code

This approach will require you to compile new code. However, if you are testing ready-made assembly and do not want to rebuild them, this approach can be adopted by including the following piece of code in the start up code:
(Debug.Listeners ["Default"] as DefaultTraceListener).AssertUiEnabled = true;

I hope this post will make the experience of developers looking for Mono as their cross-platform environment more pleasing.

No comments:

Blog Archive