Home | History | Annotate | Download | only in allocator
      1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <config.h>
      6 
      7 // When defined, different heap allocators can be used via an environment
      8 // variable set before running the program.  This may reduce the amount
      9 // of inlining that we get with malloc/free/etc.  Disabling makes it
     10 // so that only tcmalloc can be used.
     11 #define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
     12 
     13 // TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
     14 // from the "user code" so that debugging tools (HeapChecker) can work.
     15 
     16 // __THROW is defined in glibc systems.  It means, counter-intuitively,
     17 // "This function will never throw an exception."  It's an optional
     18 // optimization tool, but we may need to use it to match glibc prototypes.
     19 #ifndef __THROW    // I guess we're not on a glibc system
     20 # define __THROW   // __THROW is just an optimization, so ok to make it ""
     21 #endif
     22 
     23 // new_mode behaves similarly to MSVC's _set_new_mode.
     24 // If flag is 0 (default), calls to malloc will behave normally.
     25 // If flag is 1, calls to malloc will behave like calls to new,
     26 // and the std_new_handler will be invoked on failure.
     27 // Can be set by calling _set_new_mode().
     28 static int new_mode = 0;
     29 
     30 typedef enum {
     31   TCMALLOC,    // TCMalloc is the default allocator.
     32   JEMALLOC,    // JEMalloc
     33   WINDEFAULT,  // Windows Heap
     34   WINLFH,      // Windows LFH Heap
     35 } Allocator;
     36 
     37 // This is the default allocator.
     38 static Allocator allocator = TCMALLOC;
     39 
     40 // We include tcmalloc and the win_allocator to get as much inlining as
     41 // possible.
     42 #include "tcmalloc.cc"
     43 #include "win_allocator.cc"
     44 
     45 // Forward declarations from jemalloc.
     46 extern "C" {
     47 void* je_malloc(size_t s);
     48 void* je_realloc(void* p, size_t s);
     49 void je_free(void* s);
     50 size_t je_msize(void* p);
     51 bool je_malloc_init_hard();
     52 }
     53 
     54 extern "C" {
     55 
     56 // Call the new handler, if one has been set.
     57 // Returns true on successfully calling the handler, false otherwise.
     58 inline bool call_new_handler(bool nothrow) {
     59   // Get the current new handler.  NB: this function is not
     60   // thread-safe.  We make a feeble stab at making it so here, but
     61   // this lock only protects against tcmalloc interfering with
     62   // itself, not with other libraries calling set_new_handler.
     63   std::new_handler nh;
     64   {
     65     SpinLockHolder h(&set_new_handler_lock);
     66     nh = std::set_new_handler(0);
     67     (void) std::set_new_handler(nh);
     68   }
     69 #if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
     70   if (!nh)
     71     return false;
     72   // Since exceptions are disabled, we don't really know if new_handler
     73   // failed.  Assume it will abort if it fails.
     74   (*nh)();
     75   return false;  // break out of the retry loop.
     76 #else
     77   // If no new_handler is established, the allocation failed.
     78   if (!nh) {
     79     if (nothrow)
     80       return 0;
     81     throw std::bad_alloc();
     82   }
     83   // Otherwise, try the new_handler.  If it returns, retry the
     84   // allocation.  If it throws std::bad_alloc, fail the allocation.
     85   // if it throws something else, don't interfere.
     86   try {
     87     (*nh)();
     88   } catch (const std::bad_alloc&) {
     89     if (!nothrow)
     90       throw;
     91     return p;
     92   }
     93 #endif  // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
     94 }
     95 
     96 void* malloc(size_t size) __THROW {
     97   void* ptr;
     98   for (;;) {
     99 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    100     switch (allocator) {
    101       case JEMALLOC:
    102         ptr = je_malloc(size);
    103         break;
    104       case WINDEFAULT:
    105       case WINLFH:
    106         ptr = win_heap_malloc(size);
    107         break;
    108       case TCMALLOC:
    109       default:
    110         ptr = do_malloc(size);
    111         break;
    112     }
    113 #else
    114     // TCMalloc case.
    115     ptr = do_malloc(size);
    116 #endif
    117     if (ptr)
    118       return ptr;
    119 
    120     if (!new_mode || !call_new_handler(true))
    121       break;
    122   }
    123   return ptr;
    124 }
    125 
    126 void free(void* p) __THROW {
    127 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    128   switch (allocator) {
    129     case JEMALLOC:
    130       je_free(p);
    131       return;
    132     case WINDEFAULT:
    133     case WINLFH:
    134       win_heap_free(p);
    135       return;
    136   }
    137 #endif
    138   // TCMalloc case.
    139   do_free(p);
    140 }
    141 
    142 void* realloc(void* ptr, size_t size) __THROW {
    143   // Webkit is brittle for allocators that return NULL for malloc(0).  The
    144   // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
    145   // to call malloc for this case.
    146   if (!ptr)
    147     return malloc(size);
    148 
    149   void* new_ptr;
    150   for (;;) {
    151 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    152     switch (allocator) {
    153       case JEMALLOC:
    154         new_ptr = je_realloc(ptr, size);
    155         break;
    156       case WINDEFAULT:
    157       case WINLFH:
    158         new_ptr = win_heap_realloc(ptr, size);
    159         break;
    160       case TCMALLOC:
    161       default:
    162         new_ptr = do_realloc(ptr, size);
    163         break;
    164     }
    165 #else
    166     // TCMalloc case.
    167     new_ptr = do_realloc(ptr, size);
    168 #endif
    169 
    170     // Subtle warning:  NULL return does not alwas indicate out-of-memory.  If
    171     // the requested new size is zero, realloc should free the ptr and return
    172     // NULL.
    173     if (new_ptr || !size)
    174       return new_ptr;
    175     if (!new_mode || !call_new_handler(true))
    176       break;
    177   }
    178   return new_ptr;
    179 }
    180 
    181 // TODO(mbelshe): Implement this for other allocators.
    182 void malloc_stats(void) __THROW {
    183 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    184   switch (allocator) {
    185     case JEMALLOC:
    186       // No stats.
    187       return;
    188     case WINDEFAULT:
    189     case WINLFH:
    190       // No stats.
    191       return;
    192   }
    193 #endif
    194   tc_malloc_stats();
    195 }
    196 
    197 #ifdef WIN32
    198 
    199 extern "C" size_t _msize(void* p) {
    200 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    201   switch (allocator) {
    202     case JEMALLOC:
    203       return je_msize(p);
    204     case WINDEFAULT:
    205     case WINLFH:
    206       return win_heap_msize(p);
    207   }
    208 #endif
    209   return MallocExtension::instance()->GetAllocatedSize(p);
    210 }
    211 
    212 // This is included to resolve references from libcmt.
    213 extern "C" intptr_t _get_heap_handle() {
    214   return 0;
    215 }
    216 
    217 // The CRT heap initialization stub.
    218 extern "C" int _heap_init() {
    219 #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
    220   const char* override = GetenvBeforeMain("CHROME_ALLOCATOR");
    221   if (override) {
    222     if (!stricmp(override, "jemalloc"))
    223       allocator = JEMALLOC;
    224     else if (!stricmp(override, "winheap"))
    225       allocator = WINDEFAULT;
    226     else if (!stricmp(override, "winlfh"))
    227       allocator = WINLFH;
    228     else if (!stricmp(override, "tcmalloc"))
    229       allocator = TCMALLOC;
    230   }
    231 
    232   switch (allocator) {
    233     case JEMALLOC:
    234       return je_malloc_init_hard() ? 0 : 1;
    235     case WINDEFAULT:
    236       return win_heap_init(false) ? 1 : 0;
    237     case WINLFH:
    238       return win_heap_init(true) ? 1 : 0;
    239     case TCMALLOC:
    240     default:
    241       // fall through
    242       break;
    243   }
    244 #endif
    245   // Initializing tcmalloc.
    246   // We intentionally leak this object.  It lasts for the process
    247   // lifetime.  Trying to teardown at _heap_term() is so late that
    248   // you can't do anything useful anyway.
    249   new TCMallocGuard();
    250   return 1;
    251 }
    252 
    253 // The CRT heap cleanup stub.
    254 extern "C" void _heap_term() {}
    255 
    256 // We set this to 1 because part of the CRT uses a check of _crtheap != 0
    257 // to test whether the CRT has been initialized.  Once we've ripped out
    258 // the allocators from libcmt, we need to provide this definition so that
    259 // the rest of the CRT is still usable.
    260 extern "C" void* _crtheap = reinterpret_cast<void*>(1);
    261 
    262 #endif  // WIN32
    263 
    264 #include "generic_allocators.cc"
    265 
    266 }  // extern C
    267