ASP.Net, .Net, BizTalk, Tech and Life - Francois Malgreve - Bangkok

Thursday, July 31, 2008

How to Handle Unhandled Exception

When an unhandled exception occurs in the .Net Framework run-time 1.0 & 1.1, the Windows Process will terminate only if the exception occurred on the main application thread. Would an unhandled exception occur in a thread other than the main application thread, it will be caught by the runtime and therefore NOT cause the application to terminate. This means that unhandled exceptions in child threads disappear silently without anyone being able to know about it, implying that bugs are silently swallowed by the runtime without anyone being notified about it. The dangerous scenario is that a user might believe that a unit of work has executed successfully while it is actually not the case. This is in my opinion something not desirable and has been addressed in later version of the .Net Framework.

Indeed, from the .Net Framework 2.0 onwards, unhandled exceptions occurring on ANY thread (thus including any child threads being background threads or not) shuts down the application running the particular thread.

When the runtime terminates an application because of an unhandled exception, it writes an entry in the Windows Event Log which looks like the following:

EventType clr20r3, P1 processname, P2 1.0.0.0, P3 485f85f0, P4 system, P5 2.0.0.0, P6 471ebf0d, P7 3832, P8 bf, P9 system.componentmodel.win32, P10 NIL.

“processname” being the name of the .Net executable.


1. How to resolve unhandled exceptions? The System.AppDomain.UnhandledException event.

If an unhandled exception occurs in a windows application (multi-threaded or not), a windows service named winservice.exe for example, the runtime will terminate the service and write an Event Log entry such as:
“EventType clr20r3, P1 winservice.exe, P2 1.0.0.0, P3 485f85f0, P4 system, P5 2.0.0.0, P6 471ebf0d, P7 3832, P8 bf, P9 system.componentmodel.win32, P10 NIL.”

While it is good to be notified that a service crashed, the information supplied is rather cryptic and has very little added value. It would be nicer to have a way to log unhandled exceptions with a more meaningful message so that it is possible to get enough information to be able to fix the source code and re-deploy the application.

Conveniently, the .Net Framework provides the System.AppDomain.UnhandledException event. This event fires whenever an application domain unloads because of an unhandled exception. Note that registering for the event does not cause unhandled exceptions to be "handled". It simply notifies you that an exception has gone unhandled, so that it is possible to take some action such as logging the exception message and stack trace and eventually do some clean up before the application dies. The Exception is still unhandled and so still causes the application to shut down.

The discussion in this article is not a solution on how to handle exceptions. Exception handling is a strategy on how to handle exceptions and is a different discussion. This article explains how to act with unhandled exceptions so that details about the exception can be logged so that a proper solution can be found to resolve the issue/bug.

Ideally, all exceptions should be handled in the source code with try-catch-finally blocks so that unhandled exceptions do not occur. Nevertheless, there are always unforeseen cases and developer mistakes which, in my opinion, justify caring for unhandled exceptions across the board.

Here is an example on how to register for the event with an event handler that log exception details in the Windows Event Log:

// This event provides notification of uncaught exceptions. Write this in the entry point of your program, like in the OnStart() method of a Windows Service.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception ex = (Exception)args.ExceptionObject;
EventLog.WriteEntry("WinService.exe", "Unhandled Exception caught: " + ex.Message + ex.StackTrace, EventLogEntryType.Error);
}


2. Note for Windows Form Applications - Application.ThreadException event.

2.1 Application.ThreadException event

For Windows Form Applications, there is another event that is raised when an unhandled exception occurs, the System.Windows.Forms.Application.ThreadException. Nevertheless, this event fires only when unhandled exceptions happen in Window Forms threads (UI threads). This means when an exception is thrown from code that was ultimately called as a result of a Windows Message. Windows Messages are emitted by keyboard hits, mouse clicks or "paint" messages,... in short, nearly all code in a typical Windows Forms application.

While this works perfectly, it lulls one into a false sense of security that all exceptions will be caught by the central exception handler:
  • Exceptions thrown on worker threads are a good example of exceptions not caught by Application.ThreadException.
  • Exceptions thrown by the code inside the Main method of the Windows Forms application, including the main form's constructor, executes before the Windows message loop begins and so is another example of exceptions that do not raise the Application.ThreadException event.

In this case, we said the Application.ThreadException event handler to be a “central exception handler” because it is still possible for the application to keep running when this event is raised, depending on what kind of logic is implemented in the handler.

As a reminder, Worker Threads are threads:
  • Created manually: Thread.Start()
  • Created by the ThreadPool: ThreadPool.QueueUserWorkItem()
  • Created by any kind of asynchronous call which internally uses a thread pool thread to execute: Delegate.BeginInvoke(), BeginXXX()

One must attach a handler to the Application.ThreadException event before instantiating the main form of the application by calling Application.Run(). Also, because this is a static event, you must detach the event handler(s) when the application is disposed or memory leaks will result.

The Application.ThreadException as a default event handler which behaves in the following way:
  • If an unhandled exception occurs in the main application thread, the default exception handler catches it and terminates the application.
  • If an exception occurs in a thread other than the main application thread, the thread exits, but the application continues to run.


2.2 Application.SetUnhandledExceptionMode

It is possible to instruct the application whether it should catch all unhandled exceptions thrown by Windows Forms components and terminate the application, or whether it should raise an event so that an event handler can be implemented; the event handler could halt execution and expose the unhandled exception to the user. This is setting is done by using the application configuration file or the Application.SetUnhandledExceptionMode() method.
It is possible to instruct the application whether it should catch all unhandled exceptions thrown by Windows Forms components and terminate the application, or whether it should raise an event so that an event handler can be implemented; the event handler could halt execution and expose the unhandled exception to the user. This is setting is done by using the application configuration file or the Application.SetUnhandledExceptionMode() method.
  • UnhandledExceptionMode.ThrowException never route exceptions to the Application.ThreadException event handler and so the default event handler will terminate the application when an unhandled exception occurs as explained earlier.
  • UnhandledExceptionMode.CatchException always route exceptions to the Application.ThreadException event handler.

Again, as a reminder, the Application.ThreadException event handler is only for unhandled exception occurring on UI threads and so the SetUnhandledExceptionMode() method affects only the way unhandled exceptions coming from UI threads are treated, it does not affect how non UI threads unhandled exceptions are treated. System.AppDomain.UnhandledException event handlers will always be called when non UI threads unhandled exceptions occur.


2.3 Sample

Hereunder is a code sample on how to register to the Application.ThreadException event in a Windows Form Application. As said earlier the event handler will only be called for unhandled exceptions occurring on the UI thread. Thus, the code sample also has an event handler for unhandled exception on non-UI threads by registering to the System.AppDomain.UnhandledException event:

static class Program
{
///
/// The main entry point for the application.
///

[STAThread]
static void Main()
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(Form1_UIThreadException);

// Set the unhandled exception mode to force all Windows Forms errors on UI threads
// to go through our handler regardless of application settings.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

// Default generated code by Visual Studio for WinForms
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

You can find a zip file here containing a complete Visual Studio 2008 solution demonstrating both type of unhandled exception event handler and their effect.


3. Note for ASP.NET applications - Application_Error in Global.asax.

Would an unhandled exception occurs in an ASP.NET application on a worker thread, the runtime will terminate the worker process (w3wpp.exe) and write an Event Log entry such as:
“EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.1830, P3 42435be1, P4 app_web_7437ep-9, P5 0.0.0.0, P6 433b1670, P7 9, P8 a, P9 system.exception, P10 NIL.”

In ASP.Net, there is an Application_Error method in the Global.asax file that can be implemented so that an action can be taken when an unhandled exception occurs (logging the exception details, for example). Again, treating an unhandled exception in such a way is not handling an exception (that should be done in the try-catch-finally block in the code), the web application will still crash and display an error message on the browser but it lets the programmer have a way to log information about the exception and eventually do some other clean-up tasks.

Nevertheless, similarly to Windows Forms Application, the Application_Error will be called only for unhandled exception that occurred on the main thread. Unhandled exceptions occurring on worker threads will not be caught by the Application_Error event handler in the global.asax file. If we want to be notified of unhandled exception occurring on worker threads, we need to register an event handler to the System.AppDomain.UnhandledException event, as we did for Windows Services and Forms Applications. The event handler should at least log the exception and eventually do some clean tasks.

Microsoft has a KB on how to implement an event handler for the System.AppDomain.UnhandledException event within an HTTPModule.

You can find here a zip file with a Visual Studio 2008 solution using the HTTPModule defined in the KB. I registered the HTTP module in the web.config the following way:

<system.web>
<httpModules>
<add name="UnhandledExceptionModule" type="WebMonitor.UnhandledExceptionModule, UnhandledExceptionModule, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=51d169497dc4a81e, processorArchitecture=MSIL"/>
</httpModules>
</system.web>

The only non trivial attribute is the “PublicKeyToken”. The Public Key Token is a 64-bit hash of the public key corresponding to the private key used to sign the assembly. A .Net assembly is said to have a strong name when it is signed.

There are 2 ways to retrieve the Public Key Token from a strongly named assembly:
  • The lazy way is to add the assembly in the GAC and go to C:\WINDOWS\assembly, one of the column showed in Windows Explorer is the Public Key Token. Only strongly named assemblies can be stored in the GAC.
  • Run the command line “sn -T UnhandledExceptionModule.dll” which prints the Public Key token of a strongly named assembly.

Contrary to the instructions given in the KB, I did not GAC either NGEN the HTTP module; I just referenced the HTTPModule project from the web project so that the HTTP module library is automatically copied to the web application bin folder. All assemblies in the bin folder are automatically found by the run-time so that is not necessary to GAC all third party libraries.

Labels: ,

Monday, March 31, 2008

Windows Process, .Net Application Domain and 2 GB limit on 32-bit Windows

A few weeks ago I heard some comments from a colleague about how .Net applications run and that “all .Net applications run in the same runtime (CLR) so that if you start 10 separate .Net applications, they would share together a single 2 GB limit on Windows 32-bit”. This of course not true and it gave me the idea to blog about the 2GB limit on 32-bit systems, Windows Process, .Net applications and the concept of .Net Application Domain.


2 GB limitation on Windows 32-bit.

32-bit Operating Systems are capped by the number of unique pointers that can exist at a time. On 32-bit processors, only 2^32 distinct addresses can exist. Would all these addresses be used, that would represent 4 GB of memory. On a Windows Operating System the memory address system is not a 1-to-1 relationship to the physical memory of your hardware otherwise you would be stuck with a maximum of 4 GB of addressable memory for the whole machine. This would include all the I/O address space, kernel memory and so leave much less actual memory for programmers to use.

This is why when we talk about memory, it is important to realize the distinction between the physical memory (RAM on the motherboard) and the Virtual Memory accessible through the Virtual Address Space. Note that actually, Virtual Memory is not the same as Virtual Address Space and that there are ways to use Virtual Memory without using the Virtual Address Space. I will nevertheless not go into these details; the important thing to remember is that Windows has a complex memory management system that enables the O.S. to use much more than 4 GB as a whole. The inner workings are not for the faint-hearted and are actually not of interest for most .Net programmers living in the managed world.

Check this blog post for a primer on memory management on Windows Operating System.

When Windows 32 starts a program, a 32 bit process using 32 bit size pointers is created and so the process has a maximum of 4 GB of addressable memory.
Windows will assign to the process a Virtual Address Space of 4 GB (2^32) split in two; 2 GB of user mode virtual address space and 2 GB of kernel mode virtual address space. The user mode virtual address space is the “memory” (read the virtual address space to be correct) available for your program to use.
This 2 GB user mode virtual address space limit is what is commonly called the 2 GB memory limit on Windows 32-bit.


/3 GB switch on 32-bit Windows

The /3GB switch changes the way the 4GB virtual address space is split up. With the /3GB switch, the split is 3GB of user mode virtual address space and 1GB of kernel mode virtual address space. It is nevertheless not recommended to use this option as it can bring unexpected bug from drivers and other kernel-mode processes which might expect to have 2 GB of kernel virtual address space available (not that a driver would ever need 2 GB, just that an older driver might expect to have addresses from 0x80000000 to 0xFFFFFFFF available).
See here and here for other problems that can arise when using the /3GB switch.


AWE

AWE does not give more virtual address space to a process. AWE stands for Address Windowing Extension and is a Microsoft API (Application Programming Interface) that allows a 32-bit software application to access more physical memory that it has virtual address space.
AWE enables programs to reserve physical memory as non-paged memory and then to dynamically map portions of the non-paged memory to the program’s working set of memory. This process enables memory-intensive programs, such as large database systems, to reserve large amounts of physical memory for data without having to be paged in and out of a paging file for usage.
To be clear, AWE can only be available on programs that actually use the AWE API, it is not an OS switch that can be turned on/off on any program.


Windows 64-bit

On windows 64-bit there is not 2 GB limit, the user mode virtual address space limit being 8TB. See here for reference.


Windows Process and Runtime Host

A Windows process is an instance of a program that is executing over the Windows layer. A process contains the executable code and data inside the memory reserved for it by the Operating System. There will be at least one thread executing instructions within the process but more in most cases.

Any program running on Windows is actually working within a process. If you open 2 instances of notepad, you can see that 2 processes running notepad.exe are visible under the Processes tab of the Windows Task Manager.

The concept of a Process exists for two main reasons:
  • To enable multitasking (time sharing), the different processes a CPU is running will have their states changing between running and waiting very quickly and so give the illusion to the end-user that all processes are running in the same time. This brings multitasking as well as scalability.
  • To provide boundaries between running programs so that a process cannot peak into another one and that erroneous code inside a process cannot corrupt areas outside of that process (so that a process cannot crash another one). This is brings security and stability.
The isolation between processes is achieved by making sure that any given unique virtual address space runs exactly into one process and not any other.


Runtime Host

.Net applications are compiled in CIL (Common Intermediate Language, formally called MSIL – Microsoft Intermediate Language), and then are JITed (Just-In-Time compiled) by the CLR (Common Language Runtime) into instructions directly understandable by the CPU (native code).
Here is an illustration of this 2 step compilation process:


This means that .Net applications are not Win32 applications and so cannot be executed directly by the Operating System. As any application running on Windows has to run through a Windows Process, a Windows Process called a Runtime Host will actually execute (host) the .Net Application. The Runtime Host first loads the CLR dll (a native Windows library – unmanaged code) which in turn loads the .Net application (managed code), JIT compiles it and runs it. The process thus effectively transitions the control of running the application from itself to the CLR.

There are 2 types of Runtime Host shipped with the .Net Framework, ASP.NET and Shell. Shell runs all Windows-type applications (Windows Form, Windows Service or Console App).

We can see that this concept actually adds a new layer between the .Net application and the Operating System. This layer, implemented by the CLR, is generically called a Virtual Machine and has OS-like features. It is an abstraction layer between the .Net application and the Operating System. As with Java, this permits any .Net Application to run on any Operating System as long as there is a CLR implemented for that OS.


2 GB limit for .Net applications

As the Runtime Host is a Windows Process, the .Net applications run by a Runtime Host is limited to the 2GB barrier on 32-bit Windows OS. Nevertheless, every Runtime Host has a separate 2 GB virtual address space limit. So would you launch 2 instances of a .Net application, each being a separate process in Task Manager, they would each have 2 GB limit.


.Net Application Domain

An Application Domain is the CLR equivalent of an Operating System’s process. As the Windows OS brings logical and physical isolation between Windows applications through the use of Processes, a single Runtime Host Windows Process can run several isolated .Net applications through the use of Application Domains. As explained before, Windows isolate processes by assigning different virtual memory address space to each process. In the .Net world, the memory is actively managed by the CLR and so the CLR can make sure that memory addresses are not shared between application domains, effectively isolating different Application Domains running in the same Runtime Host.

When a Runtime Host starts a .Net application, the CLR will create a default Application Domain to run the .Net application. As multiple Processes can run on a single OS, multiple Application Domains can run within the same Runtime Host.

An Application Domain is cheaper to create than a Windows Process and has relatively less overhead to maintain. It is thus more efficient to isolate .Net Application through Application Domains rather than Windows Processes. Application Domains are sometimes referenced as lightweight processes but strictly speaking, they are NOT processes.

To summarize, here is a list of advantages of having Application Domains within a Runtime Host Process (which are for most of them similar to the advantages of having Processes within an Operating System):
  • An Application Domain is a more lightweight mean to provide isolation between .Net applications than Processes.
  • A .Net application in an Application Domain can be stopped without affecting the state of another application running in a separate Application Domain.
  • A crash in an Application Domain will not affect other Application Domains neither the Runtime Host Process hosting the Application Domains.
  • Configuration information is part of an Application Domain scope, not the process’ scope.
  • Each Application Domain can have different security access levels assigned to them, all within the same Runtime Host Process.
  • Code in one .Net Application Domain cannot directly access memory in another Application Domain. If two .Net applications need to communicate across Application Domains, they need to use .Net Remoting to do so. In .Net 1.x, this kind of inter-process communication was expensive because the TCP/IP stack needed to be involved. In .Net 2.0, .Net Remoting supports named pipe remoting which is much more efficient. WCF in .Net 3.x has this feature as well.

Labels: ,