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