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