Changeset 31660 for trunk/psLib/src/sys/psMemory.c
- Timestamp:
- Jun 22, 2011, 12:27:33 AM (15 years ago)
- File:
-
- 1 edited
-
trunk/psLib/src/sys/psMemory.c (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/sys/psMemory.c
r31152 r31660 35 35 #include "psError.h" // for psErrorStackPrint() only 36 36 #include "psMemory.h" 37 #include "psAbort.h" 37 38 38 39 // Magic number in psMemBlock header 39 40 #define P_PS_MEMMAGIC (uint32_t)0xdeadbeef 40 41 42 // The psLib memory tools require locking to manipulate the linked list which holds the memory 43 // structure. There are two major options which can be invoked, given below. 44 45 // USE_SPINLOCK replaces the mutex in this code block with a spinlock. This might be faster in 46 // some cases, but probably not enough to matter. The disadavantages are: (1) programs would 47 // need to call the function 'psMemInit' (which they currently do not in general); (2) 48 // single-processor machines are poorly set up to use spinlocks; (3) not all compilers / 49 // machine combinations support spinlocks. This option is NOT recommended 50 51 // USE_HARDLOCK replaces the per-memBlock locking that is the minimum required to protect the 52 // psRealloc function with pure mutex locking that holds the lock during the (possibly 53 // expensive) realloc call. At this point, it does not seem clear that there is a substantial 54 // gain in processing speed from using the per-memBlock locking, but more testing may reveal 55 // cases where it matters. 56 57 # define USE_SPINLOCK 0 58 # define USE_HARDLOCK 0 59 60 # if (USE_SPINLOCK) 61 #define MUTEX_LOCK(mutexPtr) \ 62 if (safeThreads) { \ 63 pthread_spin_lock(mutexPtr); \ 64 } 65 #define MUTEX_UNLOCK(mutexPtr) \ 66 if (safeThreads) { \ 67 pthread_spin_unlock(mutexPtr); \ 68 } 69 # else // !USE_SPINLOCK 41 70 #define MUTEX_LOCK(mutexPtr) \ 42 71 if (safeThreads) { \ 43 72 pthread_mutex_lock(mutexPtr); \ 44 73 } 45 46 74 #define MUTEX_UNLOCK(mutexPtr) \ 47 75 if (safeThreads) { \ 48 76 pthread_mutex_unlock(mutexPtr); \ 49 77 } 78 # endif // USE_SPINLOCK 50 79 51 80 // psAbort() calls functions that call psAlloc() so it is *UNSAFE* to use it … … 93 122 // 94 123 // 124 125 # if (USE_SPINLOCK) 126 static pthread_spinlock_t memBlockListMutex; 127 # else 95 128 static pthread_mutex_t memBlockListMutex = PTHREAD_MUTEX_INITIALIZER; 129 # endif 130 131 void psMemInit() { 132 133 # if (USE_SPINLOCK) 134 pthread_spin_init(&memBlockListMutex, 0); 135 # endif 136 137 } 96 138 97 139 /******** thread safety options management ********/ … … 325 367 } 326 368 369 /** BLOCK_LOCK, BLOCK_UNLOCK, BLOCKSET_LOCK, BLOCKSET_UNLOCK are used to set per-memBlock locks 370 ** to allow psRealloc to release the global lock before performing the expensive system 371 ** call 'realloc' 372 **/ 373 374 # if (!USE_HARDLOCK) 375 static int setLock = 0; 376 static int clearLock = 0; 377 static int retryLock = 0; 378 # endif 379 380 # define BLOCK_SLEEP 100 381 382 # define MEM_ASSERT(COND,MSG) { if (!(COND)) { fprintf (stderr, MSG); abort(); } } 383 327 384 // pointer to the last mem block that was allocated. 328 385 // This is the root of the entire memory list 329 386 static psMemBlock *lastMemBlockAllocated = NULL; 387 388 // set lock on lastMemBlockAllocated 389 void BLOCKLAST_LOCK () { 390 391 # if (USE_HARDLOCK) 392 MUTEX_LOCK(&memBlockListMutex); 393 # else 394 395 // if memBlock is not defined, we are just asking for a m 396 if (!lastMemBlockAllocated) { 397 MUTEX_LOCK(&memBlockListMutex); 398 return; 399 } 400 401 while (true) { 402 // set a lock, then lock this memblock and its neighbors 403 MUTEX_LOCK(&memBlockListMutex); 404 405 // did we beat the race condition? is the lock still clear? 406 if (lastMemBlockAllocated->inFlight) { 407 // nope, we need to try again 408 MUTEX_UNLOCK(&memBlockListMutex); 409 retryLock ++; 410 usleep (BLOCK_SLEEP); 411 continue; 412 } 413 414 // the marker is ours! 415 lastMemBlockAllocated->inFlight = true; 416 setLock ++; 417 return; 418 } 419 # endif 420 } 421 422 // memBlock->inFlight is a 'soft' lock. if true, the lock is set 423 void BLOCK_LOCK (psMemBlock *memBlock, bool keepMutex) { 424 425 # if (USE_HARDLOCK) 426 MUTEX_LOCK(&memBlockListMutex); 427 # else 428 429 MEM_ASSERT (memBlock || keepMutex, "trying to set a soft lock on a non-existent memBlock\n"); 430 431 // if memBlock is not defined, we are just asking for a m 432 if (!memBlock) { 433 MUTEX_LOCK(&memBlockListMutex); 434 return; 435 } 436 437 while (true) { 438 // set a lock, then lock this memblock and its neighbors 439 MUTEX_LOCK(&memBlockListMutex); 440 441 // did we beat the race condition? is the lock still clear? 442 if (memBlock->inFlight) { 443 // nope, we need to try again 444 MUTEX_UNLOCK(&memBlockListMutex); 445 usleep (BLOCK_SLEEP); 446 retryLock ++; 447 continue; 448 } 449 450 // the marker is ours! 451 memBlock->inFlight = true; 452 setLock ++; 453 if (keepMutex) return; 454 455 MUTEX_UNLOCK(&memBlockListMutex); 456 return; 457 } 458 # endif 459 } 460 461 void BLOCK_UNLOCK (psMemBlock *memBlock, bool haveMutex) { 462 463 # if (USE_HARDLOCK) 464 MUTEX_UNLOCK(&memBlockListMutex); 465 # else 466 467 MEM_ASSERT (memBlock || haveMutex, "trying to clear a soft lock on a non-existent memBlock\n"); 468 469 // if memBlock is not defined, we are just asking for a regular lock 470 if (!memBlock) { 471 MUTEX_UNLOCK(&memBlockListMutex); 472 return; 473 } 474 475 MEM_ASSERT (memBlock->inFlight, "trying to clear an unlocked memBlock\n"); 476 477 // need to lock before clearing the marker 478 if (!haveMutex) { 479 MUTEX_LOCK(&memBlockListMutex); 480 } 481 memBlock->inFlight = false; 482 clearLock ++; 483 MUTEX_UNLOCK(&memBlockListMutex); 484 return; 485 # endif 486 } 487 488 // attempt to grab the markers on the given memBlock and its two neighbors. the only times the 489 // two neighbors do not exist is if we are at the beginning or end of the list (or the list is 490 // new). 491 void BLOCKSET_LOCK (psMemBlock *memBlock, bool keepMutex) { 492 493 # if (USE_HARDLOCK) 494 MUTEX_LOCK(&memBlockListMutex); 495 # else 496 497 MEM_ASSERT (memBlock, "trying to set a soft lock on a non-existent memBlock\n"); 498 499 while (true) { 500 // we cannot set the lock on the marker while the lock is held 501 // wait until all three markers are clear: 502 // while (memBlock->inFlight) { usleep (BLOCK_SLEEP); } 503 // while (memBlock->nextBlock && memBlock->nextBlock->inFlight) { usleep (BLOCK_SLEEP); } 504 // while (memBlock->previousBlock && memBlock->previousBlock->inFlight) { usleep (BLOCK_SLEEP); } 505 506 // set a lock, then lock this memblock and its neighbors 507 MUTEX_LOCK(&memBlockListMutex); 508 509 // did we beat the race condition? are all three locks still clear? 510 if (memBlock->inFlight) { 511 // nope, we need to try again 512 MUTEX_UNLOCK(&memBlockListMutex); 513 usleep (BLOCK_SLEEP); 514 retryLock ++; 515 continue; 516 } 517 if (memBlock->nextBlock && memBlock->nextBlock->inFlight) { 518 // nope, we need to try again 519 MUTEX_UNLOCK(&memBlockListMutex); 520 usleep (BLOCK_SLEEP); 521 retryLock ++; 522 continue; 523 } 524 if (memBlock->previousBlock && memBlock->previousBlock->inFlight) { 525 // nope, we need to try again 526 MUTEX_UNLOCK(&memBlockListMutex); 527 usleep (BLOCK_SLEEP); 528 retryLock ++; 529 continue; 530 } 531 532 // the markers are ours! 533 memBlock->inFlight = true; 534 setLock ++; 535 if (memBlock->nextBlock) { 536 memBlock->nextBlock->inFlight = true; 537 } 538 if (memBlock->previousBlock) { 539 memBlock->previousBlock->inFlight = true; 540 } 541 if (keepMutex) return; 542 543 MUTEX_UNLOCK(&memBlockListMutex); 544 return; 545 } 546 # endif 547 } 548 549 void BLOCKSET_UNLOCK (psMemBlock *memBlock, bool haveMutex) { 550 551 # if (USE_HARDLOCK) 552 MUTEX_UNLOCK(&memBlockListMutex); 553 # else 554 555 // need to lock before clearing the marker 556 if (!haveMutex) { 557 MUTEX_LOCK(&memBlockListMutex); 558 } 559 MEM_ASSERT (memBlock->inFlight, "trying to clear an unlocked memBlock\n"); 560 memBlock->inFlight = false; 561 clearLock ++; 562 if (memBlock->nextBlock) { 563 MEM_ASSERT (memBlock->nextBlock->inFlight, "trying to clear an unlocked memBlock\n"); 564 memBlock->nextBlock->inFlight = false; 565 } 566 if (memBlock->previousBlock) { 567 MEM_ASSERT (memBlock->previousBlock->inFlight, "trying to clear an unlocked memBlock\n"); 568 memBlock->previousBlock->inFlight = false; 569 } 570 MUTEX_UNLOCK(&memBlockListMutex); 571 return; 572 # endif 573 } 330 574 331 575 /* Actually allocate memory … … 365 609 // function 366 610 memBlock->func = func; 611 612 // per-block lock (off by default) 613 memBlock->inFlight = false; 367 614 368 615 #if defined(PS_MEM_BACKTRACE) && defined(HAVE_BACKTRACE) … … 394 641 memBlock->refCounter = 1; // one user so far 395 642 643 psMemBlock *last0 = lastMemBlockAllocated; 644 int flight0 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10; 645 396 646 // need exclusive access of the memory block list now... 397 647 // this lock is very frequently called in a given program 398 MUTEX_LOCK(&memBlockListMutex); 399 400 // increment the memory id only after we've grabbed the memBlockListMutex 648 // lastMemBlockAllocated is always true except the first allocation 649 BLOCKLAST_LOCK (); 650 651 psMemBlock *last1 = lastMemBlockAllocated; 652 int flight1 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10; 653 654 if (false) { 655 fprintf (stderr, "last 0 : %lld, last 1 : %lld, flight0: %d, flight1: %d\n", (long long int) last0, (long long int) last1, (int) flight0, (int) flight1); 656 } 657 658 // increment the memory id only after we've grabbed the memBlockListMutex this value is 659 // returned by psMemGetID and psMemGetLastID, but only ever modified here 401 660 *(psMemId* )&memBlock->id = ++memid; 402 661 … … 415 674 } 416 675 417 MUTEX_UNLOCK(&memBlockListMutex);676 BLOCK_UNLOCK (memBlock->nextBlock, true); 418 677 419 678 // And return the user the memory that they allocated … … 536 795 // Reallocate the memory 537 796 538 // we need to lock this whole section because we need to be able to adjust 539 // lastMemBlockAllocated if needed (ie, this IS lastMemBlockAllocated) 540 // this lock is frequently called in a given program 541 MUTEX_LOCK(&memBlockListMutex); 797 /* psRealloc and locks: 798 799 psRealloc calls 'realloc' to change the size of the memory block. This call is likely 800 to change the address of the memory block itself. This is a problem because the address 801 of the memory block is referred to by the neighbor blocks via their memBlock->nextBlock 802 and memBlock->previousBlock elements. If the current memBlock is the first one, then 803 this issue also applies to 'lastMemBlockAllocated'. The elements may also be modified 804 by psFree and psAlloc. Thus, we need to prevent multiple threads from calling psFree, 805 psAlloc, or psRealloc on neighbor memory blocks at the same time. 806 807 Unfortunately, unlike psAlloc and psFree, psRealloc cannot perform the (expensive) 808 system call operation (realloc) first and adjust the pointers in separate step (since 809 the value modified by realloc *is* one of those points. It is thus necessary to include 810 the realloc call within the locked segement, making psRealloc likely to serialize the 811 thread operations. 812 813 Alternatively, we can recognize that the lock only need be applied to operations which 814 are performed on neighboring memBlocks. We can thus reduce the contention by having a 815 per-memBlock boolean (inFlight) which says the memBlock or a neighbor is being 816 modified. If psFree and psAlloc respect that boolean, they will not modify a memBlock 817 which is already being realloced (or its neighbor). In this way, the 'realloc' call can 818 be performed outside of the locked region. 819 820 */ 821 822 // set a lock, then lock this memblock and its neighbors. at the end of this call, 823 // the global mutex is released, but the memBlock and its neighbors are protected 824 BLOCKSET_LOCK (memBlock, false); 825 // MUTEX_LOCK(&memBlockListMutex); 542 826 543 827 psMemBlock *nextBlock = memBlock->nextBlock; … … 549 833 bool isBlockLast = (memBlock == lastMemBlockAllocated); 550 834 835 // do the expensive system call. other threads can continue to modify other memBlocks 836 // except this one and its two neighbors 551 837 memBlock = (psMemBlock *)realloc(memBlock, sizeof(psMemBlock) + size + sizeof(void *)); 552 838 if (memBlock == NULL) { … … 582 868 } 583 869 584 MUTEX_UNLOCK(&memBlockListMutex); 870 // all of the list modifications are done; set the lock and clear the per-memBlock locks 871 BLOCKSET_UNLOCK(memBlock, false); 872 // MUTEX_UNLOCK(&memBlockListMutex); 873 874 // XXX these are not actually guaranteed : another thread may already grab them before we get here 875 // psAssert (!memBlock->inFlight, "unreleased lock?"); 876 // psAssert (!nextBlock || !nextBlock->inFlight, "unreleased lock?"); 877 // psAssert (!previousBlock || !previousBlock->inFlight, "unreleased lock?"); 585 878 586 879 return memBlock + 1; // usr memory … … 601 894 psS32 j = 0; 602 895 psMemBlock *topBlock = lastMemBlockAllocated; 896 897 // XXX move this elsewhere? 898 fprintf (stderr, "set %d locks, cleared %d locks, retry on %d locks (memID %ld)\n", setLock, clearLock, retryLock, memid); 603 899 604 900 // make sure that the memblock list is free of corruption before we crawl … … 808 1104 // if we have multiple references, just decrement the count and return. 809 1105 // we need a MUTEX_LOCK here: otherwise, two functions can race on refCounter--; 810 MUTEX_LOCK(&memBlockListMutex);1106 BLOCK_LOCK (memBlock, true); 811 1107 if (memBlock->refCounter > 1) { 812 1108 memBlock->refCounter--; … … 816 1112 memFreeID += memFreeCallback(memBlock); 817 1113 } 818 MUTEX_UNLOCK(&memBlockListMutex);1114 BLOCK_UNLOCK(memBlock, true); 819 1115 return ptr; 820 1116 } 821 MUTEX_UNLOCK(&memBlockListMutex);1117 BLOCK_UNLOCK(memBlock, true); 822 1118 823 1119 // we can't invoke freeFunc() while we're holding memBlockListMutex as it … … 828 1124 829 1125 // this lock is frequently set in a given program 830 MUTEX_LOCK(&memBlockListMutex);1126 BLOCKSET_LOCK (memBlock, true); 831 1127 832 1128 // Did the user ask to be informed about this deallocation? … … 849 1145 } 850 1146 1147 BLOCKSET_UNLOCK (memBlock, true); 1148 1149 // XXX keep this? 1150 psAssert (memBlock->nextBlock == nextBlock, "unexpected rearrangement"); 1151 psAssert (memBlock->previousBlock == previousBlock, "unexpected rearrangement"); 1152 851 1153 // NULL out the refs so no one can get confused 852 1154 memBlock->nextBlock = NULL; 853 1155 memBlock->previousBlock = NULL; 854 1156 855 MUTEX_UNLOCK(&memBlockListMutex); 1157 // XXX these are not actually guaranteed : another thread may already grab them before we get here 1158 // psAssert (!memBlock->inFlight, "unreleased lock?"); 1159 // psAssert (!nextBlock || !nextBlock->inFlight, "unreleased lock?"); 1160 // psAssert (!previousBlock || !previousBlock->inFlight, "unreleased lock?"); 856 1161 857 1162 // invoke free only after we've released the block list lock as free()
Note:
See TracChangeset
for help on using the changeset viewer.
