зеркало из https://github.com/mozilla/gecko-dev.git
302 строки
7.5 KiB
C
302 строки
7.5 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* error.c
|
|
*
|
|
* This file contains the code implementing the per-thread error
|
|
* stacks upon which most NSS routines report their errors.
|
|
*/
|
|
|
|
#ifndef BASE_H
|
|
#include "base.h"
|
|
#endif /* BASE_H */
|
|
#include <limits.h> /* for UINT_MAX */
|
|
#include <string.h> /* for memmove */
|
|
|
|
#if defined(__MINGW32__)
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */
|
|
|
|
/*
|
|
* The stack itself has a header, and a sequence of integers.
|
|
* The header records the amount of space (as measured in stack
|
|
* slots) already allocated for the stack, and the count of the
|
|
* number of records currently being used.
|
|
*/
|
|
|
|
struct stack_header_str {
|
|
PRUint16 space;
|
|
PRUint16 count;
|
|
};
|
|
|
|
struct error_stack_str {
|
|
struct stack_header_str header;
|
|
PRInt32 stack[1];
|
|
};
|
|
typedef struct error_stack_str error_stack;
|
|
|
|
/*
|
|
* error_stack_index
|
|
*
|
|
* Thread-private data must be indexed. This is that index.
|
|
* See PR_NewThreadPrivateIndex for more information.
|
|
*
|
|
* Thread-private data indexes are in the range [0, 127].
|
|
*/
|
|
|
|
#define INVALID_TPD_INDEX UINT_MAX
|
|
static PRUintn error_stack_index = INVALID_TPD_INDEX;
|
|
|
|
/*
|
|
* call_once
|
|
*
|
|
* The thread-private index must be obtained (once!) at runtime.
|
|
* This block is used for that one-time call.
|
|
*/
|
|
|
|
static PRCallOnceType error_call_once;
|
|
static const PRCallOnceType error_call_again;
|
|
|
|
/*
|
|
* error_once_function
|
|
*
|
|
* This is the once-called callback.
|
|
*/
|
|
static PRStatus
|
|
error_once_function(void)
|
|
{
|
|
|
|
/*
|
|
* This #ifdef function is redundant. It performs the same thing as the
|
|
* else case.
|
|
*
|
|
* However, the MinGW version looks up the function from nss3's export
|
|
* table, and on MinGW _that_ behaves differently than passing a
|
|
* function pointer in a different module because MinGW has
|
|
* -mnop-fun-dllimport specified, which generates function thunks for
|
|
* cross-module calls. And when a module (like nssckbi) gets unloaded,
|
|
* and you try to call into that thunk (which is now missing) you'll
|
|
* crash. So we do this bit of ugly to avoid that crash. Fortunately
|
|
* this is the only place we've had to do this.
|
|
*/
|
|
#if defined(__MINGW32__)
|
|
HMODULE nss3 = GetModuleHandleW(L"nss3");
|
|
if (nss3) {
|
|
FARPROC freePtr = GetProcAddress(nss3, "PR_Free");
|
|
if (freePtr) {
|
|
return PR_NewThreadPrivateIndex(&error_stack_index, freePtr);
|
|
}
|
|
}
|
|
return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free);
|
|
#else
|
|
return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* error_get_my_stack
|
|
*
|
|
* This routine returns the calling thread's error stack, creating
|
|
* it if necessary. It may return NULL upon error, which implicitly
|
|
* means that it ran out of memory.
|
|
*/
|
|
|
|
static error_stack *
|
|
error_get_my_stack(void)
|
|
{
|
|
PRStatus st;
|
|
error_stack *rv;
|
|
PRUintn new_size;
|
|
PRUint32 new_bytes;
|
|
error_stack *new_stack;
|
|
|
|
if (INVALID_TPD_INDEX == error_stack_index) {
|
|
st = PR_CallOnce(&error_call_once, error_once_function);
|
|
if (PR_SUCCESS != st) {
|
|
return (error_stack *)NULL;
|
|
}
|
|
}
|
|
|
|
rv = (error_stack *)PR_GetThreadPrivate(error_stack_index);
|
|
if ((error_stack *)NULL == rv) {
|
|
/* Doesn't exist; create one */
|
|
new_size = 16;
|
|
} else if (rv->header.count == rv->header.space &&
|
|
rv->header.count < NSS_MAX_ERROR_STACK_COUNT) {
|
|
/* Too small, expand it */
|
|
new_size = PR_MIN(rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT);
|
|
} else {
|
|
/* Okay, return it */
|
|
return rv;
|
|
}
|
|
|
|
new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack);
|
|
/* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */
|
|
new_stack = PR_Calloc(1, new_bytes);
|
|
|
|
if ((error_stack *)NULL != new_stack) {
|
|
if ((error_stack *)NULL != rv) {
|
|
(void)nsslibc_memcpy(new_stack, rv, rv->header.space);
|
|
}
|
|
new_stack->header.space = new_size;
|
|
}
|
|
|
|
/* Set the value, whether or not the allocation worked */
|
|
PR_SetThreadPrivate(error_stack_index, new_stack);
|
|
return new_stack;
|
|
}
|
|
|
|
/*
|
|
* The error stack
|
|
*
|
|
* The public methods relating to the error stack are:
|
|
*
|
|
* NSS_GetError
|
|
* NSS_GetErrorStack
|
|
*
|
|
* The nonpublic methods relating to the error stack are:
|
|
*
|
|
* nss_SetError
|
|
* nss_ClearErrorStack
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* NSS_GetError
|
|
*
|
|
* This routine returns the highest-level (most general) error set
|
|
* by the most recent NSS library routine called by the same thread
|
|
* calling this routine.
|
|
*
|
|
* This routine cannot fail. However, it may return zero, which
|
|
* indicates that the previous NSS library call did not set an error.
|
|
*
|
|
* Return value:
|
|
* 0 if no error has been set
|
|
* A nonzero error number
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRInt32
|
|
NSS_GetError(void)
|
|
{
|
|
error_stack *es = error_get_my_stack();
|
|
|
|
if ((error_stack *)NULL == es) {
|
|
return NSS_ERROR_NO_MEMORY; /* Good guess! */
|
|
}
|
|
|
|
if (0 == es->header.count) {
|
|
return 0;
|
|
}
|
|
|
|
return es->stack[es->header.count - 1];
|
|
}
|
|
|
|
/*
|
|
* NSS_GetErrorStack
|
|
*
|
|
* This routine returns a pointer to an array of integers, containing
|
|
* the entire sequence or "stack" of errors set by the most recent NSS
|
|
* library routine called by the same thread calling this routine.
|
|
* NOTE: the caller DOES NOT OWN the memory pointed to by the return
|
|
* value. The pointer will remain valid until the calling thread
|
|
* calls another NSS routine. The lowest-level (most specific) error
|
|
* is first in the array, and the highest-level is last. The array is
|
|
* zero-terminated. This routine may return NULL upon error; this
|
|
* indicates a low-memory situation.
|
|
*
|
|
* Return value:
|
|
* NULL upon error, which is an implied NSS_ERROR_NO_MEMORY
|
|
* A NON-caller-owned pointer to an array of integers
|
|
*/
|
|
|
|
NSS_IMPLEMENT PRInt32 *
|
|
NSS_GetErrorStack(void)
|
|
{
|
|
error_stack *es = error_get_my_stack();
|
|
|
|
if ((error_stack *)NULL == es) {
|
|
return (PRInt32 *)NULL;
|
|
}
|
|
|
|
/* Make sure it's terminated */
|
|
es->stack[es->header.count] = 0;
|
|
|
|
return es->stack;
|
|
}
|
|
|
|
/*
|
|
* nss_SetError
|
|
*
|
|
* This routine places a new error code on the top of the calling
|
|
* thread's error stack. Calling this routine wiht an error code
|
|
* of zero will clear the error stack.
|
|
*/
|
|
|
|
NSS_IMPLEMENT void
|
|
nss_SetError(PRUint32 error)
|
|
{
|
|
error_stack *es;
|
|
|
|
if (0 == error) {
|
|
nss_ClearErrorStack();
|
|
return;
|
|
}
|
|
|
|
es = error_get_my_stack();
|
|
if ((error_stack *)NULL == es) {
|
|
/* Oh, well. */
|
|
return;
|
|
}
|
|
|
|
if (es->header.count < es->header.space) {
|
|
es->stack[es->header.count++] = error;
|
|
} else {
|
|
memmove(es->stack, es->stack + 1,
|
|
(es->header.space - 1) * (sizeof es->stack[0]));
|
|
es->stack[es->header.space - 1] = error;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* nss_ClearErrorStack
|
|
*
|
|
* This routine clears the calling thread's error stack.
|
|
*/
|
|
|
|
NSS_IMPLEMENT void
|
|
nss_ClearErrorStack(void)
|
|
{
|
|
error_stack *es = error_get_my_stack();
|
|
if ((error_stack *)NULL == es) {
|
|
/* Oh, well. */
|
|
return;
|
|
}
|
|
|
|
es->header.count = 0;
|
|
es->stack[0] = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* nss_DestroyErrorStack
|
|
*
|
|
* This routine frees the calling thread's error stack.
|
|
*/
|
|
|
|
NSS_IMPLEMENT void
|
|
nss_DestroyErrorStack(void)
|
|
{
|
|
if (INVALID_TPD_INDEX != error_stack_index) {
|
|
PR_SetThreadPrivate(error_stack_index, NULL);
|
|
error_stack_index = INVALID_TPD_INDEX;
|
|
error_call_once = error_call_again; /* allow to init again */
|
|
}
|
|
return;
|
|
}
|