saturnng/Chf/chf_init.c
2022-03-21 11:05:59 +01:00

780 lines
22 KiB
C

/* .+
.identifier : $Id: chf_init.c,v 2.2 2001/01/25 14:05:23 cibrario Exp $
.context : CHF, Condition Handling Facility
.title : $RCSfile: chf_init.c,v $, condition generation
.kind : C source
.author : Ivan Cibrario B.
.site : CSTV-CNR
.creation : 3-May-1996
.keywords : *
.description :
This module implements the CHF initialization function ChfInit()
.include : Chf.h
.notes :
$Log: chf_init.c,v $
Revision 2.2 2001/01/25 14:05:23 cibrario
Added partial Win32 support (Windows CE only).
Revision 2.1 2000/05/26 15:30:42 cibrario
- Renamed static context chf_context to _chf_context; it is now in
actual use only when mt support is not enabled, otherwise it is
used only as a prototype to prime the per-thread contexts on demand
- Conditional definition of mt support static variables
(context_mutex to access the static master context, fputs_mutex to
ensure the atomicity of DefaultHandler() while it is printing a
sequence of grouped condition messages) and functions (DestroyContext()
to destroy a per-thread context when the owning thread terminates)
- Deeply revised ChfInit() and ChfExit() to implement multithreading
support
- Implemented function _ChfGetContext() to return a pointer to the
per-thread Chf context, creating them on-demand.
Revision 1.6 1997/01/15 13:37:19 cibrario
The Chf condition handlers now have a third argument: the private handler
context pointer; the code has been updated accordingly.
Revision 1.4 1996/09/25 13:29:01 cibrario
Added static char[] variable rcs_lib_id; it contains the value of the
CHF_LIBRARY_ID macro. The header Chf.h sets that macro to its RCS Id,
that is also the Id of the whole Chf library.
Revision 1.1 1996/05/28 12:54:58 cibrario
Initial revision
.- */
#ifndef lint
static char rcs_id[] = "$Id: chf_init.c,v 2.2 2001/01/25 14:05:23 cibrario Exp $";
#endif
#include <stdio.h>
#include <stdlib.h>
#ifndef _WIN32
#include <errno.h>
#endif
#include <setjmp.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#endif
#include "Chf.h"
#include "ChfPriv.h"
/* -------------------------------------------------------------------------
Global and static variables
------------------------------------------------------------------------- */
/* Chf Library Id */
#ifndef lint
static ChfChar rcs_lib_id[] = CHF_LIBRARY_ID;
#endif
/* CHF context */
ChfContext _chf_context;
/* Message separator and severity names for ChfBuildMessage() */
static const ChfChar separator[] = CHF_MESSAGE_SEPARATOR;
static const ChfChar *severity_name[] = CHF_SEVERITY_NAMES;
/* -------------------------------------------------------------------------
Multithreading support
------------------------------------------------------------------------- */
#ifdef _REENTRANT
#include <pthread.h>
/* Mutex to access chf_context during initialization and exit;
mutex to puts condition messages on stderr (DefaultHandler)
*/
static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t fputs_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Chf data key */
static pthread_key_t data_key;
/* This function is called to destroy a Chf context when the owning
thread terminated.
*/
static void DestroyContext(void *context)
{
free(((ChfContext *)context)->message_buffer);
free(((ChfContext *)context)->handler_stack);
free(((ChfContext *)context)->condition_stack);
free(context);
}
#endif
/* -------------------------------------------------------------------------
Private functions
------------------------------------------------------------------------- */
/* .+
.title : DefaultHandler
.kind : C function
.creation : 17-May-1996
.description :
This function is the default condition handler of CHF. It's automatically
pushed into the condition handler stack by ChfInit() and 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 signalled 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.
- if the severity of the condition being signalled is less than
CHF_FATAL, it returns to the caller requesting the action
CHF_CONTINUE, else
- if the CHF_FATAL condition was NOT signalled during an unwind
operation, it returns to the caller requesting the action
CHF_UNWIND, otherwise it requests the action CHF_RESIGNAL.
WIN32:
- stderr stream is not available and modal MessageBox require the
parent's handle to work reliably; the default handler does not print
anything
.call :
action = DefaultHandler(desc, state, context);
.input :
const ChfDescriptor *desc, condition descriptor
const ChfState state, current CHF state
.output :
ChfAction action, action requested by the handler
.status_codes :
none
.notes :
1.1, 16-May-1996, creation
1.6, 15-Jan-1997, update:
- the Chf condition handlers now have a third argument: the private
handler context pointer.
2.1, 25-May-2000, update:
- added multithreading support
2.2, 22-Jan-2001, update:
- added Win32 support
.- */
static ChfAction DefaultHandler(
const ChfDescriptor *desc,
const ChfState state,
ChfPointer handler_context
)
{
ChfAction action;
const ChfDescriptor *d;
if(state == CHF_UNWINDING)
/* If CHF is unwinding, do nothing */
action = CHF_RESIGNAL;
else
{
/* Print the condition messages, if necessary. The sequence of fputs()
is done atomically if multithreading support is enabled.
In Win32, the default handler does not print anything.
*/
if(ChfGetSeverity(desc) > CHF_SUCCESS)
{
#ifdef _REENTRANT
if(pthread_mutex_lock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
#endif
#ifndef _WIN32
for(d = desc; d != CHF_NULL_DESCRIPTOR; d = ChfGetNextDescriptor(d))
fputs(ChfBuildMessage(d), stderr);
#endif
#ifdef _REENTRANT
if(pthread_mutex_unlock(&fputs_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
#endif
}
/* Determine the handler action */
switch(ChfGetSeverity(desc))
{
case CHF_SUCCESS:
case CHF_INFO:
case CHF_WARNING:
case CHF_ERROR:
{
/* Continue execution if the severity is less than CHF_FATAL */
action = CHF_CONTINUE;
break;
}
default:
{
/* The severity of the condition is CHF_FATAL; appempt to unwind if
the fatal condition wasn't signalled during another unwind.
*/
action = ((state == CHF_SIGNAL_UNWINDING) ? CHF_RESIGNAL: CHF_UNWIND);
break;
}
}
}
/* Return the action code to the Chf handler dispatcher */
return action;
}
/* .+
.title : scopy
.kind : C function
.creation : 16-May-1996
.description :
This function writes the NUL-terminated string pointed by 'q' starting
from 'p', including the NUL terminator and without trepassing 'p_end'.
The function returns a pointer to the NUL-terminator just written.
.call :
np = scopy(p, q, p_end);
.input :
char *p, starting position for the write
const char *q, pointer to the string to be copied
char *p_end, pointer to the end of the output area
.output :
char *np, pointer to the NUL-terminator just written
.status_codes :
none
.notes :
1.1, 16-May-1996, creation
.- */
static ChfChar *scopy(
ChfChar *p,
const ChfChar *q,
ChfChar *p_end
)
{
size_t q_len = ChfStrlen(q);
size_t p_avail = p_end - p;
if(q_len < p_avail)
{
ChfStrcpy(p, q);
p += q_len;
}
else if(p_avail > 1)
{
ChfStrncpy(p, q, p_avail-2);
p[p_avail-1] = '\0';
p = p_end;
}
return p;
}
/* -------------------------------------------------------------------------
Public functions
------------------------------------------------------------------------- */
/* .+
.title : ChfGetMessage
.kind : C function
.creation : 17-May-1996
.description :
This function retrieves the message associated with the pair
('module_id', 'condition_code') and returns a pointer to it. The function
will return 'default_message' if it isn't able to retrieve the message.
If module_id==CHF_ERRNO_SET, the function will use strerror(), if
necessary, to retrieve the requested message.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT
if CHF hasn't been correctly initialized.
NOTE: The returned pointer points to per-thread static storage, which will be
overwritten by subsequent calls to this function.
WIN32:
- strerror() is not supported; the last-chance translation of condition
codes in CHF_ERRNO_SET is not performed
.call :
message = ChfGetMessage(module_id, condition_code,
default_message);
.input :
const int module_id, module identifier
const int condition_code, condition code
const char *default_message, default message
.output :
const char *message, pointer to the retrieved message
.status_codes :
none
.notes :
1.1, 17-May-1996, creation
2.2, 22-Jan-2001, update:
- added Win32 support
.- */
const ChfChar *ChfGetMessage( /* Retrieve a condition message */
const int module_id,
const int condition_code,
const ChfChar *default_message
)
{
const ChfChar *message;
/* Check that CHF has been correctly initialized */
if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT);
if((message = chf_context.mrs_get(chf_context.mrs_data,
module_id, condition_code, default_message)) == default_message &&
module_id == CHF_ERRNO_SET)
# ifdef _WIN32
message = default_message;
# else
message = strerror(condition_code);
# endif
return(message);
}
/* .+
.title : ChfBuildMessage
.kind : C function
.creation : 16-May-1996
.description :
This function builds the message associated with the given condition
descriptor and returns a pointer to a string containing it.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT
if CHF hasn't been correctly initialized.
NOTE: The returned pointer points to per-thread static storage, which will 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
.call :
msg = ChfBuildMessage(descriptor);
.input :
const ChfDescriptor *descriptor, condition descriptor
.output :
char *msg, pointer to the message associated with 'descriptor'
.status_codes :
none
.notes :
1.1, 16-May-1996, creation
2.2, 22-Jan-2001, update:
- added Win32 support
.- */
ChfChar *ChfBuildMessage( /* Build a condition message */
const ChfDescriptor *descriptor
)
{
ChfChar *tmp_p;
ChfChar *tmp_end;
ChfChar def_message[CHF_DEF_MESSAGE_LENGTH];
ChfSeverity severity;
/* Check that CHF has been correctly initialized */
if(chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT);
/* Set appropriate pointers to the start/end of the message buffer */
tmp_p = chf_context.message_buffer;
tmp_end = tmp_p + CHF_MAX_MESSAGE_LENGTH;
#ifndef _WIN32
/* The message starts with "<app_name>: " if the condition is the first of
its condition group, with "\t" if not.
*/
if(descriptor == chf_context.condition_sp-1)
{
tmp_p = scopy(tmp_p, chf_context.app_name, tmp_end);
tmp_p = scopy(tmp_p, separator, tmp_end);
}
else
tmp_p = scopy(tmp_p, ChfText("\t"), tmp_end);
#endif
/* The message continues with the module name */
ChfSprintf(def_message, CHF_DEF_MID_MSG_FMT, ChfGetModuleId(descriptor));
tmp_p = scopy(tmp_p,
ChfGetMessage(CHF_MODULE_NAMES_SET, ChfGetModuleId(descriptor),
def_message), tmp_end);
/* Add also the extended information, if any */
if(ChfGetLineNumber(descriptor) != CHF_UNKNOWN_LINE_NUMBER)
{
tmp_p = scopy(tmp_p, ChfText(" "), tmp_end);
ChfSprintf(def_message, CHF_EXTENDED_INFO_FMT,
ChfGetFileName(descriptor), ChfGetLineNumber(descriptor));
tmp_p = scopy(tmp_p, def_message, tmp_end);
}
tmp_p = scopy(tmp_p, separator, tmp_end);
#ifndef _WIN32
/* Add the severity code of the message */
tmp_p = scopy(tmp_p,
((severity = ChfGetSeverity(descriptor)) < CHF_SUCCESS ||
severity > CHF_FATAL) ? CHF_UNKNOWN_SEVERITY : severity_name[severity],
tmp_end);
tmp_p = scopy(tmp_p, separator, tmp_end);
#endif
/* The message ends with the partial message from the descriptor */
tmp_p = scopy(tmp_p, ChfGetPartialMessage(descriptor), tmp_end);
(void)scopy(tmp_p, CHF_MESSAGE_TERMINATOR, tmp_end);
return chf_context.message_buffer;
}
/* .+
.title : ChfInit
.kind : C function
.creation : 13-May-1996
.description :
This function initializes CHF and returns to the caller a condition code;
that code will be either CHF_S_OK if the initialization was succesful,
or one of the other values listed below.
It's necessary to invoke succesfully ChfInit() before using any other CHF
function.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_DUP_INIT
if CHF has already been initialized before.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD
if a pthread operation fails.
.call :
cc = ChfInit(app_name, options,
mrs_data, mrs_get, mrs_exit,
condition_stack_size, handler_stack_size,
exit_code);
.input :
const char *app_name, Application's name
const ChfOptions options, Options
void *mrs_data, Message retrieval private data
ChfMrsGet mrs_get, 'GetMessage' function
ChfMrsExit mrs_exit, 'Exit' function
const int condition_stack_size, Size of the condition stack
const int handler_stack_size, Size of the handler stack
const int exit_code, Abnormal exit code
.output :
int cc, condition code
.status_codes :
CHF_F_MALLOC, FATAL, dynamic memory allocation failed
.notes :
1.1, 13-May-1996, creation
1.6, 15-Jan-1997, update:
- updated the call to ChfPushHandler() to accomodate its new interface.
2.1, 19-May-2000, update:
- added multithreading support
2.2, 22-Jan-2001, update:
- added Win32 support; a malloc() call was not portable.
.- */
int ChfInit( /* Generic initialization */
const ChfChar *app_name, /* Application's name */
const ChfOptions options, /* Options */
void *mrs_data, /* Message retrieval private data */
ChfMrsGet mrs_get, /* 'GetMessage' function */
ChfMrsExit mrs_exit, /* 'Exit' function */
const int condition_stack_size, /* Size of the condition stack */
const int handler_stack_size, /* Size of the handler stack */
const int exit_code /* Abnormal exit code */
)
{
int cc;
/* Check that CHF has not been initialized yet */
#ifndef _REENTRANT
if(_chf_context.state != CHF_UNKNOWN) ChfAbort(CHF_ABORT_DUP_INIT);
#else
/* Reentrant check; lock context_mutex first */
if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
if(_chf_context.state != CHF_UNKNOWN)
{
if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
ChfAbort(CHF_ABORT_DUP_INIT);
}
#endif
#ifndef _REENTRANT
if((_chf_context.condition_stack =
(ChfDescriptor *)
malloc((size_t)(condition_stack_size+1)*sizeof(ChfDescriptor))) ==
CHF_NULL_DESCRIPTOR)
cc = CHF_F_MALLOC;
else if((_chf_context.handler_stack =
(ChfHandlerDescriptor *)
malloc((size_t)handler_stack_size*sizeof(ChfHandlerDescriptor))) ==
(ChfHandlerDescriptor *)NULL)
{
free(_chf_context.condition_stack);
cc = CHF_F_MALLOC;
}
else if((_chf_context.message_buffer =
(ChfChar *)malloc(
(size_t)(CHF_MAX_MESSAGE_LENGTH) * sizeof(ChfChar))) == (ChfChar *)NULL)
{
free(_chf_context.condition_stack);
free(_chf_context.handler_stack);
cc = CHF_F_MALLOC;
}
else
#else
/* Reentrant init: condition_stack, handler_stack, message_buffer
are not needed in the master Chf context.
Init the Chf data key instead.
*/
_chf_context.condition_stack = CHF_NULL_DESCRIPTOR;
_chf_context.handler_stack = (ChfHandlerDescriptor *)NULL;
_chf_context.message_buffer = (char *)NULL;
if(pthread_key_create(&data_key, DestroyContext))
ChfAbort(CHF_ABORT_PTHREAD);
#endif
{
/* Initialize the CHF context */
_chf_context.app_name = app_name;
_chf_context.options = options;
_chf_context.mrs_data = mrs_data;
_chf_context.mrs_get = mrs_get;
_chf_context.mrs_exit = mrs_exit;
_chf_context.condition_stack_size = condition_stack_size;
_chf_context.handler_stack_size = handler_stack_size;
_chf_context.exit_code = exit_code;
_chf_context.condition_base = _chf_context.condition_sp =
_chf_context.condition_stack;
_chf_context.handler_sp = _chf_context.handler_stack;
_chf_context.state = CHF_IDLE;
#ifndef _REENTRANT
/* Push the default handler; in the reentrant case, this will be
done once per thread, when the thread-specific context is primed.
*/
ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER);
#endif
cc = CHF_S_OK;
}
#ifdef _REENTRANT
if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
#endif
return cc;
}
/* .+
.title : ChfExit
.kind : C function
.creation : 24-May-1996
.description :
This function shuts down CHF and returns nothing to the caller; after
calling ChfExit() the application can continue, but any subsequent call
to any other CHF function, except the inizialization functions, will abort
the application using ChfAbort() with abort code CHF_ABORT_INIT.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_INIT
if CHF hasn't been initialized.
NOTE: This function will call ChfAbort() with abort code CHF_ABORT_PTHREAD
if a pthread operation fails.
.call :
ChfExit();
.input :
void
.output :
void
.status_codes :
none
.notes :
1.1, 24-May-1996, creation
2.1, 19-May-2000, update:
- added multithreading support
.- */
void ChfExit(
void
)
{
/* Check that CHF has been correctly initialized */
#ifndef _REENTRANT
if(_chf_context.state == CHF_UNKNOWN) ChfAbort(CHF_ABORT_INIT);
#else
/* Reentrant check; lock context_mutex first */
if(pthread_mutex_lock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
if(_chf_context.state == CHF_UNKNOWN)
{
if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
ChfAbort(CHF_ABORT_INIT);
}
#endif
/* Destroy the context associated with this thread now; this is necessary
to ensure that the context is actually destroyed when a single-threaded
application links with the multithreaded version of Chf: in this case,
pthread_exit() is called *after* ChfExit(), the Chf data key no longer
exists when pthread_exit() is called and the destructor registered
with pthread_key_create() does not take place.
The data pointer associated with the Chf data key is set to NULL to
avoid any subsequent reactivation of the destructor.
*/
#ifdef _REENTRANT
DestroyContext(&chf_context);
if(pthread_setspecific(data_key, (void *)NULL))
{
(void)pthread_mutex_unlock(&context_mutex);
ChfAbort(CHF_ABORT_PTHREAD);
}
#endif
/* Shut down the message retrieval subsystem first */
_chf_context.mrs_exit(_chf_context.mrs_data);
#ifndef _REENTRANT
/* Free the dynamic memory previously allocated */
free(_chf_context.message_buffer);
free(_chf_context.handler_stack);
free(_chf_context.condition_stack);
#else
/* Destroy the Chf data key */
if(pthread_key_delete(data_key)) ChfAbort(CHF_ABORT_PTHREAD);
#endif
/* Reset CHF state to prevent subsequent calls to ChfExit() itself */
_chf_context.state = CHF_UNKNOWN;
#ifdef _REENTRANT
if(pthread_mutex_unlock(&context_mutex)) ChfAbort(CHF_ABORT_PTHREAD);
#endif
}
/* .+
.title : _ChfGetContext
.kind : C function
.creation : 19-May-2000
.description :
This function dynamically primes a new Chf context for the calling
thread (if necessary), and returns a pointer to that context to
the caller. If something does wrong, it aborts the application
with ChfAbort(CHF_ABORT_GET_CONTEXT).
Results are unpredictable if this function is called before a
successful call to ChfInit().
.call :
context = _ChfGetContext(void);
.input :
.output :
ChfContext *context, per-thread Chf context
.status_codes :
none
.notes :
2.1, 19-May-2000, creation
.- */
ChfContext *_ChfGetContext(
void
)
{
ChfContext *context;
#ifndef _REENTRANT
/* This function is doomed to fail if _REENTRANT is not defined */
ChfAbort(CHF_ABORT_GET_CONTEXT);
return((ChfContext *)NULL);
#else
/* Get the thread-specific context pointer associated with the
CHF data key */
if((context = (ChfContext *)pthread_getspecific(data_key))
== (ChfContext *)NULL)
{
/* No context pointer; prime a new one, cloning the master context */
if((context = (ChfContext *)malloc(sizeof(ChfContext)))
== (ChfContext *)NULL)
ChfAbort(CHF_ABORT_GET_CONTEXT);
memcpy(context, &_chf_context, sizeof(ChfContext));
/* Allocate per-thread stacks and message buffer */
if((context->condition_stack =
(ChfDescriptor *)
malloc((size_t)(context->condition_stack_size+1)
*sizeof(ChfDescriptor))) == CHF_NULL_DESCRIPTOR)
ChfAbort(CHF_ABORT_GET_CONTEXT);
if((context->handler_stack =
(ChfHandlerDescriptor *)
malloc((size_t)(context->handler_stack_size)
*sizeof(ChfHandlerDescriptor))) == (ChfHandlerDescriptor *)NULL)
{
free(context->condition_stack);
ChfAbort(CHF_ABORT_GET_CONTEXT);
}
if((context->message_buffer =
(char *)
malloc((size_t)(CHF_MAX_MESSAGE_LENGTH))) == (char *)NULL)
{
free(context->condition_stack);
free(context->handler_stack);
ChfAbort(CHF_ABORT_GET_CONTEXT);
}
/* Initialize stack pointers */
context->condition_base = context->condition_sp =
context->condition_stack;
context->handler_sp = context->handler_stack;
/* Set the thread-specific context pointer; this must be done
before invoking any other function using the context,
including ChfPushHandler() below.
*/
if(pthread_setspecific(data_key, context))
ChfAbort(CHF_ABORT_GET_CONTEXT);
/* Push the default handler */
ChfPushHandler(DefaultHandler, CHF_NULL_CONTEXT, CHF_NULL_POINTER);
}
return context;
#endif
}