diff --git a/common.mk b/common.mk
index ff56a0c93f..236d40ed04 100644
--- a/common.mk
+++ b/common.mk
@@ -1942,6 +1942,7 @@ error.$(OBJEXT): $(CCAN_DIR)/list/list.h
 error.$(OBJEXT): $(CCAN_DIR)/str/str.h
 error.$(OBJEXT): $(hdrdir)/ruby.h
 error.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+error.$(OBJEXT): $(hdrdir)/ruby/thread.h
 error.$(OBJEXT): {$(VPATH)}assert.h
 error.$(OBJEXT): {$(VPATH)}config.h
 error.$(OBJEXT): {$(VPATH)}defines.h
diff --git a/error.c b/error.c
index e29910ae92..3a63a5f2a9 100644
--- a/error.c
+++ b/error.c
@@ -11,6 +11,7 @@
 
 #include "ruby/encoding.h"
 #include "ruby/st.h"
+#include "ruby/thread.h"
 #include "internal.h"
 #include "ruby_assert.h"
 #include "vm_core.h"
@@ -2604,16 +2605,46 @@ rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
     rb_exc_raise(rb_exc_new3(exc, mesg));
 }
 
+struct rb_raise_tag {
+    VALUE exc;
+    const char *fmt;
+    va_list *args;
+};
+
+static void *
+rb_vraise(void *ptr)
+{
+    struct rb_raise_tag *argv = ptr;
+    VALUE msg = rb_vsprintf(argv->fmt, *argv->args);
+    VALUE exc = rb_exc_new3(argv->exc, msg);
+    rb_exc_raise(exc);
+    UNREACHABLE_RETURN(NULL);
+}
+
 void
 rb_raise(VALUE exc, const char *fmt, ...)
 {
     va_list args;
-    VALUE mesg;
-
     va_start(args, fmt);
-    mesg = rb_vsprintf(fmt, args);
+    struct rb_raise_tag argv = {
+        exc, fmt, &args,
+    };
+
+    if (ruby_thread_has_gvl_p()) {
+        rb_vraise(&argv);
+        UNREACHABLE;
+    }
+    else if (ruby_native_thread_p()) {
+        rb_thread_call_with_gvl(rb_vraise, &argv);
+        UNREACHABLE;
+    }
+    else {
+        /* Not in a ruby thread */
+        vfprintf(stderr, fmt, args);
+        abort();
+    }
+
     va_end(args);
-    rb_exc_raise(rb_exc_new3(exc, mesg));
 }
 
 NORETURN(static void raise_loaderror(VALUE path, VALUE mesg));
diff --git a/gc.c b/gc.c
index f4292370a0..cc4ea12250 100644
--- a/gc.c
+++ b/gc.c
@@ -172,6 +172,9 @@ size_mul_or_raise(size_t x, size_t y, VALUE exc)
     if (LIKELY(!t.left)) {
         return t.right;
     }
+    else if (rb_during_gc()) {
+        rb_memerror();          /* or...? */
+    }
     else {
         rb_raise(
             exc,
@@ -195,6 +198,9 @@ size_mul_add_or_raise(size_t x, size_t y, size_t z, VALUE exc)
     if (LIKELY(!t.left)) {
         return t.right;
     }
+    else if (rb_during_gc()) {
+        rb_memerror();          /* or...? */
+    }
     else {
         rb_raise(
             exc,
@@ -219,6 +225,9 @@ size_mul_add_mul_or_raise(size_t x, size_t y, size_t z, size_t w, VALUE exc)
     if (LIKELY(!t.left)) {
         return t.right;
     }
+    else if (rb_during_gc()) {
+        rb_memerror();          /* or...? */
+    }
     else {
         rb_raise(
             exc,
@@ -9590,28 +9599,10 @@ objspace_reachable_objects_from_root(rb_objspace_t *objspace, void (func)(const
 
 static void objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t size);
 
-static void *
-negative_size_allocation_error_with_gvl(void *ptr)
-{
-    rb_raise(rb_eNoMemError, "%s", (const char *)ptr);
-    return 0; /* should not be reached */
-}
-
 static void
 negative_size_allocation_error(const char *msg)
 {
-    if (ruby_thread_has_gvl_p()) {
-	rb_raise(rb_eNoMemError, "%s", msg);
-    }
-    else {
-	if (ruby_native_thread_p()) {
-	    rb_thread_call_with_gvl(negative_size_allocation_error_with_gvl, (void *)msg);
-	}
-	else {
-	    fprintf(stderr, "[FATAL] %s\n", msg);
-	    exit(EXIT_FAILURE);
-	}
-    }
+    rb_raise(rb_eNoMemError, "%s", msg);
 }
 
 static void *