Skip to content

Commit 25ad7e8

Browse files
committed
rb_gc_impl_malloc can return NULL
Let there be rooms for each GC implementations how to handle multi threaded situations. They can be totally reentrant, or can have their own mutex, or can rely on rb_thread_call_with_gvl. In any ways the allocator (has been, but now officially is) expected to run properly without a GVL. This means there need be a way for them to inform the interpreter about their allocation failures, without relying on raising exceptions. Let them do so by returning NULL.
1 parent 5067a46 commit 25ad7e8

File tree

3 files changed

+90
-15
lines changed

3 files changed

+90
-15
lines changed

gc.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4398,12 +4398,20 @@ ruby_memerror(void)
43984398
fprintf(stderr, "[FATAL] failed to allocate memory\n");
43994399
}
44004400
}
4401+
4402+
/* We have discussions whether we should die here; */
4403+
/* We might rethink about it later. */
44014404
exit(EXIT_FAILURE);
44024405
}
44034406

44044407
void
44054408
rb_memerror(void)
44064409
{
4410+
/* the `GET_VM()->special_exceptions` below assumes that
4411+
* the VM is reachable from the current thread. We should
4412+
* definitely make sure of that. */
4413+
RUBY_ASSERT_ALWAYS(ruby_thread_has_gvl_p());
4414+
44074415
rb_execution_context_t *ec = GET_EC();
44084416
VALUE exc = GET_VM()->special_exceptions[ruby_error_nomemory];
44094417

@@ -4428,8 +4436,27 @@ rb_malloc_info_show_results(void)
44284436
{
44294437
}
44304438

4439+
static void *
4440+
handle_malloc_failure(void *ptr)
4441+
{
4442+
if (LIKELY(ptr)) {
4443+
return ptr;
4444+
}
4445+
else {
4446+
return ruby_memerror_body(ptr);
4447+
}
4448+
}
4449+
4450+
static void *ruby_xmalloc_body(size_t size);
4451+
44314452
void *
44324453
ruby_xmalloc(size_t size)
4454+
{
4455+
return handle_malloc_failure(ruby_xmalloc_body(size));
4456+
}
4457+
4458+
static void *
4459+
ruby_xmalloc_body(size_t size)
44334460
{
44344461
if ((ssize_t)size < 0) {
44354462
negative_size_allocation_error("too large allocation size");
@@ -4446,23 +4473,47 @@ ruby_malloc_size_overflow(size_t count, size_t elsize)
44464473
count, elsize);
44474474
}
44484475

4476+
static void *ruby_xmalloc2_body(size_t n, size_t size);
4477+
44494478
void *
44504479
ruby_xmalloc2(size_t n, size_t size)
4480+
{
4481+
return handle_malloc_failure(ruby_xmalloc2_body(n, size));
4482+
}
4483+
4484+
static void *
4485+
ruby_xmalloc2_body(size_t n, size_t size)
44514486
{
44524487
return rb_gc_impl_malloc(rb_gc_get_objspace(), xmalloc2_size(n, size));
44534488
}
44544489

4490+
static void *ruby_xcalloc_body(size_t n, size_t size);
4491+
44554492
void *
44564493
ruby_xcalloc(size_t n, size_t size)
4494+
{
4495+
return handle_malloc_failure(ruby_xcalloc_body(n, size));
4496+
}
4497+
4498+
static void *
4499+
ruby_xcalloc_body(size_t n, size_t size)
44574500
{
44584501
return rb_gc_impl_calloc(rb_gc_get_objspace(), xmalloc2_size(n, size));
44594502
}
44604503

4504+
static void *ruby_sized_xrealloc_body(void *ptr, size_t new_size, size_t old_size);
4505+
44614506
#ifdef ruby_sized_xrealloc
44624507
#undef ruby_sized_xrealloc
44634508
#endif
44644509
void *
44654510
ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size)
4511+
{
4512+
return handle_malloc_failure(ruby_sized_xrealloc_body(ptr, new_size, old_size));
4513+
}
4514+
4515+
static void *
4516+
ruby_sized_xrealloc_body(void *ptr, size_t new_size, size_t old_size)
44664517
{
44674518
if ((ssize_t)new_size < 0) {
44684519
negative_size_allocation_error("too large allocation size");
@@ -4477,11 +4528,19 @@ ruby_xrealloc(void *ptr, size_t new_size)
44774528
return ruby_sized_xrealloc(ptr, new_size, 0);
44784529
}
44794530

4531+
static void *ruby_sized_xrealloc2_body(void *ptr, size_t n, size_t size, size_t old_n);
4532+
44804533
#ifdef ruby_sized_xrealloc2
44814534
#undef ruby_sized_xrealloc2
44824535
#endif
44834536
void *
44844537
ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n)
4538+
{
4539+
return handle_malloc_failure(ruby_sized_xrealloc2_body(ptr, n, size, old_n));
4540+
}
4541+
4542+
static void *
4543+
ruby_sized_xrealloc2_body(void *ptr, size_t n, size_t size, size_t old_n)
44854544
{
44864545
size_t len = xmalloc2_size(n, size);
44874546
return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, len, old_n * size);

gc/default/default.c

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6746,22 +6746,23 @@ int ruby_thread_has_gvl_p(void);
67466746
static int
67476747
garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason)
67486748
{
6749-
if (dont_gc_val()) return TRUE;
6750-
if (ruby_thread_has_gvl_p()) {
6751-
return garbage_collect(objspace, reason);
6749+
if (dont_gc_val()) {
6750+
return TRUE;
6751+
}
6752+
else if (!ruby_native_thread_p()) {
6753+
return TRUE;
6754+
}
6755+
else if (!ruby_thread_has_gvl_p()) {
6756+
void *ret;
6757+
struct objspace_and_reason oar;
6758+
oar.objspace = objspace;
6759+
oar.reason = reason;
6760+
ret = rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
6761+
6762+
return !!ret;
67526763
}
67536764
else {
6754-
if (ruby_native_thread_p()) {
6755-
struct objspace_and_reason oar;
6756-
oar.objspace = objspace;
6757-
oar.reason = reason;
6758-
return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
6759-
}
6760-
else {
6761-
/* no ruby thread */
6762-
fprintf(stderr, "[FATAL] failed to allocate memory\n");
6763-
exit(EXIT_FAILURE);
6764-
}
6765+
return garbage_collect(objspace, reason);
67656766
}
67666767
}
67676768

@@ -8123,7 +8124,7 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
81238124
#endif
81248125

81258126
#define GC_MEMERROR(...) \
8126-
((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : rb_memerror())
8127+
((RB_BUG_INSTEAD_OF_RB_MEMERROR+0) ? rb_bug("" __VA_ARGS__) : (void)0)
81278128

81288129
#define TRY_WITH_GC(siz, expr) do { \
81298130
const gc_profile_record_flag gpr = \
@@ -8197,6 +8198,7 @@ rb_gc_impl_malloc(void *objspace_ptr, size_t size)
81978198
size = objspace_malloc_prepare(objspace, size);
81988199
TRY_WITH_GC(size, mem = malloc(size));
81998200
RB_DEBUG_COUNTER_INC(heap_xmalloc);
8201+
if (!mem) return mem;
82008202
return objspace_malloc_fixup(objspace, mem, size);
82018203
}
82028204

@@ -8216,6 +8218,7 @@ rb_gc_impl_calloc(void *objspace_ptr, size_t size)
82168218

82178219
size = objspace_malloc_prepare(objspace, size);
82188220
TRY_WITH_GC(size, mem = calloc1(size));
8221+
if (!mem) return mem;
82198222
return objspace_malloc_fixup(objspace, mem, size);
82208223
}
82218224

@@ -8284,6 +8287,7 @@ rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_si
82848287

82858288
old_size = objspace_malloc_size(objspace, ptr, old_size);
82868289
TRY_WITH_GC(new_size, mem = RB_GNUC_EXTENSION_BLOCK(realloc(ptr, new_size)));
8290+
if (!mem) return mem;
82878291
new_size = objspace_malloc_size(objspace, mem, new_size);
82888292

82898293
#if CALC_EXACT_MALLOC_SIZE

gc/gc_impl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj);
5252
GC_IMPL_FN size_t rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size);
5353
GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size);
5454
// Malloc
55+
/*
56+
* BEWARE: These functions may or may not run under GVL.
57+
*
58+
* You might want to make them thread-safe.
59+
* Garbage collecting inside is possible if and only if you
60+
* already have GVL. Also raising exceptions without one is a
61+
* total disaster.
62+
*
63+
* When you absolutely cannot allocate the requested amount of
64+
* memory just return NULL (with appropriate errno set).
65+
* The caller side takes care of that situation.
66+
*/
5567
GC_IMPL_FN void *rb_gc_impl_malloc(void *objspace_ptr, size_t size);
5668
GC_IMPL_FN void *rb_gc_impl_calloc(void *objspace_ptr, size_t size);
5769
GC_IMPL_FN void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size);

0 commit comments

Comments
 (0)