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