Never return null by C++ GC allocators
authorIvan Maidanski <ivmai@mail.ru>
Wed, 6 Jun 2018 22:08:07 +0000 (01:08 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Wed, 6 Jun 2018 22:08:07 +0000 (01:08 +0300)
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.

include/gc_allocator.h
include/new_gc_alloc.h

index 7d1d186..6aa8664 100644 (file)
  */
 
 #ifndef GC_ALLOCATOR_H
-
 #define GC_ALLOCATOR_H
 
 #include "gc.h"
-#include <new> // for placement new
+#include <new> // for placement new and bad_alloc
 
 #ifndef GC_ATTR_EXPLICIT
 # if (__cplusplus >= 201103L) || defined(CPPCHECK)
 # 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 <class GC_Tp>
 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<GC_true_type>(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_Tp*>(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<GC_Tp*>(obj);
   }
 
   // __p is not permitted to be a null pointer.
index 5d459ee..a42733d 100644 (file)
@@ -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 <int dummy>
 void * GC_aux_template<dummy>::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<T, alloc> { \
   public: \
     static T *allocate(size_t n) \
-        { return 0 == n? 0 : \
-            reinterpret_cast<T*>(alloc::ptr_free_allocate(n * sizeof(T))); } \
+        { reinterpret_cast<T*>(alloc::ptr_free_allocate(0 == n ? 1 \
+                                                    : n * sizeof(T))); } \
     static T *allocate(void) \
         { return reinterpret_cast<T*>(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)); } \
   };