security/nss/lib/base/arena.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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 }

mercurial