From: Ivan Maidanski Date: Wed, 6 Jun 2018 22:08:07 +0000 (+0300) Subject: Never return null by C++ GC allocators X-Git-Tag: v8.0.0~139 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c89559ba82563f00de01f5fb5573320ef2eb80ec;p=gc Never return null by C++ GC allocators Now, in case of the allocation failure, the allocators (defined in gc_allocator.h or new_gc_alloc.h) throw bad_alloc (or abort the application if compiled without exceptions support). * include/gc_allocator.h (GC_ALLOCATOR_THROW_OR_ABORT): New macro (either throws bad_alloc or calls GC_abort_on_oom). * include/gc_allocator.h (GC_selective_alloc, traceable_allocator::allocate): Call GC_ALLOCATOR_THROW_OR_ABORT() if the allocation failed. * include/new_gc_alloc.h (GC_ALLOCATOR_THROW_OR_ABORT): New macro (redirected to GC_abort_on_oom). * include/new_gc_alloc.h (GC_out_of_line_malloc, single_client_gc_alloc_template::allocate, single_client_gc_alloc_template::ptr_free_allocate, single_client_traceable_alloc_template::allocate, single_client_traceable_alloc_template::ptr_free_allocate, gc_alloc_template::allocate, gc_alloc_template::ptr_free_allocate, traceable_alloc_template::allocate, traceable_alloc_template::ptr_free_allocate): Call GC_ALLOCATOR_THROW_OR_ABORT() instead of returning 0. * include/new_gc_alloc.h (simple_alloc::allocate): If n is 0 then allocate 1 byte. * include/new_gc_alloc.h (simple_alloc::deallocate): If n is 0 then call ptr_free_deallocate for 1-byte object. --- diff --git a/include/gc_allocator.h b/include/gc_allocator.h index 7d1d1862..6aa86644 100644 --- a/include/gc_allocator.h +++ b/include/gc_allocator.h @@ -38,11 +38,10 @@ */ #ifndef GC_ALLOCATOR_H - #define GC_ALLOCATOR_H #include "gc.h" -#include // for placement new +#include // for placement new and bad_alloc #ifndef GC_ATTR_EXPLICIT # if (__cplusplus >= 201103L) || defined(CPPCHECK) @@ -52,6 +51,12 @@ # endif #endif +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() +#else +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + /* First some helpers to allow us to dispatch on whether or not a type * is known to be pointer-free. * These are private, except that the client may invoke the @@ -87,7 +92,10 @@ GC_DECLARE_PTRFREE(long double); // pointer-free object. template inline void * GC_selective_alloc(size_t n, GC_Tp, bool ignore_off_page) { - return ignore_off_page?GC_MALLOC_IGNORE_OFF_PAGE(n):GC_MALLOC(n); + void *obj = ignore_off_page ? GC_MALLOC_IGNORE_OFF_PAGE(n) : GC_MALLOC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; } #if !defined(__WATCOMC__) @@ -95,8 +103,11 @@ inline void * GC_selective_alloc(size_t n, GC_Tp, bool ignore_off_page) { template <> inline void * GC_selective_alloc(size_t n, GC_true_type, bool ignore_off_page) { - return ignore_off_page? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) - : GC_MALLOC_ATOMIC(n); + void * obj = ignore_off_page ? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) + : GC_MALLOC_ATOMIC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; } #endif @@ -288,7 +299,10 @@ public: // GC_n is permitted to be 0. The C++ standard says nothing about what // the return value is when GC_n == 0. GC_Tp* allocate(size_type GC_n, const void* = 0) { - return static_cast(GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp))); + void * obj = GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return static_cast(obj); } // __p is not permitted to be a null pointer. diff --git a/include/new_gc_alloc.h b/include/new_gc_alloc.h index 5d459ee4..a42733d7 100644 --- a/include/new_gc_alloc.h +++ b/include/new_gc_alloc.h @@ -78,6 +78,8 @@ #define GC_generic_malloc_words_small(lw, k) \ GC_generic_malloc((lw) * sizeof(GC_word), k) +#define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() + // Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and // AUNCOLLECTABLE in gc_priv.h. @@ -149,7 +151,8 @@ template void * GC_aux_template::GC_out_of_line_malloc(size_t nwords, int kind) { void * op = GC_generic_malloc_words_small(nwords, kind); - if (!op) return 0; + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); GC_bytes_recently_allocd += GC_uncollectable_bytes_recently_allocd; GC_non_gc_bytes += @@ -183,7 +186,12 @@ class single_client_gc_alloc_template { void ** flh; void * op; - if (n > GC_max_fast_bytes) return GC_malloc(n); + if (n > GC_max_fast_bytes) { + op = GC_malloc(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } flh = &GC_objfreelist_ptr[nwords]; op = *flh; if (0 == op) { @@ -199,7 +207,12 @@ class single_client_gc_alloc_template { void ** flh; void * op; - if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + if (n > GC_max_fast_bytes) { + op = GC_malloc_atomic(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } flh = &GC_aobjfreelist_ptr[nwords]; op = *flh; if (0 == op) { @@ -251,7 +264,12 @@ class single_client_traceable_alloc_template { void ** flh; void * op; - if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + if (n > GC_max_fast_bytes) { + op = GC_malloc_uncollectable(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } flh = &GC_uobjfreelist_ptr[nwords]; op = *flh; if (0 == op) { @@ -268,7 +286,12 @@ class single_client_traceable_alloc_template { void ** flh; void * op; - if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + if (n > GC_max_fast_bytes) { + op = GC_malloc_atomic_uncollectable(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } flh = &GC_auobjfreelist_ptr[nwords]; op = *flh; if (0 == op) { @@ -314,9 +337,18 @@ typedef single_client_traceable_alloc_template<0> single_client_traceable_alloc; template < int dummy > class gc_alloc_template { public: - static void * allocate(size_t n) { return GC_malloc(n); } - static void * ptr_free_allocate(size_t n) - { return GC_malloc_atomic(n); } + static void * allocate(size_t n) { + void * op = GC_malloc(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } + static void * ptr_free_allocate(size_t n) { + void * op = GC_malloc_atomic(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } static void deallocate(void *, size_t) { } static void ptr_free_deallocate(void *, size_t) { } }; @@ -326,9 +358,18 @@ typedef gc_alloc_template < 0 > gc_alloc; template < int dummy > class traceable_alloc_template { public: - static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } - static void * ptr_free_allocate(size_t n) - { return GC_malloc_atomic_uncollectable(n); } + static void * allocate(size_t n) { + void * op = GC_malloc_uncollectable(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } + static void * ptr_free_allocate(size_t n) { + void * op = GC_malloc_atomic_uncollectable(n); + if (0 == op) + GC_ALLOCATOR_THROW_OR_ABORT(); + return op; + } static void deallocate(void *p, size_t) { GC_free(p); } static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } }; @@ -345,12 +386,12 @@ typedef traceable_alloc_template < 0 > traceable_alloc; class simple_alloc { \ public: \ static T *allocate(size_t n) \ - { return 0 == n? 0 : \ - reinterpret_cast(alloc::ptr_free_allocate(n * sizeof(T))); } \ + { reinterpret_cast(alloc::ptr_free_allocate(0 == n ? 1 \ + : n * sizeof(T))); } \ static T *allocate(void) \ { return reinterpret_cast(alloc::ptr_free_allocate(sizeof(T))); } \ static void deallocate(T *p, size_t n) \ - { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof(T)); } \ + { alloc::ptr_free_deallocate(p, 0 == n ? 1 : n * sizeof(T)); } \ static void deallocate(T *p) \ { alloc::ptr_free_deallocate(p, sizeof(T)); } \ };