tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prmem.c (16043B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 ** Thread safe versions of malloc, free, realloc, calloc and cfree.
      8 */
      9 
     10 #include "primpl.h"
     11 
     12 #ifdef _PR_ZONE_ALLOCATOR
     13 
     14 /*
     15 ** The zone allocator code must use native mutexes and cannot
     16 ** use PRLocks because PR_NewLock calls PR_Calloc, resulting
     17 ** in cyclic dependency of initialization.
     18 */
     19 
     20 #  include <string.h>
     21 
     22 union memBlkHdrUn;
     23 
     24 typedef struct MemoryZoneStr {
     25  union memBlkHdrUn* head; /* free list */
     26  pthread_mutex_t lock;
     27  size_t blockSize;    /* size of blocks on this free list */
     28  PRUint32 locked;     /* current state of lock */
     29  PRUint32 contention; /* counter: had to wait for lock */
     30  PRUint32 hits;       /* allocated from free list */
     31  PRUint32 misses;     /* had to call malloc */
     32  PRUint32 elements;   /* on free list */
     33 } MemoryZone;
     34 
     35 typedef union memBlkHdrUn {
     36  unsigned char filler[48]; /* fix the size of this beast */
     37  struct memBlkHdrStr {
     38    union memBlkHdrUn* next;
     39    MemoryZone* zone;
     40    size_t blockSize;
     41    size_t requestedSize;
     42    PRUint32 magic;
     43  } s;
     44 } MemBlockHdr;
     45 
     46 #  define MEM_ZONES 7
     47 #  define THREAD_POOLS 11 /* prime number for modulus */
     48 #  define ZONE_MAGIC 0x0BADC0DE
     49 
     50 static MemoryZone zones[MEM_ZONES][THREAD_POOLS];
     51 
     52 static PRBool use_zone_allocator = PR_FALSE;
     53 
     54 static void pr_ZoneFree(void* ptr);
     55 
     56 void _PR_DestroyZones(void) {
     57  int i, j;
     58 
     59  if (!use_zone_allocator) {
     60    return;
     61  }
     62 
     63  for (j = 0; j < THREAD_POOLS; j++) {
     64    for (i = 0; i < MEM_ZONES; i++) {
     65      MemoryZone* mz = &zones[i][j];
     66      pthread_mutex_destroy(&mz->lock);
     67      while (mz->head) {
     68        MemBlockHdr* hdr = mz->head;
     69        mz->head = hdr->s.next; /* unlink it */
     70        free(hdr);
     71        mz->elements--;
     72      }
     73    }
     74  }
     75  use_zone_allocator = PR_FALSE;
     76 }
     77 
     78 /*
     79 ** pr_FindSymbolInProg
     80 **
     81 ** Find the specified data symbol in the program and return
     82 ** its address.
     83 */
     84 
     85 #  ifdef HAVE_DLL
     86 
     87 #    if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL)
     88 
     89 #      include <dlfcn.h>
     90 
     91 static void* pr_FindSymbolInProg(const char* name) {
     92  void* h;
     93  void* sym;
     94 
     95  h = dlopen(0, RTLD_LAZY);
     96  if (h == NULL) {
     97    return NULL;
     98  }
     99  sym = dlsym(h, name);
    100  (void)dlclose(h);
    101  return sym;
    102 }
    103 
    104 #    elif defined(USE_HPSHL)
    105 
    106 #      include <dl.h>
    107 
    108 static void* pr_FindSymbolInProg(const char* name) {
    109  shl_t h = NULL;
    110  void* sym;
    111 
    112  if (shl_findsym(&h, name, TYPE_DATA, &sym) == -1) {
    113    return NULL;
    114  }
    115  return sym;
    116 }
    117 
    118 #    elif defined(USE_MACH_DYLD) || defined(NO_DLOPEN_NULL)
    119 
    120 static void* pr_FindSymbolInProg(const char* name) {
    121  /* FIXME: not implemented */
    122  return NULL;
    123 }
    124 
    125 #    else
    126 
    127 #      error "The zone allocator is not supported on this platform"
    128 
    129 #    endif
    130 
    131 #  else /* !defined(HAVE_DLL) */
    132 
    133 static void* pr_FindSymbolInProg(const char* name) {
    134  /* can't be implemented */
    135  return NULL;
    136 }
    137 
    138 #  endif /* HAVE_DLL */
    139 
    140 void _PR_InitZones(void) {
    141  int i, j;
    142  char* envp;
    143  PRBool* sym;
    144 
    145  if ((sym = (PRBool*)pr_FindSymbolInProg("nspr_use_zone_allocator")) != NULL) {
    146    use_zone_allocator = *sym;
    147  } else if ((envp = getenv("NSPR_USE_ZONE_ALLOCATOR")) != NULL) {
    148    use_zone_allocator = (atoi(envp) == 1);
    149  }
    150 
    151  if (!use_zone_allocator) {
    152    return;
    153  }
    154 
    155  for (j = 0; j < THREAD_POOLS; j++) {
    156    for (i = 0; i < MEM_ZONES; i++) {
    157      MemoryZone* mz = &zones[i][j];
    158      int rv = pthread_mutex_init(&mz->lock, NULL);
    159      PR_ASSERT(0 == rv);
    160      if (rv != 0) {
    161        goto loser;
    162      }
    163      mz->blockSize = 16 << (2 * i);
    164    }
    165  }
    166  return;
    167 
    168 loser:
    169  _PR_DestroyZones();
    170  return;
    171 }
    172 
    173 PR_IMPLEMENT(void)
    174 PR_FPrintZoneStats(PRFileDesc* debug_out) {
    175  int i, j;
    176 
    177  for (j = 0; j < THREAD_POOLS; j++) {
    178    for (i = 0; i < MEM_ZONES; i++) {
    179      MemoryZone* mz = &zones[i][j];
    180      MemoryZone zone = *mz;
    181      if (zone.elements || zone.misses || zone.hits) {
    182        PR_fprintf(debug_out,
    183                   "pool: %d, zone: %d, size: %d, free: %d, hit: %d, miss: %d, "
    184                   "contend: %d\n",
    185                   j, i, zone.blockSize, zone.elements, zone.hits, zone.misses,
    186                   zone.contention);
    187      }
    188    }
    189  }
    190 }
    191 
    192 static void* pr_ZoneMalloc(PRUint32 size) {
    193  void* rv;
    194  unsigned int zone;
    195  size_t blockSize;
    196  MemBlockHdr *mb, *mt;
    197  MemoryZone* mz;
    198 
    199  /* Always allocate a non-zero amount of bytes */
    200  if (size < 1) {
    201    size = 1;
    202  }
    203  for (zone = 0, blockSize = 16; zone < MEM_ZONES; ++zone, blockSize <<= 2) {
    204    if (size <= blockSize) {
    205      break;
    206    }
    207  }
    208  if (zone < MEM_ZONES) {
    209    pthread_t me = pthread_self();
    210    unsigned int pool = (PRUptrdiff)me % THREAD_POOLS;
    211    PRUint32 wasLocked;
    212    mz = &zones[zone][pool];
    213    wasLocked = mz->locked;
    214    pthread_mutex_lock(&mz->lock);
    215    mz->locked = 1;
    216    if (wasLocked) {
    217      mz->contention++;
    218    }
    219    if (mz->head) {
    220      mb = mz->head;
    221      PR_ASSERT(mb->s.magic == ZONE_MAGIC);
    222      PR_ASSERT(mb->s.zone == mz);
    223      PR_ASSERT(mb->s.blockSize == blockSize);
    224      PR_ASSERT(mz->blockSize == blockSize);
    225 
    226      mt = (MemBlockHdr*)(((char*)(mb + 1)) + blockSize);
    227      PR_ASSERT(mt->s.magic == ZONE_MAGIC);
    228      PR_ASSERT(mt->s.zone == mz);
    229      PR_ASSERT(mt->s.blockSize == blockSize);
    230 
    231      mz->hits++;
    232      mz->elements--;
    233      mz->head = mb->s.next; /* take off free list */
    234      mz->locked = 0;
    235      pthread_mutex_unlock(&mz->lock);
    236 
    237      mt->s.next = mb->s.next = NULL;
    238      mt->s.requestedSize = mb->s.requestedSize = size;
    239 
    240      rv = (void*)(mb + 1);
    241      return rv;
    242    }
    243 
    244    mz->misses++;
    245    mz->locked = 0;
    246    pthread_mutex_unlock(&mz->lock);
    247 
    248    mb = (MemBlockHdr*)malloc(blockSize + 2 * (sizeof *mb));
    249    if (!mb) {
    250      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    251      return NULL;
    252    }
    253    mb->s.next = NULL;
    254    mb->s.zone = mz;
    255    mb->s.magic = ZONE_MAGIC;
    256    mb->s.blockSize = blockSize;
    257    mb->s.requestedSize = size;
    258 
    259    mt = (MemBlockHdr*)(((char*)(mb + 1)) + blockSize);
    260    memcpy(mt, mb, sizeof *mb);
    261 
    262    rv = (void*)(mb + 1);
    263    return rv;
    264  }
    265 
    266  /* size was too big.  Create a block with no zone */
    267  blockSize = (size & 15) ? size + 16 - (size & 15) : size;
    268  mb = (MemBlockHdr*)malloc(blockSize + 2 * (sizeof *mb));
    269  if (!mb) {
    270    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    271    return NULL;
    272  }
    273  mb->s.next = NULL;
    274  mb->s.zone = NULL;
    275  mb->s.magic = ZONE_MAGIC;
    276  mb->s.blockSize = blockSize;
    277  mb->s.requestedSize = size;
    278 
    279  mt = (MemBlockHdr*)(((char*)(mb + 1)) + blockSize);
    280  memcpy(mt, mb, sizeof *mb);
    281 
    282  rv = (void*)(mb + 1);
    283  return rv;
    284 }
    285 
    286 static void* pr_ZoneCalloc(PRUint32 nelem, PRUint32 elsize) {
    287  PRUint32 size = nelem * elsize;
    288  void* p = pr_ZoneMalloc(size);
    289  if (p) {
    290    memset(p, 0, size);
    291  }
    292  return p;
    293 }
    294 
    295 static void* pr_ZoneRealloc(void* oldptr, PRUint32 bytes) {
    296  void* rv;
    297  MemBlockHdr* mb;
    298  int ours;
    299  MemBlockHdr phony;
    300 
    301  if (!oldptr) {
    302    return pr_ZoneMalloc(bytes);
    303  }
    304  mb = (MemBlockHdr*)((char*)oldptr - (sizeof *mb));
    305  if (mb->s.magic != ZONE_MAGIC) {
    306    /* Maybe this just came from ordinary malloc */
    307 #  ifdef DEBUG
    308    fprintf(stderr,
    309            "Warning: reallocing memory block %p from ordinary malloc\n",
    310            oldptr);
    311 #  endif
    312    /*
    313     * We are going to realloc oldptr.  If realloc succeeds, the
    314     * original value of oldptr will point to freed memory.  So this
    315     * function must not fail after a successfull realloc call.  We
    316     * must perform any operation that may fail before the realloc
    317     * call.
    318     */
    319    rv = pr_ZoneMalloc(bytes); /* this may fail */
    320    if (!rv) {
    321      return rv;
    322    }
    323 
    324    /* We don't know how big it is.  But we can fix that. */
    325    oldptr = realloc(oldptr, bytes);
    326    /*
    327     * If realloc returns NULL, this function loses the original
    328     * value of oldptr.  This isn't a leak because the caller of
    329     * this function still has the original value of oldptr.
    330     */
    331    if (!oldptr) {
    332      if (bytes) {
    333        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    334        pr_ZoneFree(rv);
    335        return oldptr;
    336      }
    337    }
    338    phony.s.requestedSize = bytes;
    339    mb = &phony;
    340    ours = 0;
    341  } else {
    342    size_t blockSize = mb->s.blockSize;
    343    MemBlockHdr* mt = (MemBlockHdr*)(((char*)(mb + 1)) + blockSize);
    344 
    345    PR_ASSERT(mt->s.magic == ZONE_MAGIC);
    346    PR_ASSERT(mt->s.zone == mb->s.zone);
    347    PR_ASSERT(mt->s.blockSize == blockSize);
    348 
    349    if (bytes <= blockSize) {
    350      /* The block is already big enough. */
    351      mt->s.requestedSize = mb->s.requestedSize = bytes;
    352      return oldptr;
    353    }
    354    ours = 1;
    355    rv = pr_ZoneMalloc(bytes);
    356    if (!rv) {
    357      return rv;
    358    }
    359  }
    360 
    361  if (oldptr && mb->s.requestedSize) {
    362    memcpy(rv, oldptr, mb->s.requestedSize);
    363  }
    364  if (ours) {
    365    pr_ZoneFree(oldptr);
    366  } else if (oldptr) {
    367    free(oldptr);
    368  }
    369  return rv;
    370 }
    371 
    372 static void pr_ZoneFree(void* ptr) {
    373  MemBlockHdr *mb, *mt;
    374  MemoryZone* mz;
    375  size_t blockSize;
    376  PRUint32 wasLocked;
    377 
    378  if (!ptr) {
    379    return;
    380  }
    381 
    382  mb = (MemBlockHdr*)((char*)ptr - (sizeof *mb));
    383 
    384  if (mb->s.magic != ZONE_MAGIC) {
    385    /* maybe this came from ordinary malloc */
    386 #  ifdef DEBUG
    387    fprintf(stderr, "Warning: freeing memory block %p from ordinary malloc\n",
    388            ptr);
    389 #  endif
    390    free(ptr);
    391    return;
    392  }
    393 
    394  blockSize = mb->s.blockSize;
    395  mz = mb->s.zone;
    396  mt = (MemBlockHdr*)(((char*)(mb + 1)) + blockSize);
    397  PR_ASSERT(mt->s.magic == ZONE_MAGIC);
    398  PR_ASSERT(mt->s.zone == mz);
    399  PR_ASSERT(mt->s.blockSize == blockSize);
    400  if (!mz) {
    401    PR_ASSERT(blockSize > 65536);
    402    /* This block was not in any zone.  Just free it. */
    403    free(mb);
    404    return;
    405  }
    406  PR_ASSERT(mz->blockSize == blockSize);
    407  wasLocked = mz->locked;
    408  pthread_mutex_lock(&mz->lock);
    409  mz->locked = 1;
    410  if (wasLocked) {
    411    mz->contention++;
    412  }
    413  mt->s.next = mb->s.next = mz->head; /* put on head of list */
    414  mz->head = mb;
    415  mz->elements++;
    416  mz->locked = 0;
    417  pthread_mutex_unlock(&mz->lock);
    418 }
    419 
    420 PR_IMPLEMENT(void*) PR_Malloc(PRUint32 size) {
    421  if (!_pr_initialized) {
    422    _PR_ImplicitInitialization();
    423  }
    424 
    425  return use_zone_allocator ? pr_ZoneMalloc(size) : malloc(size);
    426 }
    427 
    428 PR_IMPLEMENT(void*) PR_Calloc(PRUint32 nelem, PRUint32 elsize) {
    429  if (!_pr_initialized) {
    430    _PR_ImplicitInitialization();
    431  }
    432 
    433  return use_zone_allocator ? pr_ZoneCalloc(nelem, elsize)
    434                            : calloc(nelem, elsize);
    435 }
    436 
    437 PR_IMPLEMENT(void*) PR_Realloc(void* ptr, PRUint32 size) {
    438  if (!_pr_initialized) {
    439    _PR_ImplicitInitialization();
    440  }
    441 
    442  return use_zone_allocator ? pr_ZoneRealloc(ptr, size) : realloc(ptr, size);
    443 }
    444 
    445 PR_IMPLEMENT(void) PR_Free(void* ptr) {
    446  if (use_zone_allocator) {
    447    pr_ZoneFree(ptr);
    448  } else {
    449    free(ptr);
    450  }
    451 }
    452 
    453 #else /* !defined(_PR_ZONE_ALLOCATOR) */
    454 
    455 /*
    456 ** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply
    457 ** call their libc equivalents now.  This may seem redundant, but it
    458 ** ensures that we are calling into the same runtime library.  On
    459 ** Win32, it is possible to have multiple runtime libraries (e.g.,
    460 ** objects compiled with /MD and /MDd) in the same process, and
    461 ** they maintain separate heaps, which cannot be mixed.
    462 */
    463 PR_IMPLEMENT(void*) PR_Malloc(PRUint32 size) { return malloc(size); }
    464 
    465 PR_IMPLEMENT(void*) PR_Calloc(PRUint32 nelem, PRUint32 elsize) {
    466  return calloc(nelem, elsize);
    467 }
    468 
    469 PR_IMPLEMENT(void*) PR_Realloc(void* ptr, PRUint32 size) {
    470  return realloc(ptr, size);
    471 }
    472 
    473 PR_IMPLEMENT(void) PR_Free(void* ptr) { free(ptr); }
    474 
    475 #endif /* _PR_ZONE_ALLOCATOR */
    476 
    477 /*
    478 ** Complexity alert!
    479 **
    480 ** If malloc/calloc/free (etc.) were implemented to use pr lock's then
    481 ** the entry points could block when called if some other thread had the
    482 ** lock.
    483 **
    484 ** Most of the time this isn't a problem. However, in the case that we
    485 ** are using the thread safe malloc code after PR_Init but before
    486 ** PR_AttachThread has been called (on a native thread that nspr has yet
    487 ** to be told about) we could get royally screwed if the lock was busy
    488 ** and we tried to context switch the thread away. In this scenario
    489 **  PR_CURRENT_THREAD() == NULL
    490 **
    491 ** To avoid this unfortunate case, we use the low level locking
    492 ** facilities for malloc protection instead of the slightly higher level
    493 ** locking. This makes malloc somewhat faster so maybe it's a good thing
    494 ** anyway.
    495 */
    496 #ifdef _PR_OVERRIDE_MALLOC
    497 
    498 /* Imports */
    499 extern void* _PR_UnlockedMalloc(size_t size);
    500 extern void* _PR_UnlockedMemalign(size_t alignment, size_t size);
    501 extern void _PR_UnlockedFree(void* ptr);
    502 extern void* _PR_UnlockedRealloc(void* ptr, size_t size);
    503 extern void* _PR_UnlockedCalloc(size_t n, size_t elsize);
    504 
    505 static PRBool _PR_malloc_initialised = PR_FALSE;
    506 
    507 #  ifdef _PR_PTHREADS
    508 static pthread_mutex_t _PR_MD_malloc_crustylock;
    509 
    510 #    define _PR_Lock_Malloc()                                 \
    511      {                                                       \
    512        if (PR_TRUE == _PR_malloc_initialised) {              \
    513          PRStatus rv;                                        \
    514          rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \
    515          PR_ASSERT(0 == rv);                                 \
    516        }
    517 
    518 #    define _PR_Unlock_Malloc()                               \
    519      if (PR_TRUE == _PR_malloc_initialised) {                \
    520        PRStatus rv;                                          \
    521        rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \
    522        PR_ASSERT(0 == rv);                                   \
    523      }                                                       \
    524      }
    525 #  else /* _PR_PTHREADS */
    526 static _MDLock _PR_MD_malloc_crustylock;
    527 
    528 #    define _PR_Lock_Malloc()                                 \
    529      {                                                       \
    530        PRIntn _is;                                           \
    531        if (PR_TRUE == _PR_malloc_initialised) {              \
    532          if (_PR_MD_CURRENT_THREAD() &&                      \
    533              !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) \
    534            _PR_INTSOFF(_is);                                 \
    535          _PR_MD_LOCK(&_PR_MD_malloc_crustylock);             \
    536        }
    537 
    538 #    define _PR_Unlock_Malloc()                             \
    539      if (PR_TRUE == _PR_malloc_initialised) {              \
    540        _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock);           \
    541        if (_PR_MD_CURRENT_THREAD() &&                      \
    542            !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) \
    543          _PR_INTSON(_is);                                  \
    544      }                                                     \
    545      }
    546 #  endif /* _PR_PTHREADS */
    547 
    548 PR_IMPLEMENT(PRStatus) _PR_MallocInit(void) {
    549  PRStatus rv = PR_SUCCESS;
    550 
    551  if (PR_TRUE == _PR_malloc_initialised) {
    552    return PR_SUCCESS;
    553  }
    554 
    555 #  ifdef _PR_PTHREADS
    556  {
    557    int status;
    558    pthread_mutexattr_t mattr;
    559 
    560    status = _PT_PTHREAD_MUTEXATTR_INIT(&mattr);
    561    PR_ASSERT(0 == status);
    562    status = _PT_PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr);
    563    PR_ASSERT(0 == status);
    564    status = _PT_PTHREAD_MUTEXATTR_DESTROY(&mattr);
    565    PR_ASSERT(0 == status);
    566  }
    567 #  else  /* _PR_PTHREADS */
    568  _MD_NEW_LOCK(&_PR_MD_malloc_crustylock);
    569 #  endif /* _PR_PTHREADS */
    570 
    571  if (PR_SUCCESS == rv) {
    572    _PR_malloc_initialised = PR_TRUE;
    573  }
    574 
    575  return rv;
    576 }
    577 
    578 void* malloc(size_t size) {
    579  void* p;
    580  _PR_Lock_Malloc();
    581  p = _PR_UnlockedMalloc(size);
    582  _PR_Unlock_Malloc();
    583  return p;
    584 }
    585 
    586 void free(void* ptr) {
    587  _PR_Lock_Malloc();
    588  _PR_UnlockedFree(ptr);
    589  _PR_Unlock_Malloc();
    590 }
    591 
    592 void* realloc(void* ptr, size_t size) {
    593  void* p;
    594  _PR_Lock_Malloc();
    595  p = _PR_UnlockedRealloc(ptr, size);
    596  _PR_Unlock_Malloc();
    597  return p;
    598 }
    599 
    600 void* calloc(size_t n, size_t elsize) {
    601  void* p;
    602  _PR_Lock_Malloc();
    603  p = _PR_UnlockedCalloc(n, elsize);
    604  _PR_Unlock_Malloc();
    605  return p;
    606 }
    607 
    608 void cfree(void* p) {
    609  _PR_Lock_Malloc();
    610  _PR_UnlockedFree(p);
    611  _PR_Unlock_Malloc();
    612 }
    613 
    614 void _PR_InitMem(void) {
    615  PRStatus rv;
    616  rv = _PR_MallocInit();
    617  PR_ASSERT(PR_SUCCESS == rv);
    618 }
    619 
    620 #endif /* _PR_OVERRIDE_MALLOC */