This manual documents the Condition Handling Facility library (libChf r2.2).

The Chf library provides a centralized, unified method of generating and signaling exceptional conditions, establishing exception handlers, and printing status and error messages.

This manual describes the Condition Handling Facility library, and applies to its release 2.2.

Overview
********

The Condition Handling facility library provides a centralized, unified method of generating and signaling exceptional conditions.  This method can be used either in conjunction with, or in alternative to, the traditional style of handling error conditions by returning distinguished error values when an error occurs in a procedure, and checking for these values after each procedure invocation.

It serves many different purposes:

  * It helps to gather and make available as much information as possible when an error occurs, such as which source module detected it, its severity, and any other ancillary information the user may find useful.

  * It provides an unified method to log and print error messages, and to internationalize applications.

  * By means of condition handlers, it gives the user a convenient way to implement global exception recovery actions in an application.

  * It may improve the readability of the source code, because less explicit checks for error conditions are required.

Since release 2.2, the Chf library also supports Unicode-based Win32 systems, notably Windows CE.  Implementation details of the Win32 version of the library will be discussed in the documentation, and will be highlighted by the Win32 keyword.  Moreover, the following general notes apply:

  * All library functions, when compiled for Win32, handle Unicode character strings (array of TCHAR) instead of ASCII strings (array of char); this change is transparent and the documentation does not reflect it.

  * Library functions sigsetjmp() and siglongjmp() are not supported; accordingly, they are replaced by setjmp() and longjmp().  The non-local control transfer context buffer, sigjmp_buf is also replaced by jmp_buf; this change is transparent and the documentation does not reflect it.

  * The reentrant flavor of the Chf library is not yet supported.  Multithreaded application must serialize Chf accesses themselves.

  * The ChfErrnoCondition macro is not supported, and has no effect.

  * The default condition handler does not display the condition message on the standard error stream.

  * The CHF_ABORT initialization flag is not fully supported; when used, the Chf library invokes exit(EXIT_FAILURE) instead of abort() (not supported by Win32) to abort the application.

The following terms will be used often throughout this document:

Exception
     An exception is an event that changes the normal flow of instruction execution, and is usually caused by an error condition.  Any procedure can generate an exception using the ChfGenerate macro, and then raise or signal it using the ChfSignal() function call.

Condition
     An informational state which exists when an exception occurs, and encloses information about the exception itself.  The term condition is preferred, since the term exception usually implies an error.

Condition handling
     When a condition is detected during the execution of a procedure, the procedure can signal the condition.  The hierarchy of currently active condition handlers is then permitted to respond to the condition; the procedure response is called condition handling.

     The condition handlers are themselves procedures and they are called with the usual calling conventions; the only exception is that, in the current release of the Chf library, they cannot establish condition handlers of their own.

     A procedure can establish a condition handler using the function ChfPushHandler(), that pushes a new condition handler on top of a LIFO handler stack; the condition handler stays active until a matching call to ChfPopHandler(), that pops the topmost handler from the handler stack is executed, or until an execution stack unwind occurs.

     Another way to implicitly establishing and removing condition handlers is to use the structured condition handling macros. See Structured condition handling, for more information.

Initialization and exit
***********************

Before using any function of the Chf library, it must be correctly initialized.  Since the library has a generic interface towards the message retrieval subsystem (see Message retrieval), more than one initialization function exist, one each for every message retrieval subsystem the library supports, plus one custom initialization function to use if a custom message retrieval subsystem is used. After initialization, the library accesses the message retrieval subsystem transparently, and its peculiarities are no longer visible to the users.

All initialization functions, when successful, register a default condition handler. See Default handler.

Moreover, each module using the Chf library must include the header file Chf.h and provide some basic definitions. See Library header.

ChfInit()
=========

 -- Function: int ChfInit (const char *app_name, const ChfOptions options, void *mrs_data, ChfMrsGet mrs_get, ChfMrsExit mrs_exit, const int condition_stack_size, const int handler_stack_size, const int exit_code)

     This function initializes the Chf library using a custom message retrieval subsystem, and returns to the caller a condition code; that code will be either CHF_S_OK if the initialization was successful, or one of the other values listed below.

     Arguments:

     app_name
          is the name of the application. It can be used by condition handlers to print out error messages.

     options
          is the bitwise or of the following option identifiers:

          CHF_DEFAULT
               Represents the default value for all options.

          CHF_ABORT
               If this flag is set, the Chf library will use abort() instead of either exit(exit_code) or pthread_exit(exit_code) to abort the application when an unrecoverable exception occurs.  This forces a core dump that may be useful for debugging purposes.

               Win32: This flag is not fully supported; the Chf library invokes exit(EXIT_FAILURE) instead of abort() (not supported by Win32) to abort the application.

     mrs_data
          Private data pointer for the custom message retrieval subsystem.

     mrs_get
          Message retrieval subsystem's 'Get Message' function.

     mrs_exit
          Message retrieval subsystem's 'Exit' function.

     condition_stack_size
          Size of the condition stack.  This value determines the maximum number of pending conditions, i.e. conditions that have been generated but have not been signaled yet.  A condition that is being signaled is still pending until the signaling sequence completes.

     handler_stack_size
          Size of the handler stack.  This value determines the maximum number of active condition handlers the application can have.  A condition handler is activated when a ChfPushHandler() is executed for it, and it is deactivated when the corresponding ChfPopHandler() completes. In addition, each execution of the ChfTry macro also pushes a condition handler; the handler is removed when ChfEndTry is executed.

     exit_code
          This is the exit code the Chf library will use to exit either the application or the offending thread when an unrecoverable exception occurs.

     Return values:

     CHF_S_OK
          The Chf library has been successfully initialized.

     CHF_F_MALLOC
          Dynamic memory allocation failed during initialization.

     Notes:

       * It's necessary to invoke successfully ChfInit(), either directly or indirectly, through another Chf initialization function, before using any other Chf function; otherwise, they will either fail and abort the application (when multithreading support is not enabled), or have unpredictable results (when multithreading support is enabled).

       * This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT if the Chf library has already been initialized before.  Notice that when multithreading support is enabled, ChfInit() must be invoked only once before starting multithreaded operation.

ChfMsgcatInit()
===============

 -- Function: int ChfMsgcatInit (const char *app_name, const ChfOptions options, const char *msgcat_name, const int condition_stack_size, const int handler_stack_size, const int exit_code)

     This function initializes the Chf library and activates the message retrieval subsystem based on catgets(), offering support for text-based internationalized applications.

     The argument msgcat_name is the name of the application/locale specific message catalog to be used to retrieve the messages. The other arguments have the meaning already described for ChfInit(), see ChfInit().

     Win32:  This function is not supported and always returns CHF_F_NOT_AVAILABLE.

     Return values:

     CHF_S_OK
          The Chf library has been successfully initialized.

     CHF_F_SETLOCALE
          the setlocale() function has failed during initialization.

     CHF_F_CATOPEN
          the catopen() function has failed and therefore the message catalog was not opened.

     CHF_F_MALLOC
          Dynamic memory allocation failed during initialization.

     CHF_F_NOT_AVAILABLE
          Function not available on current platform.

     Notes:

       * It's necessary to invoke successfully either ChfMsgcatInit(), or another Chf initialization function described in this chapter, before using any other Chf function.

       * This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT if the Chf library has already been initialized before.

ChfStaticInit()
===============

 -- Function: int ChfStaticInit (const char *app_name, const ChfOptions options, const ChfTable *table, const size_t table_size, const int condition_stack_size, const int handler_stack_size, const int exit_code)

     This function initializes the Chf library and activates the static message retrieval subsystem using the message table table, of table_size elements. This is the simplest message retrieval subsystem currently supported, and supports applications that must work with minimal operating system assistance.

     Return values:

     CHF_S_OK
          The Chf library has been successfully initialized.

     CHF_F_MALLOC
          Dynamic memory allocation failed during initialization.

     Notes:

       * It's necessary to invoke successfully either ChfStaticInit(), or another Chf initialization function described in this section, before using any other Chf function.

       * This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT if the Chf library has already been initialized before.

 -- Data type: ChfTable
     This data type is a structure representing an element of a standalone message table, to be used with the static message retrieval subsystem. Each table element associates a pair (module_id, code) with a message fragment or template. It has the following fields:

     int module
          The module identifier of the message fragment to be defined.

     int code
          The condition code of the message fragment to be defined.

     char *msg_template
          The message fragment or template associated with (module_id, code).

ChfWin32Init()
==============

 -- Function: int ChfWin32Init (const char *app_name, const ChfOptions options, HINSTANCE instance, const int condition_stack_size, const int handler_stack_size, const int exit_code)

     This function initializes the Chf library and activates the message retrieval subsystem based on the Win32 function LoadString(), offering limited support for Win32 internationalized applications.

     This function does not support message sets, so the linear message id passed to it is made by module_id*1000 + condition_code.

     The instance argument is the handle of the calling application instance; it will be used as an argument to LoadString().  All other arguments have the meaning already described for ChfInit(), see ChfInit().

     Win32: This function is supported on Win32 only, and always returns CHF_F_NOT_AVAILABLE when invoked on Unix platforms. Notice also that on Win32 the ChfAbort() function will be able to print out the abort message only if Chf has been initialized correctly and the GetActiveWindow() function yields a valid window handle when ChfAbort() is invoked.

     Return values:

     CHF_S_OK
          The Chf library has been successfully initialized.

     CHF_F_MALLOC
          Dynamic memory allocation failed during initialization.

     CHF_F_NOT_AVAILABLE
          Function not available on current platform.

     Notes:

       * It's necessary to invoke successfully either ChfWin32Init(), or another Chf initialization function described in this chapter, before using any other Chf function.

       * This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT if the Chf library has already been initialized before.

ChfExit()
=========

 -- Function: void ChfExit (void)

     This function shuts down the Chf library and returns nothing to the caller; after calling ChfExit() the application can continue, but any subsequent call to other Chf functions, except to the initialization functions, will either abort the application using ChfAbort() with abort code CHF_ABORT_INIT (when multithreading support is not enabled) or have unpredictable results (when multithreading support is enabled).

     Notes:

       * This function will call ChfAbort() with abort code CHF_ABORT_INIT if Chf hasn't been initialized yet.

       * When multithreading support is enabled, it is responsibility of the application designer to ensure that no other Chf functions, except initialization functions, will be called after this function has been.

ChfAbort()
==========

 -- Function: void ChfAbort (const int abort_code)
     This function prints the message associated with abort_code and then immediately aborts either the application or the invoking thread. The abort is performed either:

       * using abort() if either Chf has not been correctly initialized yet, or the Chf option flag CHF_ABORT has been set during initialization.  Since this is mainly intended to be a debugging aid, abort() is used whether or not multithreading support is enabled. Win32: The CHF_ABORT flag is not fully supported; when this flag is set the Chf library invokes exit(EXIT_FAILURE) instead of abort() (not supported by Win32) to abort the application.

       * using either exit(exit_code) or pthread_exit(exit_code) if the above mentioned flag is clear.  The former function is used when multithreading support is not enabled, the latter when it is.  The argument exit_code is the value of the exit_code argument given to the Chf initialization function.

     The following abort messages are currently defined:

     "Not initialized"
          The Chf library has not been correctly initialized.

     "Temporary message buffer overflow"
          The temporary message buffer used by Chf to combine the additional condition arguments with the message template was overflowed. Shorten the message.

     "Invalid action from last chance handler"
          The condition handler registered first, that usually is the default condition handler, has returned an invalid action code.

     "Already initialized"
          One of the Chf initialization functions was called when Chf was already initialized.

     "Unwind request while unwinding"
          A condition handler has requested an unwind action when Chf was already unwinding.  This is not allowed.

     "Improperly handled condition"
          No condition handlers were able to properly handle the current condition group.  This should not occur if the default condition handler is active as the last chance handler.

     "Fatal condition while unwinding"
          A fatal condition was signaled while Chf was unwinding.  This is not allowed because is would require another unwind to be recovered.

     "Condition stack overflow"
          An attempt was made to generate a condition while the Chf condition CHF_F_COND_STACK_FULL (condition stack full) was being signaled.

     "Can't prime a new Chf context"
          Chf was unable to clone the master Chf context when a thread first attempted to access its context.  This message is generated only when multithreading support is enabled.

     "Pthread interaction failed"
          An unrecoverable error occurred when Chf invoked a pthread function.  This message is generated only when multithreading support is enabled.

     No message is printed if the abort_code is CHF_ABORT_SILENT; this code is used, for example, by the default condition handler to terminate the application when a CHF_FATAL condition occurs.

     Notes:

       * This function is called by other Chf library functions only when they detect an unrecoverable failure. Even if it has a public prototype, the user should never invoke this function.

       * Win32: Since the standard error stream is not available, this function is able to display the abort message only if it is able to obtain a valid main window handle, that is, if the GetActiveWindow() function yields a valid value when invoked.

Library header
==============

The library header Chf.h must be included by all source modules using the Chf library. Prior to include it, the following macro definitions must be made:

CHF_MODULE_ID
     This macro must expand into an integer representing the module identifier of the module the source file belongs.  The module identifiers with value less than 10 are reserved for Chf internal use. See Reserved module identifiers.

     All conditions generated by the source module will use this module identifier. Even if this macro could in principle be redefined through the source, it is generally inconvenient to have functions belonging to different application's modules in the same source file, so it is discouraged. This definition is mandatory.

CHF_EXTENDED_INFO
     If this macro is set, all conditions generated by the source module will include the file name and the line number as additional information items.  This can be useful for debugging purposes.  This definition is optional.

Win32: The tchar.h system header must be included before including Chf.h.

Condition generation and signal
*******************************

Generating a condition
======================

When an unusual or noteworthy condition occurs, the application may generate a condition that describes it.

ChfCondition
------------

 -- Macro: ChfCondition ...

     This macro starts the definition of a condition using the current application's module specifier CHF_MODULE_ID; it must be followed by these additional, mandatory, arguments:

     int condition_code
          This argument is the condition code; it uniquely identifies a condition within the scope of the current application's module.

     ChfSeverity severity
          This value represents the severity of the condition. See ChfSeverity.

     Moreover, zero or more additional argument can follow; they will be immediately combined with the message template corresponding to the pair (CHF_MODULE_ID, condition_code) using sprintf(). All format specifiers of the latter function are allowed in the message template.

     As stated before, the message template is retrieved using the message retrieval subsystem (see Message retrieval) using the current Chf module identifier CHF_MODULE_ID as the message set, and the condition code condition_code as the message number.

     If in the condition stack there isn't any condition yet, a new condition group is created, otherwise the new condition is linked on top of the current condition group.  The condition stack supports multiple nested condition groups for use in condition handlers: any condition generated inside a condition handler starts a new condition group that can be individually handled and signaled.

     This function calls the Chf function ChfAbort() if one of the following unrecoverable error condition occurs:

       * Chf has not been initialized correctly (abort code CHF_ABORT_INIT)

       * there is an overflow in the internal buffer used during the generation of the partial message associated with the condition (abort code CHF_ABORT_MSG_OVF)

       * there was an attempt to generate a condition while the Chf condition CHF_F_COND_STACK_FULL (condition stack full) was being signaled (abort code CHF_ABORT_COND_STACK_OVF)

ChfEnd
------

 -- Macro: ChfEnd

     This macro terminates the generation of the condition, started with ChfCondition; it must be followed by ;. For example:

          ChfCondition
                  <condition_code>, <severity>, <additional_info>
          ChfEnd;

ChfSeverity
-----------

 -- Data type: ChfSeverity
     The severity code of a condition is an enumerated type, and can assume one of the following values:

     CHF_SUCCESS
          This condition represents the successful completion of an application's action; when generated in an application's module it is usually signaled immediately after generation, it is not reported to the module caller and does not disrupt the normal control flow of the application.

     CHF_INFO
          This condition conveys an information message to the application's user, but it in no way represents an error indication; when generated in an application's module it is usually signaled immediately after generation, it is not reported to the module caller and does not disrupt the normal control flow of the application.

     CHF_WARNING
          This condition has been generated to warn the application's user that a potentially abnormal situation occurred, but the application's module was able to complete successfully the action that was requested to it, perhaps incompletely.  It is usually not signaled immediately after generation, it is reported to the caller using some status reporting mechanism, and it requires only a minimal, local disruption of the control flow of the application to be recovered.

     CHF_ERROR
          An error occurred and the application's module was not able to complete its work, but it still may be possible to perform a local recovery and resume the normal application's control flow.  It is usually not signaled immediately after generation and it is reported to the caller using some status reporting mechanism.

     CHF_FATAL
          A fatal error occurred, and a global recovery action is required, because the normal application's control flow can't be safely resumed. The Chf library enforces that, when ChfSignal() is called with a fatal condition, the control will never return to the instruction following the ChfSignal() call.  It is usually signaled immediately after generation.

ChfErrnoCondition
-----------------

 -- Macro: ChfErrnoCondition

     This macro generates a condition from the current value of the errno variable. The module identifier of the condition is set to the special value CHF_ERRNO_SET. The severity is always CHF_ERROR.

     Win32: This macro is not supported and has no effect when invoked.

Signaling a condition
=====================

A condition already generated with one of the macros discussed in the previous section can be signaled using the following function, that starts the condition signaling sequence.

ChfSignal()
-----------

 -- Function: void ChfSignal (void)

This function signals the current condition group. If the final action requested by the condition handlers invoked during the signaling operation was CHF_CONTINUE and the overall severity of the condition group was lower than CHF_FATAL, this function will return to the caller, otherwise either a non-local control transfer will occur (CHF_UNWIND) or the invoking application/thread will be terminated.

During the signaling of a condition group the following actions are accomplished:

     First of all, a check is made to ensure that the Chf subsystem has been correctly initialized.  If not, the function ChfAbort is called.

     The state of Chf is changed to CHF_SIGNALING if it was CHF_IDLE, and to CHF_SIGNAL_UNWINDING if it was CHF_UNWINDING.

     If there is not any active condition group, the function does nothing more, restores the saved Chf state and returns to the caller.

     If there is an active condition group, the currently active condition handlers are invoked, starting from the one registered last.

     Each condition handler can recursively invoke ChfGenerate() and ChfSignal(). ChfGenerate(), when invoked, will create a new condition group, and ChfSignal() will signal the condition group just created.

     When the Chf state is CHF_SIGNALING, any new condition group generated but not yet signaled when the current handler exits is merged with the condition group currently being signaled, in order to allow the condition handlers to add their own conditions to the condition group. If the severity of the previous condition group was CHF_FATAL, the severity of the new group is forced to CHF_FATAL, too. When the Chf state is CHF_UNWINDING, the condition group for which the unwind has been requested is 'frozen', no further modifications are allowed on it, and the new condition group is simply discarded.

     The condition handlers calling sequence terminates when either the handler stack is exhausted (all condition handlers have been called) or when an handler requests either the action CHF_CONTINUE, CHF_UNWIND or CHF_UNWIND_KEEP. Please note that the CHF_CONTINUE action cannot be requested if the severity of the condition group is CHF_FATAL; when this occurs, the action is automatically changed to CHF_RESIGNAL instead.

     If the action CHF_CONTINUE has been requested and it is allowed, Chf discards the current condition group, restored the saved Chf state and returns to the instruction following the call to ChfSignal(), that started the signaling activities.

     If either one of the actions CHF_UNWIND or CHF_UNWIND_KEEP has been requested and it is allowed, that is, no other unwinds are already in progress, the current Chf state is set to CHF_UNWINDING and all handlers that were registered after the handler that requested the unwind are called again, starting from the one registered last, so that they can perform any final cleanup operation they require for proper operation. The handler that requested the unwind is called again, too, in the same fashion.  Immediately after this, the unwinded condition handlers are removed from the handler stack and discarded, the current condition group and any other group generated after it are destroyed, the Chf state is changed back to CHF_IDLE and a non-local control transfer is executed through a siglongjmp(), using the unwind_context associated with the handler that requested the unwind as target.  The ChfSignal() invocation that started the signaling activities will never return to the caller.  If the requested action is CHF_UNWIND_KEEP everything goes as described above, except that the current condition group and any other group generated after it are not destroyed, so they can still be accessed after the non-local control transfer has taken place.

     If no handlers were able to handle the condition, the invoking application/thread is terminated with ChfAbort().

     If an handler returned an invalid action code and it is not the condition handler that was registered first, a new condition group is generated and immediately signaled, containing the condition CHF_F_INVALID_ACTION with module identifier CHF_SET.

Condition handling
******************

Condition handlers
==================

The Chf library maintains a LIFO stack of active condition handlers. Each condition handler is a function which has type ChfHandler. Moreover, the functions described below are available to push and pop a condition handler to/from the condition handler stack.

ChfHandler
----------

 -- Data Type: ChfHandler

     The condition handler data type is defined as follows:

          typedef				/* Condition handler */
            ChfAction (*ChfHandler)(
              const ChfDescriptor *,
              const ChfState,
              ChfPointer
          );

     The condition handler receives the following arguments when invoked:

     const ChfDescriptor *
          is a pointer to the condition group that is being signaled.

     const ChfState
          represents the current state of the condition handling facility.

     ChfPointer
          is a copy of the handler_context pointer passed to the ChfPushHandler() invocation that established the condition handler.

     The handler must return an action code, represented by a value of type ChfAction.

     Before returning, the condition handler can generate other conditions, without signaling them, with the purpose of better specify the current condition group; these conditions will be added on top of the current group when the handler returns.  The next handler, if any, will be invoked on the updated condition group.

     The severity CHF_FATAL is 'sticky', that is, any new condition linked to a condition group where the topmost condition has that severity will be forced to the same severity level.

     If the condition handler itself generates and signals a new condition group, its superior condition handlers will be invoked on the new condition group only. When the new signaling sequence is completed, the old sequence will be resumed if appropriate, i.e. if a superior handler has requested the CHF_CONTINUE action and the severity of the new condition group allows it to do so.

ChfPushHandler()
----------------

 -- Function: void ChfPushHandler (ChfHandler new_handler, void *unwind_context, ChfPointer handler_context)

     This function pushes the new condition handler new_handler with its associated siglongjmp() context, pointed by unwind_context, into the handler stack.  If new_handler is CHF_NULL_HANDLER, the special builtin structured condition handling helper is pushed; see Structured condition handling, for further information.

     Moreover, this function saves a copy of the opaque pointer handler_context; it will be passed to new_handler upon each subsequent activation, and therefore can be used as a private handler context pointer. The user must ensure that the information pointed by handler_context, if any, will remain valid until new_handler is popped from the condition stack.

     The pointer handler_context may be set to the special (null) value CHF_NULL_POINTER to indicate that the handler hasn't any private context information.

     If, in the future, the handler will request the CHF_UNWIND action, the sigsetjmp() function invocation that established unwind_context will appear to return again.

     The pointer unwind_context can be the reserved (null) pointer CHF_NULL_CONTEXT; in this case, if the handler will request the CHF_UNWIND action, the invoking application/thread will be silently terminated calling ChfAbort() with abort code CHF_ABORT_SILENT.

     If an error occurs during the execution, the function generates and immediately signals one of the conditions listed below;  the function will never return directly to the caller, since all conditions have severity CHF_FATAL.

     CHF_F_BAD_STATE
          Bad Chf state for requested operation. A new handler can be added to the handler stack only if the condition handling subsystem is idle.

     CHF_F_HDLR_STACK_FULL,
          The handler stack is full and the new handler can't be added.

     Notes:

       * This function will call ChfAbort() with abort code CHF_ABORT_INIT if Chf hasn't been initialized.

ChfPopHandler()
---------------

 -- Function: void ChfPopHandler (void)

     This function pops the topmost condition handler from the handler stack and returns to the caller.

     If an error occurs during the execution, the function generates and immediately signals one of the conditions listed below; the function will never return directly to the caller, since all conditions have severity CHF_FATAL.

     CHF_F_BAD_STATE
          Bad Chf state for requested operation. Handlers can be removed from the handler stack only if the condition handling subsystem is idle.

     CHF_F_HDLR_STACK_EMPTY
          The handler stack is empty.

     Notes:

       * This function will call ChfAbort() with abort code CHF_ABORT_INIT if Chf hasn't been initialized.

Condition descriptors
=====================

 -- Data Type: ChfDescriptor

     The condition descriptor contains all information Chf has about a condition code. Its fields must not be accessed directly, but using the macros the Chf library provides for this purpose.

The following macros are provided to access a condition descriptor:

 -- Macro: ChfDescriptor * ChfGetNextDescriptor (Chf Descriptor *d)

     Returns to the caller the condition descriptor that hierarchically follows d in the current condition group, or the special value CHF_NULL_DESCRIPTOR if d is the last descriptor of the group.

 -- Macro: int ChfGetModuleId (Chf Descriptor *d)

     Returns to the caller the identifier of the module that generated the condition d.

 -- Macro: int ChfGetConditionCode (Chf Descriptor *d)

     Returns to the caller the condition code of the condition d.

 -- Macro: ChfSeverity ChfGetSeverity (Chf Descriptor *d)

     Returns to the caller the severity level of the condition d. See ChfSeverity.

 -- Macro: int ChfGetLineNumber (Chf Descriptor *d)

     Returns to the caller the source line number where the condition d was generated. This piece of information is available only if the Chf compile-time option CHF_EXTENDED_INFO was set when the source file that generated the condition was compiled, otherwise the special value CHF_UNKNOWN_LINE_NUMBER is returned instead.

 -- Macro: const char * ChfGetFileName (Chf Descriptor *d)

     Returns to the caller the file name where the condition d was generated. This piece of information is available only if the Chf compile-time option CHF_EXTENDED_INFO was set when the source file that generated the condition was compiled, otherwise the special value CHF_UNKNOWN_FILE_NAME is returned instead.

 -- Macro: char * ChfGetPartialMessage (Chf Descriptor *d)

     Returns to the caller the partial condition message that was associated with the condition d when it was generated. The partial condition message contains the result of the execution of sprintf() using the message template associated with the condition as format, and the ancillary condition arguments as additional arguments.

Condition handling state
========================

 -- Data Type: ChfState
     This enumerated type describes the current state of the Condition Handling Facility. It can have one of the following values:

     CHF_UNKNOWN
          The Chf library has not been initialized, or the initialization has failed. Normally, the user never sees this value, because the main use of ChfState values is inside condition handlers, and they are never invoked when the Chf library has not been initialized correctly.

     CHF_IDLE
          This is the normal state of the condition handling facility. In this state, the condition stack contains zero or more condition descriptors corresponding to conditions that have been generated but have not been signaled yet. The application follows its normal control flow.

     CHF_SIGNALING
          The Condition Handling Facility is invoking the hierarchy of active condition handlers because ChfSignal() has been invoked with a non-empty condition stack. None of the condition handlers invoked so far has requested an unwind operation.

     CHF_UNWINDING
          The Condition Handling Facility is unwinding one or more condition handlers, because another, superior, handler has requested an unwind operation. All handlers being unwinded are called once with Chf in this state to allow them to perform any final cleanup operation, and immediately after that they are removed from the condition stack.  During unwind, the action code returned by the handler being unwinded is ignored.

     CHF_SIGNAL_UNWINDING
          While Chf was performing an unwind operation, one of the handlers being unwinded has signaled another condition group.  The superiors of the signaling handler are invoked with Chf in this state, then the unwind operation is resumed.

Handler actions
===============

 -- Data Type: ChfAction

     Enumerated type representing the set of mutually exclusive actions that a condition handler can request to the condition handling facility. It can have the following values:

     CHF_CONTINUE
          Resume the application from the instruction immediately following the ChfSignal() call that triggered the invocation of the condition handler. This action is not allowed if the severity of the current condition is CHF_FATAL, since this severity always requires global recovery to take place. In this case the CHF_CONTINUE is automatically changed to CHF_RESIGNAL when encountered.

     CHF_RESIGNAL
          Invoke the next handler in the handler stack with the same condition. This action must be used when the current condition handler was not able to recover the condition.

     CHF_UNWIND
          Unwind the application's stack and restore the unwind_context associated with the condition handler, performing a non-local control transfer. See ChfPushHandler().

     CHF_UNWIND_KEEP
          Unwind the application's stack and restore the unwind_context associated with the condition handler, performing a non-local control transfer.  Unlike CHF_UNWIND, the current condition group at the time of the unwind is not destroyed, and can still be accessed after the non-local control transfer has taken place.

Default handler
===============

The default condition handler of Chf is automatically pushed into the condition handler stack by the Chf initialization functions. It performs the following functions:

     if called during an unwind, it returns immediately to the caller, requesting the action CHF_RESIGNAL, else

     if the severity of the condition being signaled is greater than CHF_SUCCESS, it prints the messages associated with the entire condition group on stderr using the standard function ChfBuildMessage() to build the messages. Win32:   Since the standard error stream is not available, the default handler does not print anything out.

     if the severity of the condition being signaled is less than CHF_FATAL, it returns to the caller requesting the action CHF_CONTINUE, else

     if the CHF_FATAL condition was not signaled during an unwind operation, it returns to the caller requesting the action CHF_UNWIND, otherwise it requests the action CHF_RESIGNAL.

Structured condition handling
*****************************

Structured condition handling places the condition handler associated with a block of code next to the block itself, instead of into a separate procedure.  Even if it is less flexible and powerful with respect to the general handling method described before, it is often clearer and simpler to implement.  Chf supports both styles of condition handling; the structured condition handling is layered above the general one.

Syntax of structured condition handlers
=======================================

The syntax establishing a structured condition handler is as follows:

     ChfTry
             {
                     ... body ...
             }
     ChfCatch
             {
                     const ChfDescriptor *exc = ChfGetTopCondition();
                     ... condition handler ...
             }
     ChfEndTry;

The macros ChfTry, ChfCatch and ChfEndTry expand as follows:

     <<
     {
       sigjmp_buf _chf_sigjmp_buf;
       if(sigsetjmp(_chf_sigjmp_buf, 1) == 0)
       {
         ChfPushHandler(CHF_NULL_HANDLER, _chf_sigjmp_buf, CHF_NULL_POINTER);
     >>
             {
                     ... body ...
             }
     <<
         ChfPopHandler();
       }
       else
       {
     >>
             {
                     const ChfDescriptor *exc = ChfGetTopCondition();
                     ... condition handler ...
             }
     <<
         ChfDiscard();
       }
     }
     >>

Above, << and >> mark the starting and ending point of each macro expansion, respectively.

The body is either a statement or a block of statements (a block is shown in the example) to be protected.  If an exception with severity equal to CHF_FATAL is signaled during the execution of body, control is transferred to the condition handler following ChfCatch.

The handler can then retrieve the condition group associated with the exception using ChfGetTopCondition(), as shown in the example, and must perform one of the following actions:

  * Handle the exception; when the condition handler completes, that is, its last statement has been executed, the current condition group is automatically discarded and control is transferred to the code that follows ChfEndTry.

  * Resignal the exception, possibly after adding one or more conditions to it; in this case, the handler explicitly calls ChfSignal(). In response, Chf continues to search for a handler that actually handles the exception, in the ChfTry/ChfCatch blocks enclosing the current one, in inner to outer order.  The control will never be returned to the resignaling handler.

Notice that it is not possible for the condition handler to resume the thread of execution disrupted by the exception. Resuming is possible only when the signaled condition has severity less than CHF_FATAL: in this case the condition handler is not invoked, the usual condition signaling sequence takes place and the CHF_CONTINUE action requested by any general condition handler is honored.

Thus, structured condition handling is further simplified because:

  * Conditions with severity less than CHF_WARNING are usually automatically signaled immediately after generation and are not reported to the caller; they are informational conditions only, and they must not disrupt the normal control flow of the application.  Accordingly, the structured condition handler is not invoked when a success/informational condition is signaled in its ChfTry body.

  * CHF_WARNING conditions generated by a module are usually not signaled immediately after generation, but are reported to the caller using some status reporting mechanism.  When a warning condition is generated by a module, it indicates that its execution has been at least partially successful; this usually means that the disruption of the control flow of the application can be kept to a minimum and handled locally.  Accordingly, the structured condition handler is not invoked when a warning condition is signaled in its ChfTry body.

  * CHF_ERROR conditions generated by a module indicate that the module execution has been unsuccessful; they are usually reported to the caller using some status reporting mechanisms.  An error condition indicates that a significant disruption of the control flow of the application is required to recover, and that local handling is still possible but could not suffice.  Accordingly, the structured condition handler is not invoked when an error condition is signaled in its ChfTry body.  Notice also that if the ChfTry body is not able to locally recover the condition, it can add a CHF_FATAL condition to the condition group and signal the modified group, thus triggering the invocation of the structured condition handler.

  * CHF_FATAL conditions are usually signaled immediately after generation and always require a global recovery action. Accordingly, the structured condition handler is invoked when a warning condition is signaled in its ChfTry body.

Notice also that control is transferred to the structured exception handler by means of a siglongjmp() sequence.  Therefore, if the structured exception handler needs to access automatic variables, they should be declared volatile.

If no handlers are able to handle an exception, the default Chf condition handler is invoked last. See Default handler, for a more detailed description of its operation.

If no exceptions are raised during the execution of body, the condition handler is skipped and execution continues immediately after ChfEndTry.

Chf behavior during structured condition handling
=================================================

When a condition is signaled and the special structured condition handling helper is invoked, the following sequence of events occurs:

  * Chf transitions to state CHF_SIGNALING and starts the handler calling sequence.

  * If any handler invoked before the structured condition handling helper requests either the CHF_CONTINUE or the CHF_UNWIND action, that action is executed as usual.

  * When invoked, the structured condition handling helper immediately requests the action CHF_UNWIND_KEEP.

  * Chf transitions to state CHF_UNWINDING and invokes all handlers registered after the structured condition handling helper again.  Such handlers can then perform whatever final cleanup operations they need.

  * Chf invokes the structured condition handling helper again; the helper immediately returns without doing anything, since Chf is now in state CHF_UNWINDING.

  * Chf transitions to the CHF_IDLE state and performs a non-local control transfer to the structured condition handler's unwind context; this corresponds to the code that follows ChfCatch. The current condition group is not removed from the condition stack.

  * Since the current condition group has not been removed from the condition stack, the code that follows ChfCatch can freely access the condition group using ChfGetTopCondition() and ChfGetNextDescriptor().

  * If any new condition is generated in this phase, it is merged with the current condition group; the condition group can be re-signaled at will by invoking ChfSignal().

  * Immediately after the code that follows ChfCatch completes, ChfDiscard() is automatically invoked to remove the topmost condition group from the stack.

If the structured condition handling helper is invoked when Chf is in CHF_SIGNAL_UNWINDING, it immediately requests the CHF_RESIGNAL action, because a further unwind request is neither allowed nor useful in this case.

The structured condition handling helper makes no use of handler_context.

In summary, the following structured condition handling macros are currently available:

ChfTry
------

 -- Macro: ChfTry ...

     This macro introduces a new structured condition handling body. The body is either a statement or a block of statements to be protected: if a CHF_FATAL exception is signaled during its execution, control is transferred to the condition handling code following ChfCatch.

ChfCatch
--------

 -- Macro: ChfCatch ...

     This macro must follow ChfTry and introduces a new structured condition handler following it; the handler can be either a statement or a block of statements.  If a CHF_FATAL exception is signaled during the execution of the body protected by ChfTry, control is transferred to the condition handling code following ChfCatch.

ChfEndTry
---------

 -- Macro: ChfEndTry

     This macro marks the end of a body/condition handler pair.

Chf conditions
**************

The Chf library can itself generate and signal the following conditions. All conditions are fatal, i.e. require a global recovery action to take place to be handled.

 -- Macro: CHF_F_COND_STACK_FULL
     The user attempted to generate a condition while the condition stack was full; this condition is generated anyway using a reserved stack entry. No additional conditions can be generated until the handling of this condition is completed, otherwise the application will be aborted.

 -- Macro: CHF_F_HDLR_STACK_FULL
     The function ChfPushHandler() was called while the handler stack was full; the new handler is not registered and this condition is signaled using the old handler hierarchy.

 -- Macro: CHF_F_HDLR_STACK_EMPTY
     A function requiring at least one condition handler to be active found an empty handler stack.

 -- Macro: CHF_F_BAD_STATE
     The current state of Chf was not appropriate to invoke a certain Chf function.  The invoked function does not do anything but signaling this condition.

 -- Macro: CHF_F_INVALID_ACTION
     During the current signaling sequence an handler returned an invalid action code. A new condition group, containing only this condition, is generated and immediately signaled.  The first handler called to handle the new condition group will be the handler registered immediately before the faulting one.

 -- Macro: CHF_F_MALLOC
     Many Chf functions dynamically allocate memory.  This condition is generated and signaled when a dynamic memory allocation attempt failed, and the library was unable to recover in another way.

 -- Macro: CHF_F_NOT_AVAILABLE
     The function is not available on the current platform.  This condition is returned by some Chf initialization functions instead of being signaled, because it prevents the Chf subsystem to be initialized and, therefore, to function properly.

 -- Macro: CHF_F_SETLOCALE
     The setlocale() function failed during the initialization of the message catalog-based message retrieval subsystem.  This condition is returned by the Chf initialization function instead of being signaled, because it prevents the Chf subsystem to be initialized and, therefore, to function properly.

 -- Macro: CHF_F_CATOPEN
     The catopen() function failed during the initialization of the message catalog-based message retrieval subsystem.  This condition is returned by the Chf initialization function instead of being signaled, because it prevents the Chf subsystem to be initialized and, therefore, to function properly.

Message retrieval
*****************

The message retrieval subsystem is responsible to retrieve the message fragments needed by the Chf library to generate the messages associated with condition codes.

Basically, given a message set identifier (that usually has the same value as the module identifier for which the condition is being generated) and a message number (that usually has the same value as the condition code), it must retrieve and return to the caller a suitable message associated to it. It the message cannot be found, it must return a default message pointer Chf provides.

Some module identifiers/message sets are used for special purposes. See Reserved module identifiers.

Retrieving a message
====================

The following functions directly interact with the message retrieval subsystem:

ChfGetMessage()
---------------

 -- Function: const char * ChfGetMessage (const int module_id, const int condition_code, const char *default_message)

     This function transparently uses the Chf message retrieval subsystem to retrieve the message associated with the pair (module_id, condition_code) and returns a pointer to it. The function will return default_message instead, if it was not able to retrieve the message. If module_id==CHF_ERRNO_SET, the function will additionally invoke strerror(), if necessary, to retrieve the message.

     Win32: Since strerror() is not supported, the additional last-chance translation described above cannot be performed.

     Notes:

       * The returned pointer points to per-thread static storage, which can be overwritten by subsequent calls to this function.

ChfBuildMessage()
-----------------

 -- Function: char * ChfBuildMessage (const ChfDescriptor *descriptor)

     This function builds the message associated with the given condition descriptor and returns a pointer to a string containing it.

     The message has a standard format and provides the following information items:

       * The application's name given to the Chf library during initialization, if the condition is the topmost within a condition group; otherwise, the message starts with an horizontal tab character.

       * The name of the module that generated the condition.

       * If available, the file name and line number of the source where the condition was generated.

       * The severity code of the message.

       * The message associated with the condition code, including any ancillary information provided during condition generation.

     Notes:

       * This function will call ChfAbort() with abort code CHF_ABORT_INIT if Chf hasn't been correctly initialized.

       * The returned pointer points to per-thread static storage, which can be overwritten by subsequent calls to this function.

       * Win32: to save space, the application's name and severity code are not included in the message.

Reserved module identifiers
===========================

The following module identifiers are currently used by the Chf library; all module identifiers with value less than 10 are reserved for future use.

1
     This module identifier is used as a message set identifier to retrieve the names of the other application's modules.  In particular, to retrieve the name of the application's module m, the message m of message set 1 is retrieved.

2
     This module identifier is used to identify the conditions generated by the Chf library itself.

3
     This module identifier is used as a message set identifier to retrieve the messages associated with the errno error codes.  If the retrieve is unsuccessful, strerror() is used instead.

Win32: In addition, the current implementation of the message retrieval subsystem on Win32 imposes the following limits: 0 <= condition_code <= 999, 0 <= module_id <= 64.

Defining a new message retrieval subsystem
==========================================

The Chf library has a generic interface towards the message retrieval subsystem (MRS); therefore, any subsystem can be used, provided it follows the guidelines described below:

  * A copy of the mrs_data argument of ChfInit() is passed to each MRS function when it is invoked.  It can be used to hold a pointer to a private MRS data structures.

  * The mrs_get function has the following arguments:

       * A copy of the mrs_data argument of ChfInit().

       * An int, representing the module identifier of the condition message template/fragment to be retrieved.

       * An int, representing the condition code of the condition message template/fragment to be retrieved.

       * A char *, pointing to a default message template/fragment to be returned if the message retrieval fails.

     The function must attempt to retrieve the message template/fragment associated with the pair (module identifier, condition code) and return it to the caller; if the retrieval fails, the default message must be returned instead.

  * The mrs_exit function is invoked when Chf is about to exit. It has as argument a copy of the mrs_data argument or ChfInit() and  must perform any cleanup operation the MRS needs.  It is guaranteed that no other MRS function will be called after a successful invocation of mrs_exit.

  * When multithreading support is enabled, the MRS must support multiple, concurrent invocations made by different application's threads.  The same value of mrs_data is common to all threads and cannot be changed after initialization.

Multithreading support
**********************

When compiled appropriately, the Chf library supports multithreading, and maintains one Chf context per active thread.  Multithreading support is enabled when the cpp macro _REENTRANT is defined.

Win32:  Multitreading support is not provided yet.

When multithreading support is enabled, the following modifications to the default Chf behavior come into effect:

  * ChfInit() no longer initializes the condition stack, the condition handler stack and the message buffer immediately; instead, one copy of these context elements will be allocated for each active thread in the application, when it first invokes any other Chf function. Also, per-thread Chf contexts are dynamically destroyed when the owning thread terminates.

  * Condition generation and both general and structured condition handling work as before inside each application's thread. All threads share the same values for condition_stack_size, handler_stack_size and exit_code, but are otherwise fully independent with respect to condition handling, because each of them has a private Chf context.

  * ChfAbort() no longer exits the whole application when the CHF_ABORT Chf flag not set; instead, it exits the invoking thread only.

  * Almost all Chf functions can trigger the CHF_ABORT_GET_CONTEXT and CHF_ABORT_PTHREAD aborts, if they fail to correctly manage dynamic Chf context creation and/or pthread interactions.

  * All Chf functions, except ChfInit() and ChfExit() no longer check whether Chf has been correctly initialized or not: that check would have been too time-consuming in a multithreaded environment. As a consequence, invoking a Chf function when Chf has not been correctly initialized has unpredictable effects instead of neatly aborting the application with abort code CHF_ABORT_INIT.

  * The performance of the Chf condition generation and signaling functions is likely to decrease, because Chf must retrieve its per-thread context dynamically instead of referring to a static storage area.

Other useful macro definitions
******************************

 -- Macro: CHF_MAX_MESSAGE_LENGTH
     Represents the maximum allowable length for condition messages.

 -- Macro: CHF_LIBRARY_ID
     It is a string representing the Chf library identifier; the identifier contains also the library release number.

 -- Macro: CHF_MAJOR_RELEASE_NUMBER
     It is an integer representing the Chf library major release number.

 -- Macro: CHF_MINOR_RELEASE_NUMBER
     It is an integer representing the Chf library minor release number.

 -- Macro: CHF_MODULE_NAMES_SET
     Contains the integer identifier of the message set whose messages are used to retrieve the names of the application's modules. It has the value 1.

 -- Macro: CHF_SET
     Contains the integer identifier of the message set containing the condition messages Chf uses internally. It has the value 2.

 -- Macro: CHF_ERRNO_SET
     Contains the integer identifier of the message set containing the condition messages for errno codes. It has the value 3.

 -- Macro: ChfChar
     This macro is used to make portable declarations of character variables, and is mapped into either char or TCHAR depending on the platform.

 -- Macro: ChfText (x)
     This macro is used to make portable declarations of character strings, and is mapped into either x or _T(x) depending on the platform.

 -- Macro: ChfSigjmp_buf
     This macro is mapped into either sigjmp_buf or jmp_buf, depending on whether the platform supports sigjmp_buf contexts or not.

 -- Macro: ChfSigsetjmp (x,y)
     This macro is mapped into either sigsetjmp() or setjmp(), depending on whether the platform supports sigjmp_buf contexts or not. In the latter case argument x is ignored.

 -- Macro: ChfSiglongjmp (x,y)
     This macro is mapped into either siglongjmp() or longjmp(), depending on whether the platform supports sigjmp_buf contexts or not.

Other useful functions
**********************

 -- Function: void ChfDiscard (void)
     This function discards the topmost condition group currently in the condition stack, without signaling it. The function does nothing if the condition stack is empty.

     This function uses the function ChfAbort() to abort the application if Chf has not been initialized correctly (abort code CHF_ABORT_INIT).

 -- Function: const ChfDescriptor * ChfGetTopCondition (void)
     This function returns to the caller a pointer to the topmost condition of the current condition group. It generates and immediately signals the condition CHF_F_BAD_STATE if the current condition group is empty.

     This function can be useful to retrieve the complete description of a condition for functions that, when an error occurs, generate a condition and return to the caller a generic error indication, such as a distinguished return value.

     The same function can also be useful in a structured exception handler, to retrieve the condition descriptor associated with the exception to be handled.

     Notes:

       * During condition signaling, Chf creates a new, empty, condition group immediately before starting the invocation sequence of the condition handlers, as previously described.  Therefore ChfGetTopCondition(), when called from a condition handler, will return a pointer to the top condition generated during the handling ONLY, if any, and NOT to the top condition of the condition group being signaled.  The latter pointer is directly available, as an argument, to condition handlers.

       * When called from a structured exception handler, ChfGetTopCondition() works as expected, that is, it returns a pointer to the top condition of the condition group associated with the exception being handled.

       * This function will call ChfAbort() with abort code CHF_ABORT_INIT if Chf hasn't been correctly initialized.

       * The returned pointer is no longer valid when any other Chf function is called after ChfGetTopCondition().

Changes since release 1
***********************

  * Chf now supports structured condition handling. See Structured condition handling.

  * Chf now supports multithreaded applications; each thread has its own Chf context. Multithreading support is conditionally enabled by linking a multithreaded version of the Chf library. See Multithreading support.

  * Chf now supports the conditional save and restore of signal masks in unwind contexts (sigjmp_buf contexts instead of jmp_buf).

  * The method of passing unwind contexts as arguments is now consistent with that used by sigsetjmp(); in particular, it is no longer necessary to reference the contexts with &.

  * Some more useful macro definitions have been added to Chf.h. See Other useful macro definitions.

Changes since release 2.1
*************************

  * Chf now offers limited support of Unicode-based Win32 platforms, notably Windows CE.