allocator_shim.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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(). 28static int new_mode = 0; 29 30typedef 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. 38static 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. 46extern "C" { 47void* je_malloc(size_t s); 48void* je_realloc(void* p, size_t s); 49void je_free(void* s); 50size_t je_msize(void* p); 51bool je_malloc_init_hard(); 52} 53 54extern "C" { 55 56// Call the new handler, if one has been set. 57// Returns true on successfully calling the handler, false otherwise. 58inline 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 96void* 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 126void 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 142void* 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. 182void 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 199extern "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. 213extern "C" intptr_t _get_heap_handle() { 214 return 0; 215} 216 217// The CRT heap initialization stub. 218extern "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. 254extern "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. 260extern "C" void* _crtheap = reinterpret_cast<void*>(1); 261 262#endif // WIN32 263 264#include "generic_allocators.cc" 265 266} // extern C 267