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 | * arena.c |
michael@0 | 7 | * |
michael@0 | 8 | * This contains the implementation of NSS's thread-safe arenas. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #ifndef BASE_H |
michael@0 | 12 | #include "base.h" |
michael@0 | 13 | #endif /* BASE_H */ |
michael@0 | 14 | |
michael@0 | 15 | #ifdef ARENA_THREADMARK |
michael@0 | 16 | #include "prthread.h" |
michael@0 | 17 | #endif /* ARENA_THREADMARK */ |
michael@0 | 18 | |
michael@0 | 19 | #include "prlock.h" |
michael@0 | 20 | #include "plarena.h" |
michael@0 | 21 | |
michael@0 | 22 | #include <string.h> |
michael@0 | 23 | |
michael@0 | 24 | /* |
michael@0 | 25 | * NSSArena |
michael@0 | 26 | * |
michael@0 | 27 | * This is based on NSPR's arena code, but it is threadsafe. |
michael@0 | 28 | * |
michael@0 | 29 | * The public methods relating to this type are: |
michael@0 | 30 | * |
michael@0 | 31 | * NSSArena_Create -- constructor |
michael@0 | 32 | * NSSArena_Destroy |
michael@0 | 33 | * NSS_ZAlloc |
michael@0 | 34 | * NSS_ZRealloc |
michael@0 | 35 | * NSS_ZFreeIf |
michael@0 | 36 | * |
michael@0 | 37 | * The nonpublic methods relating to this type are: |
michael@0 | 38 | * |
michael@0 | 39 | * nssArena_Create -- constructor |
michael@0 | 40 | * nssArena_Destroy |
michael@0 | 41 | * nssArena_Mark |
michael@0 | 42 | * nssArena_Release |
michael@0 | 43 | * nssArena_Unmark |
michael@0 | 44 | * |
michael@0 | 45 | * nss_ZAlloc |
michael@0 | 46 | * nss_ZFreeIf |
michael@0 | 47 | * nss_ZRealloc |
michael@0 | 48 | * |
michael@0 | 49 | * In debug builds, the following calls are available: |
michael@0 | 50 | * |
michael@0 | 51 | * nssArena_verifyPointer |
michael@0 | 52 | * nssArena_registerDestructor |
michael@0 | 53 | * nssArena_deregisterDestructor |
michael@0 | 54 | */ |
michael@0 | 55 | |
michael@0 | 56 | struct NSSArenaStr { |
michael@0 | 57 | PLArenaPool pool; |
michael@0 | 58 | PRLock *lock; |
michael@0 | 59 | #ifdef ARENA_THREADMARK |
michael@0 | 60 | PRThread *marking_thread; |
michael@0 | 61 | nssArenaMark *first_mark; |
michael@0 | 62 | nssArenaMark *last_mark; |
michael@0 | 63 | #endif /* ARENA_THREADMARK */ |
michael@0 | 64 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 65 | struct arena_destructor_node *first_destructor; |
michael@0 | 66 | struct arena_destructor_node *last_destructor; |
michael@0 | 67 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 68 | }; |
michael@0 | 69 | |
michael@0 | 70 | /* |
michael@0 | 71 | * nssArenaMark |
michael@0 | 72 | * |
michael@0 | 73 | * This type is used to mark the current state of an NSSArena. |
michael@0 | 74 | */ |
michael@0 | 75 | |
michael@0 | 76 | struct nssArenaMarkStr { |
michael@0 | 77 | PRUint32 magic; |
michael@0 | 78 | void *mark; |
michael@0 | 79 | #ifdef ARENA_THREADMARK |
michael@0 | 80 | nssArenaMark *next; |
michael@0 | 81 | #endif /* ARENA_THREADMARK */ |
michael@0 | 82 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 83 | struct arena_destructor_node *next_destructor; |
michael@0 | 84 | struct arena_destructor_node *prev_destructor; |
michael@0 | 85 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 86 | }; |
michael@0 | 87 | |
michael@0 | 88 | #define MARK_MAGIC 0x4d41524b /* "MARK" how original */ |
michael@0 | 89 | |
michael@0 | 90 | /* |
michael@0 | 91 | * But first, the pointer-tracking code |
michael@0 | 92 | */ |
michael@0 | 93 | #ifdef DEBUG |
michael@0 | 94 | extern const NSSError NSS_ERROR_INTERNAL_ERROR; |
michael@0 | 95 | |
michael@0 | 96 | static nssPointerTracker arena_pointer_tracker; |
michael@0 | 97 | |
michael@0 | 98 | static PRStatus |
michael@0 | 99 | arena_add_pointer |
michael@0 | 100 | ( |
michael@0 | 101 | const NSSArena *arena |
michael@0 | 102 | ) |
michael@0 | 103 | { |
michael@0 | 104 | PRStatus rv; |
michael@0 | 105 | |
michael@0 | 106 | rv = nssPointerTracker_initialize(&arena_pointer_tracker); |
michael@0 | 107 | if( PR_SUCCESS != rv ) { |
michael@0 | 108 | return rv; |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | rv = nssPointerTracker_add(&arena_pointer_tracker, arena); |
michael@0 | 112 | if( PR_SUCCESS != rv ) { |
michael@0 | 113 | NSSError e = NSS_GetError(); |
michael@0 | 114 | if( NSS_ERROR_NO_MEMORY != e ) { |
michael@0 | 115 | nss_SetError(NSS_ERROR_INTERNAL_ERROR); |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | return rv; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | return PR_SUCCESS; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | static PRStatus |
michael@0 | 125 | arena_remove_pointer |
michael@0 | 126 | ( |
michael@0 | 127 | const NSSArena *arena |
michael@0 | 128 | ) |
michael@0 | 129 | { |
michael@0 | 130 | PRStatus rv; |
michael@0 | 131 | |
michael@0 | 132 | rv = nssPointerTracker_remove(&arena_pointer_tracker, arena); |
michael@0 | 133 | if( PR_SUCCESS != rv ) { |
michael@0 | 134 | nss_SetError(NSS_ERROR_INTERNAL_ERROR); |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | return rv; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | /* |
michael@0 | 141 | * nssArena_verifyPointer |
michael@0 | 142 | * |
michael@0 | 143 | * This method is only present in debug builds. |
michael@0 | 144 | * |
michael@0 | 145 | * If the specified pointer is a valid pointer to an NSSArena object, |
michael@0 | 146 | * this routine will return PR_SUCCESS. Otherwise, it will put an |
michael@0 | 147 | * error on the error stack and return PR_FAILURE. |
michael@0 | 148 | * |
michael@0 | 149 | * The error may be one of the following values: |
michael@0 | 150 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 151 | * |
michael@0 | 152 | * Return value: |
michael@0 | 153 | * PR_SUCCESS if the pointer is valid |
michael@0 | 154 | * PR_FAILURE if it isn't |
michael@0 | 155 | */ |
michael@0 | 156 | |
michael@0 | 157 | NSS_IMPLEMENT PRStatus |
michael@0 | 158 | nssArena_verifyPointer |
michael@0 | 159 | ( |
michael@0 | 160 | const NSSArena *arena |
michael@0 | 161 | ) |
michael@0 | 162 | { |
michael@0 | 163 | PRStatus rv; |
michael@0 | 164 | |
michael@0 | 165 | rv = nssPointerTracker_initialize(&arena_pointer_tracker); |
michael@0 | 166 | if( PR_SUCCESS != rv ) { |
michael@0 | 167 | /* |
michael@0 | 168 | * This is a little disingenious. We have to initialize the |
michael@0 | 169 | * tracker, because someone could "legitimately" try to verify |
michael@0 | 170 | * an arena pointer before one is ever created. And this step |
michael@0 | 171 | * might fail, due to lack of memory. But the only way that |
michael@0 | 172 | * this step can fail is if it's doing the call_once stuff, |
michael@0 | 173 | * (later calls just no-op). And if it didn't no-op, there |
michael@0 | 174 | * aren't any valid arenas.. so the argument certainly isn't one. |
michael@0 | 175 | */ |
michael@0 | 176 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 177 | return PR_FAILURE; |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | rv = nssPointerTracker_verify(&arena_pointer_tracker, arena); |
michael@0 | 181 | if( PR_SUCCESS != rv ) { |
michael@0 | 182 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 183 | return PR_FAILURE; |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return PR_SUCCESS; |
michael@0 | 187 | } |
michael@0 | 188 | #endif /* DEBUG */ |
michael@0 | 189 | |
michael@0 | 190 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 191 | |
michael@0 | 192 | struct arena_destructor_node { |
michael@0 | 193 | struct arena_destructor_node *next; |
michael@0 | 194 | struct arena_destructor_node *prev; |
michael@0 | 195 | void (*destructor)(void *argument); |
michael@0 | 196 | void *arg; |
michael@0 | 197 | }; |
michael@0 | 198 | |
michael@0 | 199 | /* |
michael@0 | 200 | * nssArena_registerDestructor |
michael@0 | 201 | * |
michael@0 | 202 | * This routine stores a pointer to a callback and an arbitrary |
michael@0 | 203 | * pointer-sized argument in the arena, at the current point in |
michael@0 | 204 | * the mark stack. If the arena is destroyed, or an "earlier" |
michael@0 | 205 | * mark is released, then this destructor will be called at that |
michael@0 | 206 | * time. Note that the destructor will be called with the arena |
michael@0 | 207 | * locked, which means the destructor may free memory in that |
michael@0 | 208 | * arena, but it may not allocate or cause to be allocated any |
michael@0 | 209 | * memory. This callback facility was included to support our |
michael@0 | 210 | * debug-version pointer-tracker feature; overuse runs counter to |
michael@0 | 211 | * the the original intent of arenas. This routine returns a |
michael@0 | 212 | * PRStatus value; if successful, it will return PR_SUCCESS. If |
michael@0 | 213 | * unsuccessful, it will set an error on the error stack and |
michael@0 | 214 | * return PR_FAILURE. |
michael@0 | 215 | * |
michael@0 | 216 | * The error may be one of the following values: |
michael@0 | 217 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 218 | * NSS_ERROR_NO_MEMORY |
michael@0 | 219 | * |
michael@0 | 220 | * Return value: |
michael@0 | 221 | * PR_SUCCESS |
michael@0 | 222 | * PR_FAILURE |
michael@0 | 223 | */ |
michael@0 | 224 | |
michael@0 | 225 | NSS_IMPLEMENT PRStatus |
michael@0 | 226 | nssArena_registerDestructor |
michael@0 | 227 | ( |
michael@0 | 228 | NSSArena *arena, |
michael@0 | 229 | void (*destructor)(void *argument), |
michael@0 | 230 | void *arg |
michael@0 | 231 | ) |
michael@0 | 232 | { |
michael@0 | 233 | struct arena_destructor_node *it; |
michael@0 | 234 | |
michael@0 | 235 | #ifdef NSSDEBUG |
michael@0 | 236 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 237 | return PR_FAILURE; |
michael@0 | 238 | } |
michael@0 | 239 | #endif /* NSSDEBUG */ |
michael@0 | 240 | |
michael@0 | 241 | it = nss_ZNEW(arena, struct arena_destructor_node); |
michael@0 | 242 | if( (struct arena_destructor_node *)NULL == it ) { |
michael@0 | 243 | return PR_FAILURE; |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | it->prev = arena->last_destructor; |
michael@0 | 247 | arena->last_destructor->next = it; |
michael@0 | 248 | arena->last_destructor = it; |
michael@0 | 249 | it->destructor = destructor; |
michael@0 | 250 | it->arg = arg; |
michael@0 | 251 | |
michael@0 | 252 | if( (nssArenaMark *)NULL != arena->last_mark ) { |
michael@0 | 253 | arena->last_mark->prev_destructor = it->prev; |
michael@0 | 254 | arena->last_mark->next_destructor = it->next; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | return PR_SUCCESS; |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | NSS_IMPLEMENT PRStatus |
michael@0 | 261 | nssArena_deregisterDestructor |
michael@0 | 262 | ( |
michael@0 | 263 | NSSArena *arena, |
michael@0 | 264 | void (*destructor)(void *argument), |
michael@0 | 265 | void *arg |
michael@0 | 266 | ) |
michael@0 | 267 | { |
michael@0 | 268 | struct arena_destructor_node *it; |
michael@0 | 269 | |
michael@0 | 270 | #ifdef NSSDEBUG |
michael@0 | 271 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 272 | return PR_FAILURE; |
michael@0 | 273 | } |
michael@0 | 274 | #endif /* NSSDEBUG */ |
michael@0 | 275 | |
michael@0 | 276 | for( it = arena->first_destructor; it; it = it->next ) { |
michael@0 | 277 | if( (it->destructor == destructor) && (it->arg == arg) ) { |
michael@0 | 278 | break; |
michael@0 | 279 | } |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | if( (struct arena_destructor_node *)NULL == it ) { |
michael@0 | 283 | nss_SetError(NSS_ERROR_NOT_FOUND); |
michael@0 | 284 | return PR_FAILURE; |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | if( it == arena->first_destructor ) { |
michael@0 | 288 | arena->first_destructor = it->next; |
michael@0 | 289 | } |
michael@0 | 290 | |
michael@0 | 291 | if( it == arena->last_destructor ) { |
michael@0 | 292 | arena->last_destructor = it->prev; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | if( (struct arena_destructor_node *)NULL != it->prev ) { |
michael@0 | 296 | it->prev->next = it->next; |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | if( (struct arena_destructor_node *)NULL != it->next ) { |
michael@0 | 300 | it->next->prev = it->prev; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | { |
michael@0 | 304 | nssArenaMark *m; |
michael@0 | 305 | for( m = arena->first_mark; m; m = m->next ) { |
michael@0 | 306 | if( m->next_destructor == it ) { |
michael@0 | 307 | m->next_destructor = it->next; |
michael@0 | 308 | } |
michael@0 | 309 | if( m->prev_destructor == it ) { |
michael@0 | 310 | m->prev_destructor = it->prev; |
michael@0 | 311 | } |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | nss_ZFreeIf(it); |
michael@0 | 316 | return PR_SUCCESS; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | static void |
michael@0 | 320 | nss_arena_call_destructor_chain |
michael@0 | 321 | ( |
michael@0 | 322 | struct arena_destructor_node *it |
michael@0 | 323 | ) |
michael@0 | 324 | { |
michael@0 | 325 | for( ; it ; it = it->next ) { |
michael@0 | 326 | (*(it->destructor))(it->arg); |
michael@0 | 327 | } |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 331 | |
michael@0 | 332 | /* |
michael@0 | 333 | * NSSArena_Create |
michael@0 | 334 | * |
michael@0 | 335 | * This routine creates a new memory arena. This routine may return |
michael@0 | 336 | * NULL upon error, in which case it will have created an error stack. |
michael@0 | 337 | * |
michael@0 | 338 | * The top-level error may be one of the following values: |
michael@0 | 339 | * NSS_ERROR_NO_MEMORY |
michael@0 | 340 | * |
michael@0 | 341 | * Return value: |
michael@0 | 342 | * NULL upon error |
michael@0 | 343 | * A pointer to an NSSArena upon success |
michael@0 | 344 | */ |
michael@0 | 345 | |
michael@0 | 346 | NSS_IMPLEMENT NSSArena * |
michael@0 | 347 | NSSArena_Create |
michael@0 | 348 | ( |
michael@0 | 349 | void |
michael@0 | 350 | ) |
michael@0 | 351 | { |
michael@0 | 352 | nss_ClearErrorStack(); |
michael@0 | 353 | return nssArena_Create(); |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | /* |
michael@0 | 357 | * nssArena_Create |
michael@0 | 358 | * |
michael@0 | 359 | * This routine creates a new memory arena. This routine may return |
michael@0 | 360 | * NULL upon error, in which case it will have set an error on the |
michael@0 | 361 | * error stack. |
michael@0 | 362 | * |
michael@0 | 363 | * The error may be one of the following values: |
michael@0 | 364 | * NSS_ERROR_NO_MEMORY |
michael@0 | 365 | * |
michael@0 | 366 | * Return value: |
michael@0 | 367 | * NULL upon error |
michael@0 | 368 | * A pointer to an NSSArena upon success |
michael@0 | 369 | */ |
michael@0 | 370 | |
michael@0 | 371 | NSS_IMPLEMENT NSSArena * |
michael@0 | 372 | nssArena_Create |
michael@0 | 373 | ( |
michael@0 | 374 | void |
michael@0 | 375 | ) |
michael@0 | 376 | { |
michael@0 | 377 | NSSArena *rv = (NSSArena *)NULL; |
michael@0 | 378 | |
michael@0 | 379 | rv = nss_ZNEW((NSSArena *)NULL, NSSArena); |
michael@0 | 380 | if( (NSSArena *)NULL == rv ) { |
michael@0 | 381 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 382 | return (NSSArena *)NULL; |
michael@0 | 383 | } |
michael@0 | 384 | |
michael@0 | 385 | rv->lock = PR_NewLock(); |
michael@0 | 386 | if( (PRLock *)NULL == rv->lock ) { |
michael@0 | 387 | (void)nss_ZFreeIf(rv); |
michael@0 | 388 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 389 | return (NSSArena *)NULL; |
michael@0 | 390 | } |
michael@0 | 391 | |
michael@0 | 392 | /* |
michael@0 | 393 | * Arena sizes. The current security code has 229 occurrences of |
michael@0 | 394 | * PORT_NewArena. The default chunksizes specified break down as |
michael@0 | 395 | * |
michael@0 | 396 | * Size Mult. Specified as |
michael@0 | 397 | * 512 1 512 |
michael@0 | 398 | * 1024 7 1024 |
michael@0 | 399 | * 2048 5 2048 |
michael@0 | 400 | * 2048 5 CRMF_DEFAULT_ARENA_SIZE |
michael@0 | 401 | * 2048 190 DER_DEFAULT_CHUNKSIZE |
michael@0 | 402 | * 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE |
michael@0 | 403 | * 4096 1 4096 |
michael@0 | 404 | * |
michael@0 | 405 | * Obviously this "default chunksize" flexibility isn't very |
michael@0 | 406 | * useful to us, so I'll just pick 2048. |
michael@0 | 407 | */ |
michael@0 | 408 | |
michael@0 | 409 | PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double)); |
michael@0 | 410 | |
michael@0 | 411 | #ifdef DEBUG |
michael@0 | 412 | { |
michael@0 | 413 | PRStatus st; |
michael@0 | 414 | st = arena_add_pointer(rv); |
michael@0 | 415 | if( PR_SUCCESS != st ) { |
michael@0 | 416 | PL_FinishArenaPool(&rv->pool); |
michael@0 | 417 | PR_DestroyLock(rv->lock); |
michael@0 | 418 | (void)nss_ZFreeIf(rv); |
michael@0 | 419 | return (NSSArena *)NULL; |
michael@0 | 420 | } |
michael@0 | 421 | } |
michael@0 | 422 | #endif /* DEBUG */ |
michael@0 | 423 | |
michael@0 | 424 | return rv; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | /* |
michael@0 | 428 | * NSSArena_Destroy |
michael@0 | 429 | * |
michael@0 | 430 | * This routine will destroy the specified arena, freeing all memory |
michael@0 | 431 | * allocated from it. This routine returns a PRStatus value; if |
michael@0 | 432 | * successful, it will return PR_SUCCESS. If unsuccessful, it will |
michael@0 | 433 | * create an error stack and return PR_FAILURE. |
michael@0 | 434 | * |
michael@0 | 435 | * The top-level error may be one of the following values: |
michael@0 | 436 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 437 | * |
michael@0 | 438 | * Return value: |
michael@0 | 439 | * PR_SUCCESS upon success |
michael@0 | 440 | * PR_FAILURE upon failure |
michael@0 | 441 | */ |
michael@0 | 442 | |
michael@0 | 443 | NSS_IMPLEMENT PRStatus |
michael@0 | 444 | NSSArena_Destroy |
michael@0 | 445 | ( |
michael@0 | 446 | NSSArena *arena |
michael@0 | 447 | ) |
michael@0 | 448 | { |
michael@0 | 449 | nss_ClearErrorStack(); |
michael@0 | 450 | |
michael@0 | 451 | #ifdef DEBUG |
michael@0 | 452 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 453 | return PR_FAILURE; |
michael@0 | 454 | } |
michael@0 | 455 | #endif /* DEBUG */ |
michael@0 | 456 | |
michael@0 | 457 | return nssArena_Destroy(arena); |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | /* |
michael@0 | 461 | * nssArena_Destroy |
michael@0 | 462 | * |
michael@0 | 463 | * This routine will destroy the specified arena, freeing all memory |
michael@0 | 464 | * allocated from it. This routine returns a PRStatus value; if |
michael@0 | 465 | * successful, it will return PR_SUCCESS. If unsuccessful, it will |
michael@0 | 466 | * set an error on the error stack and return PR_FAILURE. |
michael@0 | 467 | * |
michael@0 | 468 | * The error may be one of the following values: |
michael@0 | 469 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 470 | * |
michael@0 | 471 | * Return value: |
michael@0 | 472 | * PR_SUCCESS |
michael@0 | 473 | * PR_FAILURE |
michael@0 | 474 | */ |
michael@0 | 475 | |
michael@0 | 476 | NSS_IMPLEMENT PRStatus |
michael@0 | 477 | nssArena_Destroy |
michael@0 | 478 | ( |
michael@0 | 479 | NSSArena *arena |
michael@0 | 480 | ) |
michael@0 | 481 | { |
michael@0 | 482 | PRLock *lock; |
michael@0 | 483 | |
michael@0 | 484 | #ifdef NSSDEBUG |
michael@0 | 485 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 486 | return PR_FAILURE; |
michael@0 | 487 | } |
michael@0 | 488 | #endif /* NSSDEBUG */ |
michael@0 | 489 | |
michael@0 | 490 | if( (PRLock *)NULL == arena->lock ) { |
michael@0 | 491 | /* Just got destroyed */ |
michael@0 | 492 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 493 | return PR_FAILURE; |
michael@0 | 494 | } |
michael@0 | 495 | PR_Lock(arena->lock); |
michael@0 | 496 | |
michael@0 | 497 | #ifdef DEBUG |
michael@0 | 498 | if( PR_SUCCESS != arena_remove_pointer(arena) ) { |
michael@0 | 499 | PR_Unlock(arena->lock); |
michael@0 | 500 | return PR_FAILURE; |
michael@0 | 501 | } |
michael@0 | 502 | #endif /* DEBUG */ |
michael@0 | 503 | |
michael@0 | 504 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 505 | /* Note that the arena is locked at this time */ |
michael@0 | 506 | nss_arena_call_destructor_chain(arena->first_destructor); |
michael@0 | 507 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 508 | |
michael@0 | 509 | PL_FinishArenaPool(&arena->pool); |
michael@0 | 510 | lock = arena->lock; |
michael@0 | 511 | arena->lock = (PRLock *)NULL; |
michael@0 | 512 | PR_Unlock(lock); |
michael@0 | 513 | PR_DestroyLock(lock); |
michael@0 | 514 | (void)nss_ZFreeIf(arena); |
michael@0 | 515 | return PR_SUCCESS; |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size); |
michael@0 | 519 | |
michael@0 | 520 | /* |
michael@0 | 521 | * nssArena_Mark |
michael@0 | 522 | * |
michael@0 | 523 | * This routine "marks" the current state of an arena. Space |
michael@0 | 524 | * allocated after the arena has been marked can be freed by |
michael@0 | 525 | * releasing the arena back to the mark with nssArena_Release, |
michael@0 | 526 | * or committed by calling nssArena_Unmark. When successful, |
michael@0 | 527 | * this routine returns a valid nssArenaMark pointer. This |
michael@0 | 528 | * routine may return NULL upon error, in which case it will |
michael@0 | 529 | * have set an error on the error stack. |
michael@0 | 530 | * |
michael@0 | 531 | * The error may be one of the following values: |
michael@0 | 532 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 533 | * NSS_ERROR_NO_MEMORY |
michael@0 | 534 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 535 | * |
michael@0 | 536 | * Return value: |
michael@0 | 537 | * NULL upon failure |
michael@0 | 538 | * An nssArenaMark pointer upon success |
michael@0 | 539 | */ |
michael@0 | 540 | |
michael@0 | 541 | NSS_IMPLEMENT nssArenaMark * |
michael@0 | 542 | nssArena_Mark |
michael@0 | 543 | ( |
michael@0 | 544 | NSSArena *arena |
michael@0 | 545 | ) |
michael@0 | 546 | { |
michael@0 | 547 | nssArenaMark *rv; |
michael@0 | 548 | void *p; |
michael@0 | 549 | |
michael@0 | 550 | #ifdef NSSDEBUG |
michael@0 | 551 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 552 | return (nssArenaMark *)NULL; |
michael@0 | 553 | } |
michael@0 | 554 | #endif /* NSSDEBUG */ |
michael@0 | 555 | |
michael@0 | 556 | if( (PRLock *)NULL == arena->lock ) { |
michael@0 | 557 | /* Just got destroyed */ |
michael@0 | 558 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 559 | return (nssArenaMark *)NULL; |
michael@0 | 560 | } |
michael@0 | 561 | PR_Lock(arena->lock); |
michael@0 | 562 | |
michael@0 | 563 | #ifdef ARENA_THREADMARK |
michael@0 | 564 | if( (PRThread *)NULL == arena->marking_thread ) { |
michael@0 | 565 | /* Unmarked. Store our thread ID */ |
michael@0 | 566 | arena->marking_thread = PR_GetCurrentThread(); |
michael@0 | 567 | /* This call never fails. */ |
michael@0 | 568 | } else { |
michael@0 | 569 | /* Marked. Verify it's the current thread */ |
michael@0 | 570 | if( PR_GetCurrentThread() != arena->marking_thread ) { |
michael@0 | 571 | PR_Unlock(arena->lock); |
michael@0 | 572 | nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); |
michael@0 | 573 | return (nssArenaMark *)NULL; |
michael@0 | 574 | } |
michael@0 | 575 | } |
michael@0 | 576 | #endif /* ARENA_THREADMARK */ |
michael@0 | 577 | |
michael@0 | 578 | p = PL_ARENA_MARK(&arena->pool); |
michael@0 | 579 | /* No error possible */ |
michael@0 | 580 | |
michael@0 | 581 | /* Do this after the mark */ |
michael@0 | 582 | rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark)); |
michael@0 | 583 | if( (nssArenaMark *)NULL == rv ) { |
michael@0 | 584 | PR_Unlock(arena->lock); |
michael@0 | 585 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 586 | return (nssArenaMark *)NULL; |
michael@0 | 587 | } |
michael@0 | 588 | |
michael@0 | 589 | #ifdef ARENA_THREADMARK |
michael@0 | 590 | if ( (nssArenaMark *)NULL == arena->first_mark) { |
michael@0 | 591 | arena->first_mark = rv; |
michael@0 | 592 | arena->last_mark = rv; |
michael@0 | 593 | } else { |
michael@0 | 594 | arena->last_mark->next = rv; |
michael@0 | 595 | arena->last_mark = rv; |
michael@0 | 596 | } |
michael@0 | 597 | #endif /* ARENA_THREADMARK */ |
michael@0 | 598 | |
michael@0 | 599 | rv->mark = p; |
michael@0 | 600 | rv->magic = MARK_MAGIC; |
michael@0 | 601 | |
michael@0 | 602 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 603 | rv->prev_destructor = arena->last_destructor; |
michael@0 | 604 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 605 | |
michael@0 | 606 | PR_Unlock(arena->lock); |
michael@0 | 607 | |
michael@0 | 608 | return rv; |
michael@0 | 609 | } |
michael@0 | 610 | |
michael@0 | 611 | /* |
michael@0 | 612 | * nss_arena_unmark_release |
michael@0 | 613 | * |
michael@0 | 614 | * This static routine implements the routines nssArena_Release |
michael@0 | 615 | * ans nssArena_Unmark, which are almost identical. |
michael@0 | 616 | */ |
michael@0 | 617 | |
michael@0 | 618 | static PRStatus |
michael@0 | 619 | nss_arena_unmark_release |
michael@0 | 620 | ( |
michael@0 | 621 | NSSArena *arena, |
michael@0 | 622 | nssArenaMark *arenaMark, |
michael@0 | 623 | PRBool release |
michael@0 | 624 | ) |
michael@0 | 625 | { |
michael@0 | 626 | void *inner_mark; |
michael@0 | 627 | |
michael@0 | 628 | #ifdef NSSDEBUG |
michael@0 | 629 | if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { |
michael@0 | 630 | return PR_FAILURE; |
michael@0 | 631 | } |
michael@0 | 632 | #endif /* NSSDEBUG */ |
michael@0 | 633 | |
michael@0 | 634 | if( MARK_MAGIC != arenaMark->magic ) { |
michael@0 | 635 | nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); |
michael@0 | 636 | return PR_FAILURE; |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | if( (PRLock *)NULL == arena->lock ) { |
michael@0 | 640 | /* Just got destroyed */ |
michael@0 | 641 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 642 | return PR_FAILURE; |
michael@0 | 643 | } |
michael@0 | 644 | PR_Lock(arena->lock); |
michael@0 | 645 | |
michael@0 | 646 | #ifdef ARENA_THREADMARK |
michael@0 | 647 | if( (PRThread *)NULL != arena->marking_thread ) { |
michael@0 | 648 | if( PR_GetCurrentThread() != arena->marking_thread ) { |
michael@0 | 649 | PR_Unlock(arena->lock); |
michael@0 | 650 | nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); |
michael@0 | 651 | return PR_FAILURE; |
michael@0 | 652 | } |
michael@0 | 653 | } |
michael@0 | 654 | #endif /* ARENA_THREADMARK */ |
michael@0 | 655 | |
michael@0 | 656 | if( MARK_MAGIC != arenaMark->magic ) { |
michael@0 | 657 | /* Just got released */ |
michael@0 | 658 | PR_Unlock(arena->lock); |
michael@0 | 659 | nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); |
michael@0 | 660 | return PR_FAILURE; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | arenaMark->magic = 0; |
michael@0 | 664 | inner_mark = arenaMark->mark; |
michael@0 | 665 | |
michael@0 | 666 | #ifdef ARENA_THREADMARK |
michael@0 | 667 | { |
michael@0 | 668 | nssArenaMark **pMark = &arena->first_mark; |
michael@0 | 669 | nssArenaMark *rest; |
michael@0 | 670 | nssArenaMark *last = (nssArenaMark *)NULL; |
michael@0 | 671 | |
michael@0 | 672 | /* Find this mark */ |
michael@0 | 673 | while( *pMark != arenaMark ) { |
michael@0 | 674 | last = *pMark; |
michael@0 | 675 | pMark = &(*pMark)->next; |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | /* Remember the pointer, then zero it */ |
michael@0 | 679 | rest = (*pMark)->next; |
michael@0 | 680 | *pMark = (nssArenaMark *)NULL; |
michael@0 | 681 | |
michael@0 | 682 | arena->last_mark = last; |
michael@0 | 683 | |
michael@0 | 684 | /* Invalidate any later marks being implicitly released */ |
michael@0 | 685 | for( ; (nssArenaMark *)NULL != rest; rest = rest->next ) { |
michael@0 | 686 | rest->magic = 0; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | /* If we just got rid of the first mark, clear the thread ID */ |
michael@0 | 690 | if( (nssArenaMark *)NULL == arena->first_mark ) { |
michael@0 | 691 | arena->marking_thread = (PRThread *)NULL; |
michael@0 | 692 | } |
michael@0 | 693 | } |
michael@0 | 694 | #endif /* ARENA_THREADMARK */ |
michael@0 | 695 | |
michael@0 | 696 | if( release ) { |
michael@0 | 697 | #ifdef ARENA_DESTRUCTOR_LIST |
michael@0 | 698 | if( (struct arena_destructor_node *)NULL != arenaMark->prev_destructor ) { |
michael@0 | 699 | arenaMark->prev_destructor->next = (struct arena_destructor_node *)NULL; |
michael@0 | 700 | } |
michael@0 | 701 | arena->last_destructor = arenaMark->prev_destructor; |
michael@0 | 702 | |
michael@0 | 703 | /* Note that the arena is locked at this time */ |
michael@0 | 704 | nss_arena_call_destructor_chain(arenaMark->next_destructor); |
michael@0 | 705 | #endif /* ARENA_DESTRUCTOR_LIST */ |
michael@0 | 706 | |
michael@0 | 707 | PL_ARENA_RELEASE(&arena->pool, inner_mark); |
michael@0 | 708 | /* No error return */ |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | PR_Unlock(arena->lock); |
michael@0 | 712 | return PR_SUCCESS; |
michael@0 | 713 | } |
michael@0 | 714 | |
michael@0 | 715 | /* |
michael@0 | 716 | * nssArena_Release |
michael@0 | 717 | * |
michael@0 | 718 | * This routine invalidates and releases all memory allocated from |
michael@0 | 719 | * the specified arena after the point at which the specified mark |
michael@0 | 720 | * was obtained. This routine returns a PRStatus value; if successful, |
michael@0 | 721 | * it will return PR_SUCCESS. If unsuccessful, it will set an error |
michael@0 | 722 | * on the error stack and return PR_FAILURE. |
michael@0 | 723 | * |
michael@0 | 724 | * The error may be one of the following values: |
michael@0 | 725 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 726 | * NSS_ERROR_INVALID_ARENA_MARK |
michael@0 | 727 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 728 | * |
michael@0 | 729 | * Return value: |
michael@0 | 730 | * PR_SUCCESS |
michael@0 | 731 | * PR_FAILURE |
michael@0 | 732 | */ |
michael@0 | 733 | |
michael@0 | 734 | NSS_IMPLEMENT PRStatus |
michael@0 | 735 | nssArena_Release |
michael@0 | 736 | ( |
michael@0 | 737 | NSSArena *arena, |
michael@0 | 738 | nssArenaMark *arenaMark |
michael@0 | 739 | ) |
michael@0 | 740 | { |
michael@0 | 741 | return nss_arena_unmark_release(arena, arenaMark, PR_TRUE); |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | /* |
michael@0 | 745 | * nssArena_Unmark |
michael@0 | 746 | * |
michael@0 | 747 | * This routine "commits" the indicated mark and any marks after |
michael@0 | 748 | * it, making them unreleasable. Note that any earlier marks can |
michael@0 | 749 | * still be released, and such a release will invalidate these |
michael@0 | 750 | * later unmarked regions. If an arena is to be safely shared by |
michael@0 | 751 | * more than one thread, all marks must be either released or |
michael@0 | 752 | * unmarked. This routine returns a PRStatus value; if successful, |
michael@0 | 753 | * it will return PR_SUCCESS. If unsuccessful, it will set an error |
michael@0 | 754 | * on the error stack and return PR_FAILURE. |
michael@0 | 755 | * |
michael@0 | 756 | * The error may be one of the following values: |
michael@0 | 757 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 758 | * NSS_ERROR_INVALID_ARENA_MARK |
michael@0 | 759 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 760 | * |
michael@0 | 761 | * Return value: |
michael@0 | 762 | * PR_SUCCESS |
michael@0 | 763 | * PR_FAILURE |
michael@0 | 764 | */ |
michael@0 | 765 | |
michael@0 | 766 | NSS_IMPLEMENT PRStatus |
michael@0 | 767 | nssArena_Unmark |
michael@0 | 768 | ( |
michael@0 | 769 | NSSArena *arena, |
michael@0 | 770 | nssArenaMark *arenaMark |
michael@0 | 771 | ) |
michael@0 | 772 | { |
michael@0 | 773 | return nss_arena_unmark_release(arena, arenaMark, PR_FALSE); |
michael@0 | 774 | } |
michael@0 | 775 | |
michael@0 | 776 | /* |
michael@0 | 777 | * We prefix this header to all allocated blocks. It is a multiple |
michael@0 | 778 | * of the alignment size. Note that this usage of a header may make |
michael@0 | 779 | * purify spew bogus warnings about "potentially leaked blocks" of |
michael@0 | 780 | * memory; if that gets too annoying we can add in a pointer to the |
michael@0 | 781 | * header in the header itself. There's not a lot of safety here; |
michael@0 | 782 | * maybe we should add a magic value? |
michael@0 | 783 | */ |
michael@0 | 784 | struct pointer_header { |
michael@0 | 785 | NSSArena *arena; |
michael@0 | 786 | PRUint32 size; |
michael@0 | 787 | }; |
michael@0 | 788 | |
michael@0 | 789 | static void * |
michael@0 | 790 | nss_zalloc_arena_locked |
michael@0 | 791 | ( |
michael@0 | 792 | NSSArena *arena, |
michael@0 | 793 | PRUint32 size |
michael@0 | 794 | ) |
michael@0 | 795 | { |
michael@0 | 796 | void *p; |
michael@0 | 797 | void *rv; |
michael@0 | 798 | struct pointer_header *h; |
michael@0 | 799 | PRUint32 my_size = size + sizeof(struct pointer_header); |
michael@0 | 800 | PL_ARENA_ALLOCATE(p, &arena->pool, my_size); |
michael@0 | 801 | if( (void *)NULL == p ) { |
michael@0 | 802 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 803 | return (void *)NULL; |
michael@0 | 804 | } |
michael@0 | 805 | /* |
michael@0 | 806 | * Do this before we unlock. This way if the user is using |
michael@0 | 807 | * an arena in one thread while destroying it in another, he'll |
michael@0 | 808 | * fault/FMR in his code, not ours. |
michael@0 | 809 | */ |
michael@0 | 810 | h = (struct pointer_header *)p; |
michael@0 | 811 | h->arena = arena; |
michael@0 | 812 | h->size = size; |
michael@0 | 813 | rv = (void *)((char *)h + sizeof(struct pointer_header)); |
michael@0 | 814 | (void)nsslibc_memset(rv, 0, size); |
michael@0 | 815 | return rv; |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | /* |
michael@0 | 819 | * NSS_ZAlloc |
michael@0 | 820 | * |
michael@0 | 821 | * This routine allocates and zeroes a section of memory of the |
michael@0 | 822 | * size, and returns to the caller a pointer to that memory. If |
michael@0 | 823 | * the optional arena argument is non-null, the memory will be |
michael@0 | 824 | * obtained from that arena; otherwise, the memory will be obtained |
michael@0 | 825 | * from the heap. This routine may return NULL upon error, in |
michael@0 | 826 | * which case it will have set an error upon the error stack. The |
michael@0 | 827 | * value specified for size may be zero; in which case a valid |
michael@0 | 828 | * zero-length block of memory will be allocated. This block may |
michael@0 | 829 | * be expanded by calling NSS_ZRealloc. |
michael@0 | 830 | * |
michael@0 | 831 | * The error may be one of the following values: |
michael@0 | 832 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 833 | * NSS_ERROR_NO_MEMORY |
michael@0 | 834 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 835 | * |
michael@0 | 836 | * Return value: |
michael@0 | 837 | * NULL upon error |
michael@0 | 838 | * A pointer to the new segment of zeroed memory |
michael@0 | 839 | */ |
michael@0 | 840 | |
michael@0 | 841 | NSS_IMPLEMENT void * |
michael@0 | 842 | NSS_ZAlloc |
michael@0 | 843 | ( |
michael@0 | 844 | NSSArena *arenaOpt, |
michael@0 | 845 | PRUint32 size |
michael@0 | 846 | ) |
michael@0 | 847 | { |
michael@0 | 848 | return nss_ZAlloc(arenaOpt, size); |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | /* |
michael@0 | 852 | * nss_ZAlloc |
michael@0 | 853 | * |
michael@0 | 854 | * This routine allocates and zeroes a section of memory of the |
michael@0 | 855 | * size, and returns to the caller a pointer to that memory. If |
michael@0 | 856 | * the optional arena argument is non-null, the memory will be |
michael@0 | 857 | * obtained from that arena; otherwise, the memory will be obtained |
michael@0 | 858 | * from the heap. This routine may return NULL upon error, in |
michael@0 | 859 | * which case it will have set an error upon the error stack. The |
michael@0 | 860 | * value specified for size may be zero; in which case a valid |
michael@0 | 861 | * zero-length block of memory will be allocated. This block may |
michael@0 | 862 | * be expanded by calling nss_ZRealloc. |
michael@0 | 863 | * |
michael@0 | 864 | * The error may be one of the following values: |
michael@0 | 865 | * NSS_ERROR_INVALID_ARENA |
michael@0 | 866 | * NSS_ERROR_NO_MEMORY |
michael@0 | 867 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 868 | * |
michael@0 | 869 | * Return value: |
michael@0 | 870 | * NULL upon error |
michael@0 | 871 | * A pointer to the new segment of zeroed memory |
michael@0 | 872 | */ |
michael@0 | 873 | |
michael@0 | 874 | NSS_IMPLEMENT void * |
michael@0 | 875 | nss_ZAlloc |
michael@0 | 876 | ( |
michael@0 | 877 | NSSArena *arenaOpt, |
michael@0 | 878 | PRUint32 size |
michael@0 | 879 | ) |
michael@0 | 880 | { |
michael@0 | 881 | struct pointer_header *h; |
michael@0 | 882 | PRUint32 my_size = size + sizeof(struct pointer_header); |
michael@0 | 883 | |
michael@0 | 884 | if( my_size < sizeof(struct pointer_header) ) { |
michael@0 | 885 | /* Wrapped */ |
michael@0 | 886 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 887 | return (void *)NULL; |
michael@0 | 888 | } |
michael@0 | 889 | |
michael@0 | 890 | if( (NSSArena *)NULL == arenaOpt ) { |
michael@0 | 891 | /* Heap allocation, no locking required. */ |
michael@0 | 892 | h = (struct pointer_header *)PR_Calloc(1, my_size); |
michael@0 | 893 | if( (struct pointer_header *)NULL == h ) { |
michael@0 | 894 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 895 | return (void *)NULL; |
michael@0 | 896 | } |
michael@0 | 897 | |
michael@0 | 898 | h->arena = (NSSArena *)NULL; |
michael@0 | 899 | h->size = size; |
michael@0 | 900 | /* We used calloc: it's already zeroed */ |
michael@0 | 901 | |
michael@0 | 902 | return (void *)((char *)h + sizeof(struct pointer_header)); |
michael@0 | 903 | } else { |
michael@0 | 904 | void *rv; |
michael@0 | 905 | /* Arena allocation */ |
michael@0 | 906 | #ifdef NSSDEBUG |
michael@0 | 907 | if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { |
michael@0 | 908 | return (void *)NULL; |
michael@0 | 909 | } |
michael@0 | 910 | #endif /* NSSDEBUG */ |
michael@0 | 911 | |
michael@0 | 912 | if( (PRLock *)NULL == arenaOpt->lock ) { |
michael@0 | 913 | /* Just got destroyed */ |
michael@0 | 914 | nss_SetError(NSS_ERROR_INVALID_ARENA); |
michael@0 | 915 | return (void *)NULL; |
michael@0 | 916 | } |
michael@0 | 917 | PR_Lock(arenaOpt->lock); |
michael@0 | 918 | |
michael@0 | 919 | #ifdef ARENA_THREADMARK |
michael@0 | 920 | if( (PRThread *)NULL != arenaOpt->marking_thread ) { |
michael@0 | 921 | if( PR_GetCurrentThread() != arenaOpt->marking_thread ) { |
michael@0 | 922 | nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); |
michael@0 | 923 | PR_Unlock(arenaOpt->lock); |
michael@0 | 924 | return (void *)NULL; |
michael@0 | 925 | } |
michael@0 | 926 | } |
michael@0 | 927 | #endif /* ARENA_THREADMARK */ |
michael@0 | 928 | |
michael@0 | 929 | rv = nss_zalloc_arena_locked(arenaOpt, size); |
michael@0 | 930 | |
michael@0 | 931 | PR_Unlock(arenaOpt->lock); |
michael@0 | 932 | return rv; |
michael@0 | 933 | } |
michael@0 | 934 | /*NOTREACHED*/ |
michael@0 | 935 | } |
michael@0 | 936 | |
michael@0 | 937 | /* |
michael@0 | 938 | * NSS_ZFreeIf |
michael@0 | 939 | * |
michael@0 | 940 | * If the specified pointer is non-null, then the region of memory |
michael@0 | 941 | * to which it points -- which must have been allocated with |
michael@0 | 942 | * NSS_ZAlloc -- will be zeroed and released. This routine |
michael@0 | 943 | * returns a PRStatus value; if successful, it will return PR_SUCCESS. |
michael@0 | 944 | * If unsuccessful, it will set an error on the error stack and return |
michael@0 | 945 | * PR_FAILURE. |
michael@0 | 946 | * |
michael@0 | 947 | * The error may be one of the following values: |
michael@0 | 948 | * NSS_ERROR_INVALID_POINTER |
michael@0 | 949 | * |
michael@0 | 950 | * Return value: |
michael@0 | 951 | * PR_SUCCESS |
michael@0 | 952 | * PR_FAILURE |
michael@0 | 953 | */ |
michael@0 | 954 | NSS_IMPLEMENT PRStatus |
michael@0 | 955 | NSS_ZFreeIf |
michael@0 | 956 | ( |
michael@0 | 957 | void *pointer |
michael@0 | 958 | ) |
michael@0 | 959 | { |
michael@0 | 960 | return nss_ZFreeIf(pointer); |
michael@0 | 961 | } |
michael@0 | 962 | |
michael@0 | 963 | /* |
michael@0 | 964 | * nss_ZFreeIf |
michael@0 | 965 | * |
michael@0 | 966 | * If the specified pointer is non-null, then the region of memory |
michael@0 | 967 | * to which it points -- which must have been allocated with |
michael@0 | 968 | * nss_ZAlloc -- will be zeroed and released. This routine |
michael@0 | 969 | * returns a PRStatus value; if successful, it will return PR_SUCCESS. |
michael@0 | 970 | * If unsuccessful, it will set an error on the error stack and return |
michael@0 | 971 | * PR_FAILURE. |
michael@0 | 972 | * |
michael@0 | 973 | * The error may be one of the following values: |
michael@0 | 974 | * NSS_ERROR_INVALID_POINTER |
michael@0 | 975 | * |
michael@0 | 976 | * Return value: |
michael@0 | 977 | * PR_SUCCESS |
michael@0 | 978 | * PR_FAILURE |
michael@0 | 979 | */ |
michael@0 | 980 | |
michael@0 | 981 | NSS_IMPLEMENT PRStatus |
michael@0 | 982 | nss_ZFreeIf |
michael@0 | 983 | ( |
michael@0 | 984 | void *pointer |
michael@0 | 985 | ) |
michael@0 | 986 | { |
michael@0 | 987 | struct pointer_header *h; |
michael@0 | 988 | |
michael@0 | 989 | if( (void *)NULL == pointer ) { |
michael@0 | 990 | return PR_SUCCESS; |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | h = (struct pointer_header *)((char *)pointer |
michael@0 | 994 | - sizeof(struct pointer_header)); |
michael@0 | 995 | |
michael@0 | 996 | /* Check any magic here */ |
michael@0 | 997 | |
michael@0 | 998 | if( (NSSArena *)NULL == h->arena ) { |
michael@0 | 999 | /* Heap */ |
michael@0 | 1000 | (void)nsslibc_memset(pointer, 0, h->size); |
michael@0 | 1001 | PR_Free(h); |
michael@0 | 1002 | return PR_SUCCESS; |
michael@0 | 1003 | } else { |
michael@0 | 1004 | /* Arena */ |
michael@0 | 1005 | #ifdef NSSDEBUG |
michael@0 | 1006 | if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) { |
michael@0 | 1007 | return PR_FAILURE; |
michael@0 | 1008 | } |
michael@0 | 1009 | #endif /* NSSDEBUG */ |
michael@0 | 1010 | |
michael@0 | 1011 | if( (PRLock *)NULL == h->arena->lock ) { |
michael@0 | 1012 | /* Just got destroyed.. so this pointer is invalid */ |
michael@0 | 1013 | nss_SetError(NSS_ERROR_INVALID_POINTER); |
michael@0 | 1014 | return PR_FAILURE; |
michael@0 | 1015 | } |
michael@0 | 1016 | PR_Lock(h->arena->lock); |
michael@0 | 1017 | |
michael@0 | 1018 | (void)nsslibc_memset(pointer, 0, h->size); |
michael@0 | 1019 | |
michael@0 | 1020 | /* No way to "free" it within an NSPR arena. */ |
michael@0 | 1021 | |
michael@0 | 1022 | PR_Unlock(h->arena->lock); |
michael@0 | 1023 | return PR_SUCCESS; |
michael@0 | 1024 | } |
michael@0 | 1025 | /*NOTREACHED*/ |
michael@0 | 1026 | } |
michael@0 | 1027 | |
michael@0 | 1028 | /* |
michael@0 | 1029 | * NSS_ZRealloc |
michael@0 | 1030 | * |
michael@0 | 1031 | * This routine reallocates a block of memory obtained by calling |
michael@0 | 1032 | * nss_ZAlloc or nss_ZRealloc. The portion of memory |
michael@0 | 1033 | * between the new and old sizes -- which is either being newly |
michael@0 | 1034 | * obtained or released -- is in either case zeroed. This routine |
michael@0 | 1035 | * may return NULL upon failure, in which case it will have placed |
michael@0 | 1036 | * an error on the error stack. |
michael@0 | 1037 | * |
michael@0 | 1038 | * The error may be one of the following values: |
michael@0 | 1039 | * NSS_ERROR_INVALID_POINTER |
michael@0 | 1040 | * NSS_ERROR_NO_MEMORY |
michael@0 | 1041 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 1042 | * |
michael@0 | 1043 | * Return value: |
michael@0 | 1044 | * NULL upon error |
michael@0 | 1045 | * A pointer to the replacement segment of memory |
michael@0 | 1046 | */ |
michael@0 | 1047 | |
michael@0 | 1048 | NSS_EXTERN void * |
michael@0 | 1049 | NSS_ZRealloc |
michael@0 | 1050 | ( |
michael@0 | 1051 | void *pointer, |
michael@0 | 1052 | PRUint32 newSize |
michael@0 | 1053 | ) |
michael@0 | 1054 | { |
michael@0 | 1055 | return nss_ZRealloc(pointer, newSize); |
michael@0 | 1056 | } |
michael@0 | 1057 | |
michael@0 | 1058 | /* |
michael@0 | 1059 | * nss_ZRealloc |
michael@0 | 1060 | * |
michael@0 | 1061 | * This routine reallocates a block of memory obtained by calling |
michael@0 | 1062 | * nss_ZAlloc or nss_ZRealloc. The portion of memory |
michael@0 | 1063 | * between the new and old sizes -- which is either being newly |
michael@0 | 1064 | * obtained or released -- is in either case zeroed. This routine |
michael@0 | 1065 | * may return NULL upon failure, in which case it will have placed |
michael@0 | 1066 | * an error on the error stack. |
michael@0 | 1067 | * |
michael@0 | 1068 | * The error may be one of the following values: |
michael@0 | 1069 | * NSS_ERROR_INVALID_POINTER |
michael@0 | 1070 | * NSS_ERROR_NO_MEMORY |
michael@0 | 1071 | * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD |
michael@0 | 1072 | * |
michael@0 | 1073 | * Return value: |
michael@0 | 1074 | * NULL upon error |
michael@0 | 1075 | * A pointer to the replacement segment of memory |
michael@0 | 1076 | */ |
michael@0 | 1077 | |
michael@0 | 1078 | NSS_EXTERN void * |
michael@0 | 1079 | nss_ZRealloc |
michael@0 | 1080 | ( |
michael@0 | 1081 | void *pointer, |
michael@0 | 1082 | PRUint32 newSize |
michael@0 | 1083 | ) |
michael@0 | 1084 | { |
michael@0 | 1085 | NSSArena *arena; |
michael@0 | 1086 | struct pointer_header *h, *new_h; |
michael@0 | 1087 | PRUint32 my_newSize = newSize + sizeof(struct pointer_header); |
michael@0 | 1088 | void *rv; |
michael@0 | 1089 | |
michael@0 | 1090 | if( my_newSize < sizeof(struct pointer_header) ) { |
michael@0 | 1091 | /* Wrapped */ |
michael@0 | 1092 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 1093 | return (void *)NULL; |
michael@0 | 1094 | } |
michael@0 | 1095 | |
michael@0 | 1096 | if( (void *)NULL == pointer ) { |
michael@0 | 1097 | nss_SetError(NSS_ERROR_INVALID_POINTER); |
michael@0 | 1098 | return (void *)NULL; |
michael@0 | 1099 | } |
michael@0 | 1100 | |
michael@0 | 1101 | h = (struct pointer_header *)((char *)pointer |
michael@0 | 1102 | - sizeof(struct pointer_header)); |
michael@0 | 1103 | |
michael@0 | 1104 | /* Check any magic here */ |
michael@0 | 1105 | |
michael@0 | 1106 | if( newSize == h->size ) { |
michael@0 | 1107 | /* saves thrashing */ |
michael@0 | 1108 | return pointer; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | arena = h->arena; |
michael@0 | 1112 | if (!arena) { |
michael@0 | 1113 | /* Heap */ |
michael@0 | 1114 | new_h = (struct pointer_header *)PR_Calloc(1, my_newSize); |
michael@0 | 1115 | if( (struct pointer_header *)NULL == new_h ) { |
michael@0 | 1116 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 1117 | return (void *)NULL; |
michael@0 | 1118 | } |
michael@0 | 1119 | |
michael@0 | 1120 | new_h->arena = (NSSArena *)NULL; |
michael@0 | 1121 | new_h->size = newSize; |
michael@0 | 1122 | rv = (void *)((char *)new_h + sizeof(struct pointer_header)); |
michael@0 | 1123 | |
michael@0 | 1124 | if( newSize > h->size ) { |
michael@0 | 1125 | (void)nsslibc_memcpy(rv, pointer, h->size); |
michael@0 | 1126 | (void)nsslibc_memset(&((char *)rv)[ h->size ], |
michael@0 | 1127 | 0, (newSize - h->size)); |
michael@0 | 1128 | } else { |
michael@0 | 1129 | (void)nsslibc_memcpy(rv, pointer, newSize); |
michael@0 | 1130 | } |
michael@0 | 1131 | |
michael@0 | 1132 | (void)nsslibc_memset(pointer, 0, h->size); |
michael@0 | 1133 | h->size = 0; |
michael@0 | 1134 | PR_Free(h); |
michael@0 | 1135 | |
michael@0 | 1136 | return rv; |
michael@0 | 1137 | } else { |
michael@0 | 1138 | void *p; |
michael@0 | 1139 | /* Arena */ |
michael@0 | 1140 | #ifdef NSSDEBUG |
michael@0 | 1141 | if (PR_SUCCESS != nssArena_verifyPointer(arena)) { |
michael@0 | 1142 | return (void *)NULL; |
michael@0 | 1143 | } |
michael@0 | 1144 | #endif /* NSSDEBUG */ |
michael@0 | 1145 | |
michael@0 | 1146 | if (!arena->lock) { |
michael@0 | 1147 | /* Just got destroyed.. so this pointer is invalid */ |
michael@0 | 1148 | nss_SetError(NSS_ERROR_INVALID_POINTER); |
michael@0 | 1149 | return (void *)NULL; |
michael@0 | 1150 | } |
michael@0 | 1151 | PR_Lock(arena->lock); |
michael@0 | 1152 | |
michael@0 | 1153 | #ifdef ARENA_THREADMARK |
michael@0 | 1154 | if (arena->marking_thread) { |
michael@0 | 1155 | if (PR_GetCurrentThread() != arena->marking_thread) { |
michael@0 | 1156 | PR_Unlock(arena->lock); |
michael@0 | 1157 | nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); |
michael@0 | 1158 | return (void *)NULL; |
michael@0 | 1159 | } |
michael@0 | 1160 | } |
michael@0 | 1161 | #endif /* ARENA_THREADMARK */ |
michael@0 | 1162 | |
michael@0 | 1163 | if( newSize < h->size ) { |
michael@0 | 1164 | /* |
michael@0 | 1165 | * We have no general way of returning memory to the arena |
michael@0 | 1166 | * (mark/release doesn't work because things may have been |
michael@0 | 1167 | * allocated after this object), so the memory is gone |
michael@0 | 1168 | * anyway. We might as well just return the same pointer to |
michael@0 | 1169 | * the user, saying "yeah, uh-hunh, you can only use less of |
michael@0 | 1170 | * it now." We'll zero the leftover part, of course. And |
michael@0 | 1171 | * in fact we might as well *not* adjust h->size-- this way, |
michael@0 | 1172 | * if the user reallocs back up to something not greater than |
michael@0 | 1173 | * the original size, then voila, there's the memory! This |
michael@0 | 1174 | * way a thrash big/small/big/small doesn't burn up the arena. |
michael@0 | 1175 | */ |
michael@0 | 1176 | char *extra = &((char *)pointer)[ newSize ]; |
michael@0 | 1177 | (void)nsslibc_memset(extra, 0, (h->size - newSize)); |
michael@0 | 1178 | PR_Unlock(arena->lock); |
michael@0 | 1179 | return pointer; |
michael@0 | 1180 | } |
michael@0 | 1181 | |
michael@0 | 1182 | PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize); |
michael@0 | 1183 | if( (void *)NULL == p ) { |
michael@0 | 1184 | PR_Unlock(arena->lock); |
michael@0 | 1185 | nss_SetError(NSS_ERROR_NO_MEMORY); |
michael@0 | 1186 | return (void *)NULL; |
michael@0 | 1187 | } |
michael@0 | 1188 | |
michael@0 | 1189 | new_h = (struct pointer_header *)p; |
michael@0 | 1190 | new_h->arena = arena; |
michael@0 | 1191 | new_h->size = newSize; |
michael@0 | 1192 | rv = (void *)((char *)new_h + sizeof(struct pointer_header)); |
michael@0 | 1193 | if (rv != pointer) { |
michael@0 | 1194 | (void)nsslibc_memcpy(rv, pointer, h->size); |
michael@0 | 1195 | (void)nsslibc_memset(pointer, 0, h->size); |
michael@0 | 1196 | } |
michael@0 | 1197 | (void)nsslibc_memset(&((char *)rv)[ h->size ], 0, (newSize - h->size)); |
michael@0 | 1198 | h->arena = (NSSArena *)NULL; |
michael@0 | 1199 | h->size = 0; |
michael@0 | 1200 | PR_Unlock(arena->lock); |
michael@0 | 1201 | return rv; |
michael@0 | 1202 | } |
michael@0 | 1203 | /*NOTREACHED*/ |
michael@0 | 1204 | } |
michael@0 | 1205 | |
michael@0 | 1206 | PRStatus |
michael@0 | 1207 | nssArena_Shutdown(void) |
michael@0 | 1208 | { |
michael@0 | 1209 | PRStatus rv = PR_SUCCESS; |
michael@0 | 1210 | #ifdef DEBUG |
michael@0 | 1211 | rv = nssPointerTracker_finalize(&arena_pointer_tracker); |
michael@0 | 1212 | #endif |
michael@0 | 1213 | return rv; |
michael@0 | 1214 | } |