andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: /* andre@0: * error.c andre@0: * andre@0: * This file contains the code implementing the per-thread error andre@0: * stacks upon which most NSS routines report their errors. andre@0: */ andre@0: andre@0: #ifndef BASE_H andre@0: #include "base.h" andre@0: #endif /* BASE_H */ andre@0: #include /* for UINT_MAX */ andre@0: #include /* for memmove */ andre@0: andre@0: #define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ andre@0: andre@0: /* andre@0: * The stack itself has a header, and a sequence of integers. andre@0: * The header records the amount of space (as measured in stack andre@0: * slots) already allocated for the stack, and the count of the andre@0: * number of records currently being used. andre@0: */ andre@0: andre@0: struct stack_header_str { andre@0: PRUint16 space; andre@0: PRUint16 count; andre@0: }; andre@0: andre@0: struct error_stack_str { andre@0: struct stack_header_str header; andre@0: PRInt32 stack[1]; andre@0: }; andre@0: typedef struct error_stack_str error_stack; andre@0: andre@0: /* andre@0: * error_stack_index andre@0: * andre@0: * Thread-private data must be indexed. This is that index. andre@0: * See PR_NewThreadPrivateIndex for more information. andre@0: * andre@0: * Thread-private data indexes are in the range [0, 127]. andre@0: */ andre@0: andre@0: #define INVALID_TPD_INDEX UINT_MAX andre@0: static PRUintn error_stack_index = INVALID_TPD_INDEX; andre@0: andre@0: /* andre@0: * call_once andre@0: * andre@0: * The thread-private index must be obtained (once!) at runtime. andre@0: * This block is used for that one-time call. andre@0: */ andre@0: andre@0: static PRCallOnceType error_call_once; andre@0: andre@0: /* andre@0: * error_once_function andre@0: * andre@0: * This is the once-called callback. andre@0: */ andre@0: static PRStatus andre@0: error_once_function ( void) andre@0: { andre@0: return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); andre@0: } andre@0: andre@0: /* andre@0: * error_get_my_stack andre@0: * andre@0: * This routine returns the calling thread's error stack, creating andre@0: * it if necessary. It may return NULL upon error, which implicitly andre@0: * means that it ran out of memory. andre@0: */ andre@0: andre@0: static error_stack * andre@0: error_get_my_stack ( void) andre@0: { andre@0: PRStatus st; andre@0: error_stack *rv; andre@0: PRUintn new_size; andre@0: PRUint32 new_bytes; andre@0: error_stack *new_stack; andre@0: andre@0: if( INVALID_TPD_INDEX == error_stack_index ) { andre@0: st = PR_CallOnce(&error_call_once, error_once_function); andre@0: if( PR_SUCCESS != st ) { andre@0: return (error_stack *)NULL; andre@0: } andre@0: } andre@0: andre@0: rv = (error_stack *)PR_GetThreadPrivate(error_stack_index); andre@0: if( (error_stack *)NULL == rv ) { andre@0: /* Doesn't exist; create one */ andre@0: new_size = 16; andre@0: } else if( rv->header.count == rv->header.space && andre@0: rv->header.count < NSS_MAX_ERROR_STACK_COUNT ) { andre@0: /* Too small, expand it */ andre@0: new_size = PR_MIN( rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT); andre@0: } else { andre@0: /* Okay, return it */ andre@0: return rv; andre@0: } andre@0: andre@0: new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack); andre@0: /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */ andre@0: new_stack = PR_Calloc(1, new_bytes); andre@0: andre@0: if( (error_stack *)NULL != new_stack ) { andre@0: if( (error_stack *)NULL != rv ) { andre@0: (void)nsslibc_memcpy(new_stack,rv,rv->header.space); andre@0: } andre@0: new_stack->header.space = new_size; andre@0: } andre@0: andre@0: /* Set the value, whether or not the allocation worked */ andre@0: PR_SetThreadPrivate(error_stack_index, new_stack); andre@0: return new_stack; andre@0: } andre@0: andre@0: /* andre@0: * The error stack andre@0: * andre@0: * The public methods relating to the error stack are: andre@0: * andre@0: * NSS_GetError andre@0: * NSS_GetErrorStack andre@0: * andre@0: * The nonpublic methods relating to the error stack are: andre@0: * andre@0: * nss_SetError andre@0: * nss_ClearErrorStack andre@0: * andre@0: */ andre@0: andre@0: /* andre@0: * NSS_GetError andre@0: * andre@0: * This routine returns the highest-level (most general) error set andre@0: * by the most recent NSS library routine called by the same thread andre@0: * calling this routine. andre@0: * andre@0: * This routine cannot fail. However, it may return zero, which andre@0: * indicates that the previous NSS library call did not set an error. andre@0: * andre@0: * Return value: andre@0: * 0 if no error has been set andre@0: * A nonzero error number andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRInt32 andre@0: NSS_GetError ( void) andre@0: { andre@0: error_stack *es = error_get_my_stack(); andre@0: andre@0: if( (error_stack *)NULL == es ) { andre@0: return NSS_ERROR_NO_MEMORY; /* Good guess! */ andre@0: } andre@0: andre@0: if( 0 == es->header.count ) { andre@0: return 0; andre@0: } andre@0: andre@0: return es->stack[ es->header.count-1 ]; andre@0: } andre@0: andre@0: /* andre@0: * NSS_GetErrorStack andre@0: * andre@0: * This routine returns a pointer to an array of integers, containing andre@0: * the entire sequence or "stack" of errors set by the most recent NSS andre@0: * library routine called by the same thread calling this routine. andre@0: * NOTE: the caller DOES NOT OWN the memory pointed to by the return andre@0: * value. The pointer will remain valid until the calling thread andre@0: * calls another NSS routine. The lowest-level (most specific) error andre@0: * is first in the array, and the highest-level is last. The array is andre@0: * zero-terminated. This routine may return NULL upon error; this andre@0: * indicates a low-memory situation. andre@0: * andre@0: * Return value: andre@0: * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY andre@0: * A NON-caller-owned pointer to an array of integers andre@0: */ andre@0: andre@0: NSS_IMPLEMENT PRInt32 * andre@0: NSS_GetErrorStack ( void) andre@0: { andre@0: error_stack *es = error_get_my_stack(); andre@0: andre@0: if( (error_stack *)NULL == es ) { andre@0: return (PRInt32 *)NULL; andre@0: } andre@0: andre@0: /* Make sure it's terminated */ andre@0: es->stack[ es->header.count ] = 0; andre@0: andre@0: return es->stack; andre@0: } andre@0: andre@0: /* andre@0: * nss_SetError andre@0: * andre@0: * This routine places a new error code on the top of the calling andre@0: * thread's error stack. Calling this routine wiht an error code andre@0: * of zero will clear the error stack. andre@0: */ andre@0: andre@0: NSS_IMPLEMENT void andre@0: nss_SetError ( PRUint32 error) andre@0: { andre@0: error_stack *es; andre@0: andre@0: if( 0 == error ) { andre@0: nss_ClearErrorStack(); andre@0: return; andre@0: } andre@0: andre@0: es = error_get_my_stack(); andre@0: if( (error_stack *)NULL == es ) { andre@0: /* Oh, well. */ andre@0: return; andre@0: } andre@0: andre@0: if (es->header.count < es->header.space) { andre@0: es->stack[ es->header.count++ ] = error; andre@0: } else { andre@0: memmove(es->stack, es->stack + 1, andre@0: (es->header.space - 1) * (sizeof es->stack[0])); andre@0: es->stack[ es->header.space - 1 ] = error; andre@0: } andre@0: return; andre@0: } andre@0: andre@0: /* andre@0: * nss_ClearErrorStack andre@0: * andre@0: * This routine clears the calling thread's error stack. andre@0: */ andre@0: andre@0: NSS_IMPLEMENT void andre@0: nss_ClearErrorStack ( void) andre@0: { andre@0: error_stack *es = error_get_my_stack(); andre@0: if( (error_stack *)NULL == es ) { andre@0: /* Oh, well. */ andre@0: return; andre@0: } andre@0: andre@0: es->header.count = 0; andre@0: es->stack[0] = 0; andre@0: return; andre@0: } andre@0: andre@0: /* andre@0: * nss_DestroyErrorStack andre@0: * andre@0: * This routine frees the calling thread's error stack. andre@0: */ andre@0: andre@0: NSS_IMPLEMENT void andre@0: nss_DestroyErrorStack ( void) andre@0: { andre@0: if( INVALID_TPD_INDEX != error_stack_index ) { andre@0: PR_SetThreadPrivate(error_stack_index, NULL); andre@0: } andre@0: return; andre@0: }