Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * error.c |
michael@0 | 7 | * |
michael@0 | 8 | * This file contains the code implementing the per-thread error |
michael@0 | 9 | * stacks upon which most NSS routines report their errors. |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #ifndef BASE_H |
michael@0 | 13 | #include "base.h" |
michael@0 | 14 | #endif /* BASE_H */ |
michael@0 | 15 | #include <limits.h> /* for UINT_MAX */ |
michael@0 | 16 | #include <string.h> /* for memmove */ |
michael@0 | 17 | |
michael@0 | 18 | #define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ |
michael@0 | 19 | |
michael@0 | 20 | /* |
michael@0 | 21 | * The stack itself has a header, and a sequence of integers. |
michael@0 | 22 | * The header records the amount of space (as measured in stack |
michael@0 | 23 | * slots) already allocated for the stack, and the count of the |
michael@0 | 24 | * number of records currently being used. |
michael@0 | 25 | */ |
michael@0 | 26 | |
michael@0 | 27 | struct stack_header_str { |
michael@0 | 28 | PRUint16 space; |
michael@0 | 29 | PRUint16 count; |
michael@0 | 30 | }; |
michael@0 | 31 | |
michael@0 | 32 | struct error_stack_str { |
michael@0 | 33 | struct stack_header_str header; |
michael@0 | 34 | PRInt32 stack[1]; |
michael@0 | 35 | }; |
michael@0 | 36 | typedef struct error_stack_str error_stack; |
michael@0 | 37 | |
michael@0 | 38 | /* |
michael@0 | 39 | * error_stack_index |
michael@0 | 40 | * |
michael@0 | 41 | * Thread-private data must be indexed. This is that index. |
michael@0 | 42 | * See PR_NewThreadPrivateIndex for more information. |
michael@0 | 43 | * |
michael@0 | 44 | * Thread-private data indexes are in the range [0, 127]. |
michael@0 | 45 | */ |
michael@0 | 46 | |
michael@0 | 47 | #define INVALID_TPD_INDEX UINT_MAX |
michael@0 | 48 | static PRUintn error_stack_index = INVALID_TPD_INDEX; |
michael@0 | 49 | |
michael@0 | 50 | /* |
michael@0 | 51 | * call_once |
michael@0 | 52 | * |
michael@0 | 53 | * The thread-private index must be obtained (once!) at runtime. |
michael@0 | 54 | * This block is used for that one-time call. |
michael@0 | 55 | */ |
michael@0 | 56 | |
michael@0 | 57 | static PRCallOnceType error_call_once; |
michael@0 | 58 | |
michael@0 | 59 | /* |
michael@0 | 60 | * error_once_function |
michael@0 | 61 | * |
michael@0 | 62 | * This is the once-called callback. |
michael@0 | 63 | */ |
michael@0 | 64 | static PRStatus |
michael@0 | 65 | error_once_function ( void) |
michael@0 | 66 | { |
michael@0 | 67 | return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | /* |
michael@0 | 71 | * error_get_my_stack |
michael@0 | 72 | * |
michael@0 | 73 | * This routine returns the calling thread's error stack, creating |
michael@0 | 74 | * it if necessary. It may return NULL upon error, which implicitly |
michael@0 | 75 | * means that it ran out of memory. |
michael@0 | 76 | */ |
michael@0 | 77 | |
michael@0 | 78 | static error_stack * |
michael@0 | 79 | error_get_my_stack ( void) |
michael@0 | 80 | { |
michael@0 | 81 | PRStatus st; |
michael@0 | 82 | error_stack *rv; |
michael@0 | 83 | PRUintn new_size; |
michael@0 | 84 | PRUint32 new_bytes; |
michael@0 | 85 | error_stack *new_stack; |
michael@0 | 86 | |
michael@0 | 87 | if( INVALID_TPD_INDEX == error_stack_index ) { |
michael@0 | 88 | st = PR_CallOnce(&error_call_once, error_once_function); |
michael@0 | 89 | if( PR_SUCCESS != st ) { |
michael@0 | 90 | return (error_stack *)NULL; |
michael@0 | 91 | } |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | rv = (error_stack *)PR_GetThreadPrivate(error_stack_index); |
michael@0 | 95 | if( (error_stack *)NULL == rv ) { |
michael@0 | 96 | /* Doesn't exist; create one */ |
michael@0 | 97 | new_size = 16; |
michael@0 | 98 | } else if( rv->header.count == rv->header.space && |
michael@0 | 99 | rv->header.count < NSS_MAX_ERROR_STACK_COUNT ) { |
michael@0 | 100 | /* Too small, expand it */ |
michael@0 | 101 | new_size = PR_MIN( rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT); |
michael@0 | 102 | } else { |
michael@0 | 103 | /* Okay, return it */ |
michael@0 | 104 | return rv; |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack); |
michael@0 | 108 | /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */ |
michael@0 | 109 | new_stack = PR_Calloc(1, new_bytes); |
michael@0 | 110 | |
michael@0 | 111 | if( (error_stack *)NULL != new_stack ) { |
michael@0 | 112 | if( (error_stack *)NULL != rv ) { |
michael@0 | 113 | (void)nsslibc_memcpy(new_stack,rv,rv->header.space); |
michael@0 | 114 | } |
michael@0 | 115 | new_stack->header.space = new_size; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | /* Set the value, whether or not the allocation worked */ |
michael@0 | 119 | PR_SetThreadPrivate(error_stack_index, new_stack); |
michael@0 | 120 | return new_stack; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | /* |
michael@0 | 124 | * The error stack |
michael@0 | 125 | * |
michael@0 | 126 | * The public methods relating to the error stack are: |
michael@0 | 127 | * |
michael@0 | 128 | * NSS_GetError |
michael@0 | 129 | * NSS_GetErrorStack |
michael@0 | 130 | * |
michael@0 | 131 | * The nonpublic methods relating to the error stack are: |
michael@0 | 132 | * |
michael@0 | 133 | * nss_SetError |
michael@0 | 134 | * nss_ClearErrorStack |
michael@0 | 135 | * |
michael@0 | 136 | */ |
michael@0 | 137 | |
michael@0 | 138 | /* |
michael@0 | 139 | * NSS_GetError |
michael@0 | 140 | * |
michael@0 | 141 | * This routine returns the highest-level (most general) error set |
michael@0 | 142 | * by the most recent NSS library routine called by the same thread |
michael@0 | 143 | * calling this routine. |
michael@0 | 144 | * |
michael@0 | 145 | * This routine cannot fail. However, it may return zero, which |
michael@0 | 146 | * indicates that the previous NSS library call did not set an error. |
michael@0 | 147 | * |
michael@0 | 148 | * Return value: |
michael@0 | 149 | * 0 if no error has been set |
michael@0 | 150 | * A nonzero error number |
michael@0 | 151 | */ |
michael@0 | 152 | |
michael@0 | 153 | NSS_IMPLEMENT PRInt32 |
michael@0 | 154 | NSS_GetError ( void) |
michael@0 | 155 | { |
michael@0 | 156 | error_stack *es = error_get_my_stack(); |
michael@0 | 157 | |
michael@0 | 158 | if( (error_stack *)NULL == es ) { |
michael@0 | 159 | return NSS_ERROR_NO_MEMORY; /* Good guess! */ |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | if( 0 == es->header.count ) { |
michael@0 | 163 | return 0; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | return es->stack[ es->header.count-1 ]; |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | /* |
michael@0 | 170 | * NSS_GetErrorStack |
michael@0 | 171 | * |
michael@0 | 172 | * This routine returns a pointer to an array of integers, containing |
michael@0 | 173 | * the entire sequence or "stack" of errors set by the most recent NSS |
michael@0 | 174 | * library routine called by the same thread calling this routine. |
michael@0 | 175 | * NOTE: the caller DOES NOT OWN the memory pointed to by the return |
michael@0 | 176 | * value. The pointer will remain valid until the calling thread |
michael@0 | 177 | * calls another NSS routine. The lowest-level (most specific) error |
michael@0 | 178 | * is first in the array, and the highest-level is last. The array is |
michael@0 | 179 | * zero-terminated. This routine may return NULL upon error; this |
michael@0 | 180 | * indicates a low-memory situation. |
michael@0 | 181 | * |
michael@0 | 182 | * Return value: |
michael@0 | 183 | * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY |
michael@0 | 184 | * A NON-caller-owned pointer to an array of integers |
michael@0 | 185 | */ |
michael@0 | 186 | |
michael@0 | 187 | NSS_IMPLEMENT PRInt32 * |
michael@0 | 188 | NSS_GetErrorStack ( void) |
michael@0 | 189 | { |
michael@0 | 190 | error_stack *es = error_get_my_stack(); |
michael@0 | 191 | |
michael@0 | 192 | if( (error_stack *)NULL == es ) { |
michael@0 | 193 | return (PRInt32 *)NULL; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | /* Make sure it's terminated */ |
michael@0 | 197 | es->stack[ es->header.count ] = 0; |
michael@0 | 198 | |
michael@0 | 199 | return es->stack; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | /* |
michael@0 | 203 | * nss_SetError |
michael@0 | 204 | * |
michael@0 | 205 | * This routine places a new error code on the top of the calling |
michael@0 | 206 | * thread's error stack. Calling this routine wiht an error code |
michael@0 | 207 | * of zero will clear the error stack. |
michael@0 | 208 | */ |
michael@0 | 209 | |
michael@0 | 210 | NSS_IMPLEMENT void |
michael@0 | 211 | nss_SetError ( PRUint32 error) |
michael@0 | 212 | { |
michael@0 | 213 | error_stack *es; |
michael@0 | 214 | |
michael@0 | 215 | if( 0 == error ) { |
michael@0 | 216 | nss_ClearErrorStack(); |
michael@0 | 217 | return; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | es = error_get_my_stack(); |
michael@0 | 221 | if( (error_stack *)NULL == es ) { |
michael@0 | 222 | /* Oh, well. */ |
michael@0 | 223 | return; |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | if (es->header.count < es->header.space) { |
michael@0 | 227 | es->stack[ es->header.count++ ] = error; |
michael@0 | 228 | } else { |
michael@0 | 229 | memmove(es->stack, es->stack + 1, |
michael@0 | 230 | (es->header.space - 1) * (sizeof es->stack[0])); |
michael@0 | 231 | es->stack[ es->header.space - 1 ] = error; |
michael@0 | 232 | } |
michael@0 | 233 | return; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | /* |
michael@0 | 237 | * nss_ClearErrorStack |
michael@0 | 238 | * |
michael@0 | 239 | * This routine clears the calling thread's error stack. |
michael@0 | 240 | */ |
michael@0 | 241 | |
michael@0 | 242 | NSS_IMPLEMENT void |
michael@0 | 243 | nss_ClearErrorStack ( void) |
michael@0 | 244 | { |
michael@0 | 245 | error_stack *es = error_get_my_stack(); |
michael@0 | 246 | if( (error_stack *)NULL == es ) { |
michael@0 | 247 | /* Oh, well. */ |
michael@0 | 248 | return; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | es->header.count = 0; |
michael@0 | 252 | es->stack[0] = 0; |
michael@0 | 253 | return; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | /* |
michael@0 | 257 | * nss_DestroyErrorStack |
michael@0 | 258 | * |
michael@0 | 259 | * This routine frees the calling thread's error stack. |
michael@0 | 260 | */ |
michael@0 | 261 | |
michael@0 | 262 | NSS_IMPLEMENT void |
michael@0 | 263 | nss_DestroyErrorStack ( void) |
michael@0 | 264 | { |
michael@0 | 265 | if( INVALID_TPD_INDEX != error_stack_index ) { |
michael@0 | 266 | PR_SetThreadPrivate(error_stack_index, NULL); |
michael@0 | 267 | } |
michael@0 | 268 | return; |
michael@0 | 269 | } |