summaryrefslogtreecommitdiffstats
path: root/base/allocator/allocator_shim.cc
diff options
context:
space:
mode:
authorsgk@chromium.org <sgk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-25 03:21:47 +0000
committersgk@chromium.org <sgk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-25 03:21:47 +0000
commit6a7e698052d3b0d0aafebd5ac6af0ef2f95ff2a9 (patch)
tree92133e44fa5b002e390da3b90c2aa7b1477c0a87 /base/allocator/allocator_shim.cc
parent8fa0b967bf38ffec49daf8cceed81603ed4316a7 (diff)
downloadchromium_src-6a7e698052d3b0d0aafebd5ac6af0ef2f95ff2a9.zip
chromium_src-6a7e698052d3b0d0aafebd5ac6af0ef2f95ff2a9.tar.gz
chromium_src-6a7e698052d3b0d0aafebd5ac6af0ef2f95ff2a9.tar.bz2
Branch the files in the shim layer that switches between the
memory allocation implementations (tcmalloc, jemalloc, etc.) into a base\allocator library. BUG=27911 TEST=none Review URL: http://codereview.chromium.org/434067 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@33043 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/allocator/allocator_shim.cc')
-rw-r--r--base/allocator/allocator_shim.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/base/allocator/allocator_shim.cc b/base/allocator/allocator_shim.cc
new file mode 100644
index 0000000..39161d4
--- /dev/null
+++ b/base/allocator/allocator_shim.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <config.h>
+
+// When defined, different heap allocators can be used via an environment
+// variable set before running the program. This may reduce the amount
+// of inlining that we get with malloc/free/etc. Disabling makes it
+// so that only tcmalloc can be used.
+#define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+
+// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
+// from the "user code" so that debugging tools (HeapChecker) can work.
+
+// __THROW is defined in glibc systems. It means, counter-intuitively,
+// "This function will never throw an exception." It's an optional
+// optimization tool, but we may need to use it to match glibc prototypes.
+#ifndef __THROW // I guess we're not on a glibc system
+# define __THROW // __THROW is just an optimization, so ok to make it ""
+#endif
+
+// new_mode behaves similarly to MSVC's _set_new_mode.
+// If flag is 0 (default), calls to malloc will behave normally.
+// If flag is 1, calls to malloc will behave like calls to new,
+// and the std_new_handler will be invoked on failure.
+// Can be set by calling _set_new_mode().
+static int new_mode = 0;
+
+typedef enum {
+ TCMALLOC, // TCMalloc is the default allocator.
+ JEMALLOC, // JEMalloc
+ WINDEFAULT, // Windows Heap
+ WINLFH, // Windows LFH Heap
+} Allocator;
+
+// This is the default allocator.
+static Allocator allocator = TCMALLOC;
+
+// We include tcmalloc and the win_allocator to get as much inlining as
+// possible.
+#include "tcmalloc.cc"
+#include "win_allocator.cc"
+
+// Forward declarations from jemalloc.
+extern "C" {
+void* je_malloc(size_t s);
+void* je_realloc(void* p, size_t s);
+void je_free(void* s);
+size_t je_msize(void* p);
+bool je_malloc_init_hard();
+}
+
+extern "C" {
+
+// Call the new handler, if one has been set.
+// Returns true on successfully calling the handler, false otherwise.
+inline bool call_new_handler(bool nothrow) {
+ // Get the current new handler. NB: this function is not
+ // thread-safe. We make a feeble stab at making it so here, but
+ // this lock only protects against tcmalloc interfering with
+ // itself, not with other libraries calling set_new_handler.
+ std::new_handler nh;
+ {
+ SpinLockHolder h(&set_new_handler_lock);
+ nh = std::set_new_handler(0);
+ (void) std::set_new_handler(nh);
+ }
+#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
+ if (!nh)
+ return false;
+ // Since exceptions are disabled, we don't really know if new_handler
+ // failed. Assume it will abort if it fails.
+ (*nh)();
+ return false; // break out of the retry loop.
+#else
+ // If no new_handler is established, the allocation failed.
+ if (!nh) {
+ if (nothrow)
+ return 0;
+ throw std::bad_alloc();
+ }
+ // Otherwise, try the new_handler. If it returns, retry the
+ // allocation. If it throws std::bad_alloc, fail the allocation.
+ // if it throws something else, don't interfere.
+ try {
+ (*nh)();
+ } catch (const std::bad_alloc&) {
+ if (!nothrow)
+ throw;
+ return p;
+ }
+#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
+}
+
+void* malloc(size_t size) __THROW {
+ void* ptr;
+ for (;;) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ ptr = je_malloc(size);
+ break;
+ case WINDEFAULT:
+ case WINLFH:
+ ptr = win_heap_malloc(size);
+ break;
+ case TCMALLOC:
+ default:
+ ptr = do_malloc(size);
+ break;
+ }
+#else
+ // TCMalloc case.
+ ptr = do_malloc(size);
+#endif
+ if (ptr)
+ return ptr;
+
+ if (!new_mode || !call_new_handler(true))
+ break;
+ }
+ return ptr;
+}
+
+void free(void* p) __THROW {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ je_free(p);
+ return;
+ case WINDEFAULT:
+ case WINLFH:
+ win_heap_free(p);
+ return;
+ }
+#endif
+ // TCMalloc case.
+ do_free(p);
+}
+
+void* realloc(void* ptr, size_t size) __THROW {
+ // Webkit is brittle for allocators that return NULL for malloc(0). The
+ // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
+ // to call malloc for this case.
+ if (!ptr)
+ return malloc(size);
+
+ void* new_ptr;
+ for (;;) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ new_ptr = je_realloc(ptr, size);
+ break;
+ case WINDEFAULT:
+ case WINLFH:
+ new_ptr = win_heap_realloc(ptr, size);
+ break;
+ case TCMALLOC:
+ default:
+ new_ptr = do_realloc(ptr, size);
+ break;
+ }
+#else
+ // TCMalloc case.
+ new_ptr = do_realloc(ptr, size);
+#endif
+
+ // Subtle warning: NULL return does not alwas indicate out-of-memory. If
+ // the requested new size is zero, realloc should free the ptr and return
+ // NULL.
+ if (new_ptr || !size)
+ return new_ptr;
+ if (!new_mode || !call_new_handler(true))
+ break;
+ }
+ return new_ptr;
+}
+
+// TODO(mbelshe): Implement this for other allocators.
+void malloc_stats(void) __THROW {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ // No stats.
+ return;
+ case WINDEFAULT:
+ case WINLFH:
+ // No stats.
+ return;
+ }
+#endif
+ tc_malloc_stats();
+}
+
+#ifdef WIN32
+
+extern "C" size_t _msize(void* p) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ return je_msize(p);
+ case WINDEFAULT:
+ case WINLFH:
+ return win_heap_msize(p);
+ }
+#endif
+ return MallocExtension::instance()->GetAllocatedSize(p);
+}
+
+// This is included to resolve references from libcmt.
+extern "C" intptr_t _get_heap_handle() {
+ return 0;
+}
+
+// The CRT heap initialization stub.
+extern "C" int _heap_init() {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ const char* override = GetenvBeforeMain("CHROME_ALLOCATOR");
+ if (override) {
+ if (!stricmp(override, "jemalloc"))
+ allocator = JEMALLOC;
+ else if (!stricmp(override, "winheap"))
+ allocator = WINDEFAULT;
+ else if (!stricmp(override, "winlfh"))
+ allocator = WINLFH;
+ else if (!stricmp(override, "tcmalloc"))
+ allocator = TCMALLOC;
+ }
+
+ switch (allocator) {
+ case JEMALLOC:
+ return je_malloc_init_hard() ? 0 : 1;
+ case WINDEFAULT:
+ return win_heap_init(false) ? 1 : 0;
+ case WINLFH:
+ return win_heap_init(true) ? 1 : 0;
+ case TCMALLOC:
+ default:
+ // fall through
+ break;
+ }
+#endif
+ // Initializing tcmalloc.
+ // We intentionally leak this object. It lasts for the process
+ // lifetime. Trying to teardown at _heap_term() is so late that
+ // you can't do anything useful anyway.
+ new TCMallocGuard();
+ return 1;
+}
+
+// The CRT heap cleanup stub.
+extern "C" void _heap_term() {}
+
+// We set this to 1 because part of the CRT uses a check of _crtheap != 0
+// to test whether the CRT has been initialized. Once we've ripped out
+// the allocators from libcmt, we need to provide this definition so that
+// the rest of the CRT is still usable.
+extern "C" void* _crtheap = reinterpret_cast<void*>(1);
+
+#endif // WIN32
+
+#include "generic_allocators.cc"
+
+} // extern C