summaryrefslogtreecommitdiff
path: root/src/debugallocation.cc
diff options
context:
space:
mode:
authorAliaksey Kandratsenka <alk@tut.by>2015-08-01 22:20:15 -0700
committerAliaksey Kandratsenka <alk@tut.by>2015-08-02 19:06:21 -0700
commitc4493874cd3b662d2778f3b79a3096ae61569b67 (patch)
tree9d572d23f8c6880eb125af92deb4b7a3a0f4ec3e /src/debugallocation.cc
parent09448a8fe977eaa083340c2504caac8820832179 (diff)
downloadgperftools-c4493874cd3b662d2778f3b79a3096ae61569b67.tar.gz
deal with OOM handling in one place and prior to returning result
This commit removes 4 (four!) duplicates of C++ OOM handling. And introduces one helper for that. Other change is that malloc doesn't have to check tc_new_mode anymore until it _actually_ deals with OOM condition. Which shaves off couple instructions from fast-path.
Diffstat (limited to 'src/debugallocation.cc')
-rw-r--r--src/debugallocation.cc130
1 files changed, 45 insertions, 85 deletions
diff --git a/src/debugallocation.cc b/src/debugallocation.cc
index 46a9889..8487ca3 100644
--- a/src/debugallocation.cc
+++ b/src/debugallocation.cc
@@ -1169,58 +1169,43 @@ REGISTER_MODULE_DESTRUCTOR(debugallocation, {
// ========================================================================= //
+struct debug_alloc_retry_data {
+ size_t size;
+ int new_type;
+};
+
+static void *retry_debug_allocate(void *arg) {
+ debug_alloc_retry_data *data = static_cast<debug_alloc_retry_data *>(arg);
+ return DebugAllocate(data->size, data->new_type);
+}
+
// This is mostly the same a cpp_alloc in tcmalloc.cc.
// TODO(csilvers): change Allocate() above to call cpp_alloc, so we
// don't have to reproduce the logic here. To make tc_new_mode work
// properly, I think we'll need to separate out the logic of throwing
// from the logic of calling the new-handler.
inline void* debug_cpp_alloc(size_t size, int new_type, bool nothrow) {
- for (;;) {
- void* p = DebugAllocate(size, new_type);
- if (p == NULL) { // allocation failed
- // 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) {
- // Since exceptions are disabled, we don't really know if new_handler
- // failed. Assume it will abort if it fails.
- (*nh)();
- continue;
- }
- return 0;
-#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)
- } else { // allocation success
- return p;
- }
+ void* p = DebugAllocate(size, new_type);
+ if (p != NULL) {
+ return p;
}
+ struct debug_alloc_retry_data data;
+ data.size = size;
+ data.new_type = new_type;
+ return handle_oom(retry_debug_allocate, &data,
+ true, nothrow, true);
}
inline void* do_debug_malloc_or_debug_cpp_alloc(size_t size) {
- return tc_new_mode ? debug_cpp_alloc(size, MallocBlock::kMallocType, true)
- : DebugAllocate(size, MallocBlock::kMallocType);
+ void* p = DebugAllocate(size, MallocBlock::kMallocType);
+ if (p != NULL) {
+ return p;
+ }
+ struct debug_alloc_retry_data data;
+ data.size = size;
+ data.new_type = MallocBlock::kMallocType;
+ return handle_oom(retry_debug_allocate, &data,
+ false, true, true);
}
// Exported routines
@@ -1378,53 +1363,28 @@ static void *do_debug_memalign(size_t alignment, size_t size) {
return p;
}
-// This is mostly the same as cpp_memalign in tcmalloc.cc.
-static void* debug_cpp_memalign(size_t align, size_t size) {
- for (;;) {
- void* p = do_debug_memalign(align, size);
- if (p == NULL) { // allocation failed
- // 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) {
- // Since exceptions are disabled, we don't really know if new_handler
- // failed. Assume it will abort if it fails.
- (*nh)();
- continue;
- }
- return 0;
-#else
- // If no new_handler is established, the allocation failed.
- if (!nh)
- return 0;
-
- // 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&) {
- return p;
- }
-#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
- } else { // allocation success
- return p;
- }
- }
+struct memalign_retry_data {
+ size_t align;
+ size_t size;
+};
+
+static void *retry_debug_memalign(void *arg) {
+ memalign_retry_data *data = static_cast<memalign_retry_data *>(arg);
+ return do_debug_memalign(data->align, data->size);
}
inline void* do_debug_memalign_or_debug_cpp_memalign(size_t align,
size_t size) {
- return tc_new_mode ? debug_cpp_memalign(align, size)
- : do_debug_memalign(align, size);
+ void* p = do_debug_memalign(align, size);
+ if (p != NULL) {
+ return p;
+ }
+
+ struct memalign_retry_data data;
+ data.align = align;
+ data.size = size;
+ return handle_oom(retry_debug_memalign, &data,
+ false, true, false);
}
extern "C" PERFTOOLS_DLL_DECL void* tc_memalign(size_t align, size_t size) __THROW {