There are some types of exceptions, each with their own way of dealing with them. First, there are Programming and Environmental errors returned from INtime system calls, defined by status codes. Then there are Numerics exceptions as a result of numerical operations. Hardware faults are caused by using hardware resources incorrectly. And then there are exceptions handled by Structured Exception Handling and C++ exception handling. We will describe each of these groups in detail.
Programmer exceptions are caused by providing incorrect parameters to a system call. Environmental exceptions occur when the environment cannot provide the requested resource, such as trying to allocate memory when none is available.
Programming and environmental exceptions are always indicated to the calling thread by a specific return value from the INtime system call; the actual return values for success and exceptions are dependent on the type of system call made. When an exception is indicated, you can call GetLastRtError to retrieve the status code; you may also want your application to investigate the system's state by using calls that return system accounting information.
If a system call returns a result that indicates an exception, all other output parameters of that system call are undefined.
Structured Exception handling is a Microsoft specific extension to the C programming language, implemented by Microsoft Visual Studio and supported by the operating system, in particular by Windows and also by INtime. You can use Structured Exception Handling in C source code to deal with Hardware faults and with user defined exceptions.
Using __try and __except blocks and some APIs like GetExceptionCode and RaiseException, you can decide how a thread reacts to hardware faults and user exceptions. An (fairly trivial) example follows:
/* a filter indicates if the __except clause handles this type of exception */ /* this filter handles all exceptions */ int Filter(LPEXCEPTION_POINTERS pEP) { printf("Exception %x at address %x\n", pEP->ExceptionRecord.ExceptionCode, pEP->ExceptionRecord.ExceptionAddress); return EXCEPTION_EXECUTE_HANDLER; } __try { /* perform some action that may cause a hardware exception */ } __except(Filter(GetExceptionInformation())) { ExitProcess(1); }
The result of this sample code is that if the action does indeed cause a hardware exception, the fault code and address are printed in the console window and the process is terminated. For more details, see __try, __except and __finally in the Microsoft Visual Studio documentation.
In the filter expression (the expression in parentheses following the __except keyword) you can use the GetExceptionCode and GetExceptionInformation functions. They return the exception code and an EXCEPTION_POINTERS pointer that provide details on the exception. The filter expression indicated if the __except clause is executed to handle the exception or not. For information about EXCEPTION_POINTERS and EXCEPTION_RECORD, see Windows documentation.
If you wish to force an exception that is handled like a hardware fault, you can use the RaiseException function.
C++ exception handling uses standard C++ language elements such as try and catch blocks and throw statements to handle exceptions. Although there are syntactical differences from Structured Exceptional Handling, semantically this is similar. An example (slightly different from the one above under Structured Exception Handling) could be:
class MyException : exception { /* members and functions as required */ }; try { /* perform some action that may result in a MyException exception */ } catch (MyException e) { printf("MyException occurred\n"); exit(1); }
With C++ exception handling, hardware faults are handled as the "everything else" type (indicated by ... in the catch expression); as this provides no exception details, there is an additional function _set_se_translator that allows you to translate a hardware fault into an object that provides details about the fault.
For details, see the language definition for C/C++ and the Microsoft Visual Studio documentation.
A numerics exception occurs when floating point calculations give an improper result, such as a floating point divide by zero. Such exceptions can be handled using Structured Exception Handling. The same mechanism may be used to also handle SSE2 and other advanced instruction set exceptions. See the "Floating Point Exception Handling" sample project for an example.
A hardware fault is an attempt to do an operation that violates the protection mechanisms built into the X86 architecture. For example, an integer divide-by-zero operation causes a Divide-by-Zero Fault. Likewise, trying to access a memory address in your address space that has not had physical memory assigned to it causes a Page Fault.
The RT kernel first creates a system-wide data mailbox named HW_FAULT_MBX and catalogs it in the root process' object directory, then handles hardware exceptions as shown in this figure:
When a message arrives at a HW_FAULT_MBX, you can call GetRtHandleType or ntxGetType to determine the system exception handler used for the thread and process, and thus determine the state of the offending process:
When a hardware exception occurs during the execution of an INtime real-time thread, error information is automatically sent to one or more data mailboxes. A global data mailbox, cataloged in the INtime kernel root directory with the object name HW_FAULT_MBX, is automatically created and maintained by the system and exists for the purpose of monitoring hardware exceptions. Each real-time process can optionally create a local data mailbox, which must be named HW_FAULT_MBX, and catalog it into its local object directory, for monitoring process-specific hardware exceptions.
The default action taken by the kernel, when a hardware exception occurs, is to suspend the real-time thread that caused the exception. Although a hardware exception is generally considered fatal to the executing thread, other threads in the same process remain eligible to execute and are not suspended. When a hardware exception occurs, any thread (within the same or another real-time process) that is monitoring the global HW_FAULT_MBX can send a notification and/or take other appropriate actions.
Threads that monitor their local HW_FAULT_MBX data mailbox are notified of hardware exceptions that occur only within their own process space. That is, by creating a local HW_FAULT_MBX data mailbox one can limit notifications of hardware exceptions to those that are specific to a process. By monitoring the global HW_FAULT_MBX data mailbox that resides in the root object directory, applications can be notified of hardware exceptions generated by all real-time processes.
The exception message sent to the mailbox is a structure of the type HWEXCEPTIONMSG. This structure contains handles that identify the offending thread and its associated process, an error code identifying the cause of the hardware exception, and the instruction pointer for the exception incidence.
This lists common operations on exceptions and the system calls that perform the operations:
To . . . | Use this system call . . . |
---|---|
Translate hardware fault information for C++ exception handling | _set_se_translator |
Test for abnormal termination | AbnormalTermination |
Get a hardware or user exception code | GetExceptionCode |
Get hardware or user exception details | GetExceptionInformation |
Get handler attributes | GetRtExceptionHandlerInfo |
Raise a user defined exception | RaiseException |
Set a handler | SetRtExceptionHandler |