780 lines
22 KiB
C
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
|
|
}
|