/* $Id: exception.c 13 2011-04-20 15:41:43Z mmamonski $ */
/*
* PSNC DRMAA 2.0 utilities library
* Copyright (C) 2012 Poznan Supercomputing and Networking Center
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#ifndef lint
static char rcsid[]
# ifdef __GNUC__
__attribute__ ((unused))
# endif
= "$Id: exception.c 13 2011-04-20 15:41:43Z mmamonski $";
#endif
int fsd_exc_code( const fsd_exc_t *self );
const char *fsd_exc_message( const fsd_exc_t *self );
void fsd_exc_destroy( fsd_exc_t *self );
#define EXC_INITIALIZER( code, message ) \
{ \
fsd_exc_code, fsd_exc_message, fsd_exc_destroy, \
code, message, false, false \
}
static const fsd_exc_t no_memory_exception \
= EXC_INITIALIZER( FSD_ERRNO_NO_MEMORY, "Not enough memory." );
/**
* Thread specific stack of restore points
* @see fsd_exc_try_block_t
*/
typedef struct fsd_exc_stack_s {
fsd_exc_try_block_t **restore_points;
int n_restore_points;
} fsd_exc_stack_t;
static pthread_key_t fsd_exc_stack;
static pthread_once_t fsd_exc_init_once = PTHREAD_ONCE_INIT;
static void
fsd_exc_stack_destroy( fsd_exc_stack_t *stack );
void
fsd_exc_init(void)
{
int rc;
fsd_log_enter((""));
rc = pthread_key_create(
&fsd_exc_stack,
(void (*)(void*))fsd_exc_stack_destroy
);
if( rc )
{
char errbuf[256] = "InternalError";
(void)strerror_r(errno, errbuf, 256);
fsd_log_fatal(( "pthread_key_create: %s",errbuf ));
abort();
}
fsd_log_return((""));
}
fsd_exc_stack_t *
fsd_exc_get_stack( bool create )
{
fsd_exc_stack_t *stack = NULL;
int rc;
rc = pthread_once( &fsd_exc_init_once, fsd_exc_init );
if( rc )
{
char errbuf[256] = "InternalError";
(void)strerror_r(errno, errbuf, 256);
fsd_log_fatal(( "pthread_once: %s",errbuf));
abort();
}
stack = (fsd_exc_stack_t*)pthread_getspecific( fsd_exc_stack );
if( stack == NULL && create )
{
rc = fsd_malloc_noraise( stack, fsd_exc_stack_t );
if( rc ) return NULL;
stack->restore_points = NULL;
stack->n_restore_points = 0;
rc = pthread_setspecific( fsd_exc_stack, stack );
if( rc && errno != ENOMEM )
{
char errbuf[256] = "InternalError";
(void)strerror_r(errno, errbuf, 256);
fsd_log_fatal(( "pthread_setspecific: %s",errbuf ));
abort();
}
else if( rc )
{
fsd_exc_stack_destroy( stack );
}
}
else
fsd_assert( stack != NULL );
return stack;
}
void
fsd_exc_stack_destroy( fsd_exc_stack_t *stack )
{
int i;
fsd_log_enter((""));
for( i = 0; i < stack->n_restore_points; i++ )
{
fsd_exc_try_block_t *b = stack->restore_points[i];
if( b->handled_exc != NULL )
b->handled_exc->destroy( b->handled_exc );
fsd_free( b );
}
fsd_free( stack->restore_points );
fsd_free( stack );
fsd_log_return((""));
}
fsd_exc_try_block_t *
fsd_exc_try( const char *function, int lineno )
{
fsd_exc_stack_t *stack = NULL;
fsd_exc_try_block_t *p = NULL;
int rc;
/* fsd_log_enter(( "(%s, %d)", function, lineno )); */
stack = fsd_exc_get_stack( true );
if( stack == NULL )
return NULL;
rc = fsd_realloc_noraise(
stack->restore_points, stack->n_restore_points+1,
fsd_exc_try_block_t* );
if( rc )
return NULL;
rc = fsd_malloc_noraise( p, fsd_exc_try_block_t );
if( rc )
return NULL;
p->handled_exc = NULL;
p->state = FSD_EXC_ENTER;
p->function = function;
p->lineno = lineno;
stack->restore_points[ stack->n_restore_points++ ] = p;
/* fsd_log_return(( " =%p", (void*)p )); */
return p;
}
void
fsd_exc_control( fsd_exc_try_block_t *block, int *rc )
{
/* fsd_log_enter(( "(block=%p, rc=%d)", (void*)block, *rc )); */
if( block == NULL || *rc == FSD_ERRNO_EXC_END )
return;
switch( block->state )
{
case FSD_EXC_ENTER:
block->state = FSD_EXC_TRY_BLOCK;
fsd_assert( *rc == 0 );
/* fsd_log_return(( ": rc=%d => %s:%d",
*rc, block->function, block->lineno )); */
return;
case FSD_EXC_TRY_BLOCK:
if( *rc == 0 )
{
block->state = FSD_EXC_ELSE_BLOCK;
/* fsd_log_return(( ": rc=FSD_ERRNO_EXC_ELSE => %s:%d",
block->function, block->lineno )); */
*rc = FSD_ERRNO_EXC_ELSE;
return;
}
else
{
fsd_assert( *rc > 0 );
block->state = FSD_EXC_EXCEPTION_HANDLE;
/* fsd_log_return(( ": rc=%d => %s:%d",
*rc, block->function, block->lineno )); */
return;
}
case FSD_EXC_EXCEPTION_HANDLE:
case FSD_EXC_ELSE_BLOCK:
block->state = FSD_EXC_FINALLY_BLOCK;
/* fsd_log_return(( ": rc=FSD_ERRNO_EXC_FINALLY => %s:%d",
block->function, block->lineno )); */
*rc = FSD_ERRNO_EXC_FINALLY;
return;
case FSD_EXC_FINALLY_BLOCK:
{
fsd_exc_try_block_t *current = NULL;
fsd_exc_try_block_t *upper = NULL;
fsd_exc_stack_t *stack = NULL;
block->state = FSD_EXC_LEAVE;
stack = fsd_exc_get_stack( false );
current = stack->restore_points[ stack->n_restore_points-1 ];
fsd_assert( block == current );
if( stack->n_restore_points > 1 )
upper = stack->restore_points[ stack->n_restore_points-2 ];
stack->n_restore_points --;
if( current->handled_exc && upper )
{
if( upper->handled_exc )
{
fsd_assert( upper->state == FSD_EXC_EXCEPTION_HANDLE
|| upper->state == FSD_EXC_FINALLY_BLOCK );
if( upper->state == FSD_EXC_FINALLY_BLOCK )
fsd_log_warning((
"overriding previously raised exception: <%d:%s>",
upper->handled_exc->_code, upper->handled_exc->_message ));
upper->handled_exc->destroy( upper->handled_exc );
}
upper->handled_exc = current->handled_exc;
/* current->handled_exc = NULL; */
fsd_free_noraise( current );
/* fsd_log_return(( ": longjmp(..., %d) => %s:%d",
upper->handled_exc->_code, upper->function, upper->lineno )); */
longjmp( upper->env, upper->handled_exc->_code );
}
else
{
if( current->handled_exc )
current->handled_exc->destroy( current->handled_exc );
fsd_free_noraise( current );
/* fsd_log_return(( ": rc=FSD_ERRNO_EXC_END => %s:%d",
block->function, block->lineno )); */
*rc = FSD_ERRNO_EXC_END;
return;
}
}
default:
fsd_assert(false);
}
/* no return */
}
const fsd_exc_t *
fsd_exc_get(void)
{
fsd_exc_stack_t *stack;
fsd_exc_try_block_t *block;
stack = fsd_exc_get_stack( false );
fsd_assert( stack->n_restore_points > 0 );
block = stack->restore_points[ stack->n_restore_points-1 ];
return block->handled_exc;
}
void
fsd_exc_clear(void)
{
fsd_exc_stack_t *stack;
fsd_exc_try_block_t *block;
fsd_log_enter((""));
stack = fsd_exc_get_stack( false );
fsd_assert( stack->n_restore_points > 0 );
block = stack->restore_points[ stack->n_restore_points-1 ];
if( block->handled_exc )
block->handled_exc->destroy( block->handled_exc );
block->handled_exc = NULL;
fsd_log_return((""));
}
fsd_exc_t *
fsd_exc_new( int code, char *message, bool own_message )
{
fsd_exc_t *exc = NULL;
char *volatile message_buffer = NULL;
if ( code == FSD_DRMAA_ERRNO_EXIT_TIMEOUT || code == FSD_ERRNO_STOP_ITERATION )
fsd_log_debug(("fsd_exc_new(%d,%s,%d)", code, message, own_message));
else
fsd_log_error(("fsd_exc_new(%d,%s,%d)", code, message, own_message));
TRY
{
if( own_message )
message_buffer = message;
fsd_malloc( exc, fsd_exc_t );
exc->_code = code;
exc->_message = message;
exc->_own_message = own_message;
exc->_own_self = true;
exc->code = fsd_exc_code;
exc->message = fsd_exc_message;
exc->destroy = fsd_exc_destroy;
}
EXCEPT_DEFAULT
{
fsd_free( message_buffer );
fsd_exc_reraise();
}
END_TRY
return exc;
}
int
fsd_exc_code( const fsd_exc_t *self )
{
return self->_code;
}
const char *
fsd_exc_message( const fsd_exc_t *self )
{
return self->_message;
}
void
fsd_exc_destroy( fsd_exc_t *self )
{
if( self->_own_message )
fsd_free_noraise( self->_message );
if( self->_own_self )
fsd_free_noraise( self );
}
void
fsd_exc_raise( fsd_exc_t *exc )
{
fsd_exc_stack_t *stack = NULL;
fsd_exc_try_block_t *block = NULL;
fsd_assert(( exc->_code > 0 ));
stack = fsd_exc_get_stack( false );
fsd_assert(( stack->n_restore_points > 0 ));
block = stack->restore_points[ stack->n_restore_points - 1 ];
if( block->handled_exc )
block->handled_exc->destroy( block->handled_exc );
block->handled_exc = exc;
longjmp( block->env, exc->_code );
}
void
fsd_exc_raise_code( int error_code )
{
fsd_exc_t *exc;
if( error_code == FSD_ERRNO_NO_MEMORY )
exc = (fsd_exc_t*)&no_memory_exception;
else
exc = fsd_exc_new( error_code, (char*)fsd_strerror(error_code), false );
fsd_exc_raise( exc );
}
void
fsd_exc_raise_msg( int error_code, const char *message )
{
fsd_exc_raise(
fsd_exc_new( error_code, fsd_strdup(message), true ) );
}
void
fsd_exc_raise_fmt( int error_code, const char *fmt, ... )
{
va_list args;
va_start( args, fmt );
fsd_exc_raise_fmtv( error_code, fmt, args );
va_end( args );
}
void
fsd_exc_raise_fmtv( int error_code, const char *fmt, va_list args )
{
fsd_exc_t *exc = NULL;
char *volatile message = NULL;
TRY
{
message = fsd_vasprintf( fmt, args );
exc = fsd_exc_new( error_code, message, true );
message = NULL;
}
FINALLY
{
fsd_free( message );
}
END_TRY
fsd_exc_raise( exc );
}
void
fsd_exc_raise_sys( int errno_code )
{
fsd_exc_t *exc = NULL;
if( errno_code == 0 )
errno_code = errno;
if( errno_code == ENOMEM )
exc = (fsd_exc_t*)&no_memory_exception;
else
{
int code;
char* volatile message = NULL;
volatile bool own_message = false;
TRY
{
switch( errno_code )
{
case ETIMEDOUT: code = FSD_ERRNO_TIMEOUT; break;
default: code = FSD_ERRNO_INTERNAL_ERROR; break;
}
message = (char*)fsd_astrerror( errno_code, (bool*)&own_message );
exc = fsd_exc_new( code, message, own_message );
}
EXCEPT_DEFAULT
{
if( message && own_message )
fsd_free( message );
fsd_exc_reraise();
}
END_TRY
}
fsd_exc_raise( exc );
}
void
fsd_exc_reraise(void)
{
fsd_exc_stack_t *stack = NULL;
fsd_exc_try_block_t *block = NULL;
stack = fsd_exc_get_stack( false );
fsd_assert(( stack->n_restore_points > 0 ));
block = stack->restore_points[ stack->n_restore_points - 1 ];
fsd_assert(( block->handled_exc->_code > 0 ));
longjmp( block->env, block->handled_exc->_code );
}
const char *
fsd_strerror( int error_code )
{
switch( error_code )
{
case FSD_ERRNO_SUCCESS:
return "Success.";
case FSD_ERRNO_INTERNAL_ERROR:
return "Unexpected or internal error.";
case FSD_ERRNO_NO_MEMORY:
return "Not enough memory.";
case FSD_ERRNO_INVALID_ARGUMENT:
return "Invalid argument value.";
case FSD_ERRNO_INVALID_VALUE:
return "Invalid value.";
case FSD_ERRNO_INVALID_VALUE_FORMAT:
return "Invalid value format.";
case FSD_ERRNO_STOP_ITERATION:
return "Vector have no more elements.";
case FSD_ERRNO_NOT_IMPLEMENTED:
return "Functionality is not implemented.";
case FSD_ERRNO_NOT_INITIALIZED:
return "Library is not initialized";
case FSD_ERRNO_TIMEOUT:
return "Routine returned due to time-out.";
case FSD_ERRNO_AUTH_FAILURE:
return "Authentication failure.";
case FSD_ERRNO_AUTHZ_FAILURE:
return "Authorization failure";
case FSD_ERRNO_TRY_LATER:
return "System is overloaded. Try again later.";
case FSD_ERRNO_DRM_COMMUNICATION_FAILURE:
return "Could not contact DRM system for this request.";
case FSD_ERRNO_DRMS_INIT_FAILED:
return "Unable to initialize DRM system.";
case FSD_ERRNO_DRMS_EXIT_ERROR:
return "Disengagement from the DRM system failed.";
case FSD_ERRNO_DENIED_BY_DRM:
return "DRM rejected request due to its configuration "
"or request attributes.";
case FSD_DRMAA_ERRNO_NO_ACTIVE_SESSION:
return "No active DRMAA session.";
case FSD_DRMAA_ERRNO_INVALID_CONTACT_STRING:
return "Invalid contact string.";
case FSD_DRMAA_ERRNO_DEFAULT_CONTACT_STRING_ERROR:
return "Can not determine default contact to DRM system.";
case FSD_DRMAA_ERRNO_NO_DEFAULT_CONTACT_STRING_SELECTED:
return "Contact to DRM must be set explicitly "
"because there is no default.";
case FSD_DRMAA_ERRNO_ALREADY_ACTIVE_SESSION:
return "DRMAA session already exist.";
case FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_FORMAT:
return "Invalid format of job attribute.";
case FSD_DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE:
return "Invalid value of job attribute.";
case FSD_DRMAA_ERRNO_CONFLICTING_ATTRIBUTE_VALUES:
return "Value of attribute conflicts with other attribute value.";
case FSD_DRMAA_ERRNO_INVALID_JOB:
return "Job does not exist in DRMs queue.";
case FSD_DRMAA_ERRNO_RESUME_INCONSISTENT_STATE:
return "Can not resume job (not in valid state).";
case FSD_DRMAA_ERRNO_SUSPEND_INCONSISTENT_STATE:
return "Can not suspend job (not in valid state).";
case FSD_DRMAA_ERRNO_HOLD_INCONSISTENT_STATE:
return "Can not hold job (not in valid state).";
case FSD_DRMAA_ERRNO_RELEASE_INCONSISTENT_STATE:
return "Can not release job (not in valid state).";
case FSD_DRMAA_ERRNO_EXIT_TIMEOUT:
return "Waiting for job to terminate finished due to time-out.";
case FSD_DRMAA_ERRNO_NO_RUSAGE:
return "Job finished but resource usage information "
"and/or termination status could not be provided.";
case FSD_ARES_ERRNO_INVALID_CONTACT_STRING:
return "Invalid contact string.";
case FSD_ARES_ERRNO_INVALID_ATTRIBUTE_FORMAT:
return "Invalid format of job attribute.";
case FSD_ARES_ERRNO_INVALID_ATTRIBUTE_VALUE:
return "Invalid value of job attribute.";
case FSD_ARES_ERRNO_CONFLICTING_ATTRIBUTE_VALUES:
return "Value of attribute conflicts with other attribute value.";
case FSD_ARES_ERRNO_INVALID_ARES:
return "Invalid advance reservation identifier..";
default:
return "Unknown error code!?";
}
}
static const int fsd_stacktrace_length = 32;
void
fsd_assertion_failed(
const char *file, int lineno,
const char *function, const char *precondition
)
{
char *message = NULL;
if( asprintf( &message, "%s:%d: %s: Assertion `%s' failed.",
file, lineno, function, precondition ) == -1 )
message = NULL;
if( message != NULL )
fsd_log_fatal(( "%s", message ));
fsd_log_stacktrace( 1, fsd_stacktrace_length );
if( message != NULL )
free( message );
abort();
}
void *
fsd_exc_try_except(
void*(*f)(void*), void *data,
int *error_code,
char **error_message
)
{
void *result = NULL;
TRY
{
result = f( data );
}
EXCEPT_DEFAULT
{
const fsd_exc_t *e = fsd_exc_get();
*error_code = e->code(e);
*error_message = fsd_strdup( e->message(e) );
}
ELSE
{
*error_code = FSD_ERRNO_SUCCESS;
*error_message = NULL;
}
END_TRY
return result;
}