Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 106 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions bootstraptest/test_ractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2372,3 +2372,26 @@ def call_test(obj)
ractors.each(&:join)
:ok
RUBY

# This test checks that we do not trigger a GC when we have malloc with Ractor
# locks. We cannot trigger a GC with Ractor locks because GC requires VM lock
# and Ractor barrier. If another Ractor is waiting on this Ractor lock, then it
# will deadlock because the other Ractor will never join the barrier.
#
# Creating Ractor::Port requires locking the Ractor and inserting into an
# st_table, which can call malloc.
assert_equal 'ok', <<~'RUBY'
r = Ractor.new do
loop do
Ractor::Port.new
end
end

10.times do
10_000.times do
r.send(nil)
end
sleep(0.01)
end
:ok
RUBY
24 changes: 16 additions & 8 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,9 +625,9 @@ typedef struct gc_function_map {
size_t (*heap_id_for_size)(void *objspace_ptr, size_t size);
bool (*size_allocatable_p)(size_t size);
// Malloc
void *(*malloc)(void *objspace_ptr, size_t size);
void *(*calloc)(void *objspace_ptr, size_t size);
void *(*realloc)(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size);
void *(*malloc)(void *objspace_ptr, size_t size, bool gc_allowed);
void *(*calloc)(void *objspace_ptr, size_t size, bool gc_allowed);
void *(*realloc)(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size, bool gc_allowed);
void (*free)(void *objspace_ptr, void *ptr, size_t old_size);
void (*adjust_memory_usage)(void *objspace_ptr, ssize_t diff);
// Marking
Expand Down Expand Up @@ -5118,14 +5118,22 @@ ruby_xmalloc(size_t size)
return handle_malloc_failure(ruby_xmalloc_body(size));
}

static bool
malloc_gc_allowed(void)
{
rb_ractor_t *r = rb_current_ractor_raw(false);

return r == NULL || !r->malloc_gc_disabled;
}

static void *
ruby_xmalloc_body(size_t size)
{
if ((ssize_t)size < 0) {
negative_size_allocation_error("too large allocation size");
}

return rb_gc_impl_malloc(rb_gc_get_objspace(), size);
return rb_gc_impl_malloc(rb_gc_get_objspace(), size, malloc_gc_allowed());
}

void
Expand Down Expand Up @@ -5155,7 +5163,7 @@ ruby_xmalloc2(size_t n, size_t size)
static void *
ruby_xmalloc2_body(size_t n, size_t size)
{
return rb_gc_impl_malloc(rb_gc_get_objspace(), xmalloc2_size(n, size));
return rb_gc_impl_malloc(rb_gc_get_objspace(), xmalloc2_size(n, size), malloc_gc_allowed());
}

static void *ruby_xcalloc_body(size_t n, size_t size);
Expand All @@ -5169,7 +5177,7 @@ ruby_xcalloc(size_t n, size_t size)
static void *
ruby_xcalloc_body(size_t n, size_t size)
{
return rb_gc_impl_calloc(rb_gc_get_objspace(), xmalloc2_size(n, size));
return rb_gc_impl_calloc(rb_gc_get_objspace(), xmalloc2_size(n, size), malloc_gc_allowed());
}

static void *ruby_sized_xrealloc_body(void *ptr, size_t new_size, size_t old_size);
Expand All @@ -5190,7 +5198,7 @@ ruby_sized_xrealloc_body(void *ptr, size_t new_size, size_t old_size)
negative_size_allocation_error("too large allocation size");
}

return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, new_size, old_size);
return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, new_size, old_size, malloc_gc_allowed());
}

void *
Expand All @@ -5214,7 +5222,7 @@ static void *
ruby_sized_xrealloc2_body(void *ptr, size_t n, size_t size, size_t old_n)
{
size_t len = xmalloc2_size(n, size);
return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, len, old_n * size);
return rb_gc_impl_realloc(rb_gc_get_objspace(), ptr, len, old_n * size, malloc_gc_allowed());
}

void *
Expand Down
36 changes: 18 additions & 18 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -7970,7 +7970,7 @@ objspace_malloc_gc_stress(rb_objspace_t *objspace)
}

static inline bool
objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type)
objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type, bool gc_allowed)
{
if (0) fprintf(stderr, "increase - ptr: %p, type: %s, new_size: %"PRIdSIZE", old_size: %"PRIdSIZE"\n",
mem,
Expand All @@ -7982,7 +7982,7 @@ objspace_malloc_increase_report(rb_objspace_t *objspace, void *mem, size_t new_s
}

static bool
objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type)
objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type, bool gc_allowed)
{
if (new_size > old_size) {
RUBY_ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size);
Expand All @@ -7997,7 +7997,7 @@ objspace_malloc_increase_body(rb_objspace_t *objspace, void *mem, size_t new_siz
#endif
}

if (type == MEMOP_TYPE_MALLOC) {
if (type == MEMOP_TYPE_MALLOC && gc_allowed) {
retry:
if (malloc_increase > malloc_limit && ruby_native_thread_p() && !dont_gc_val()) {
if (ruby_thread_has_gvl_p() && is_lazy_sweeping(objspace)) {
Expand Down Expand Up @@ -8079,10 +8079,10 @@ malloc_during_gc_p(rb_objspace_t *objspace)
}

static inline void *
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size, bool gc_allowed)
{
size = objspace_malloc_size(objspace, mem, size);
objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC) {}
objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC, gc_allowed) {}

#if CALC_EXACT_MALLOC_SIZE
{
Expand Down Expand Up @@ -8114,10 +8114,10 @@ objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
GPR_FLAG_MALLOC; \
objspace_malloc_gc_stress(objspace); \
\
if (RB_LIKELY((expr))) { \
if (RB_LIKELY((expr))) { \
/* Success on 1st try */ \
} \
else if (!garbage_collect_with_gvl(objspace, gpr)) { \
else if (gc_allowed && !garbage_collect_with_gvl(objspace, gpr)) { \
/* @shyouhei thinks this doesn't happen */ \
GC_MEMERROR("TRY_WITH_GC: could not GC"); \
} \
Expand Down Expand Up @@ -8160,15 +8160,15 @@ rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size)
#endif
old_size = objspace_malloc_size(objspace, ptr, old_size);

objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE) {
objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE, true) {
free(ptr);
ptr = NULL;
RB_DEBUG_COUNTER_INC(heap_xfree);
}
}

void *
rb_gc_impl_malloc(void *objspace_ptr, size_t size)
rb_gc_impl_malloc(void *objspace_ptr, size_t size, bool gc_allowed)
{
rb_objspace_t *objspace = objspace_ptr;
check_malloc_not_in_gc(objspace, "malloc");
Expand All @@ -8179,11 +8179,11 @@ rb_gc_impl_malloc(void *objspace_ptr, size_t size)
TRY_WITH_GC(size, mem = malloc(size));
RB_DEBUG_COUNTER_INC(heap_xmalloc);
if (!mem) return mem;
return objspace_malloc_fixup(objspace, mem, size);
return objspace_malloc_fixup(objspace, mem, size, gc_allowed);
}

void *
rb_gc_impl_calloc(void *objspace_ptr, size_t size)
rb_gc_impl_calloc(void *objspace_ptr, size_t size, bool gc_allowed)
{
rb_objspace_t *objspace = objspace_ptr;

Expand All @@ -8199,27 +8199,27 @@ rb_gc_impl_calloc(void *objspace_ptr, size_t size)
size = objspace_malloc_prepare(objspace, size);
TRY_WITH_GC(size, mem = calloc1(size));
if (!mem) return mem;
return objspace_malloc_fixup(objspace, mem, size);
return objspace_malloc_fixup(objspace, mem, size, gc_allowed);
}

void *
rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size)
rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size, bool gc_allowed)
{
rb_objspace_t *objspace = objspace_ptr;

check_malloc_not_in_gc(objspace, "realloc");

void *mem;

if (!ptr) return rb_gc_impl_malloc(objspace, new_size);
if (!ptr) return rb_gc_impl_malloc(objspace, new_size, gc_allowed);

/*
* The behavior of realloc(ptr, 0) is implementation defined.
* Therefore we don't use realloc(ptr, 0) for portability reason.
* see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm
*/
if (new_size == 0) {
if ((mem = rb_gc_impl_malloc(objspace, 0)) != NULL) {
if ((mem = rb_gc_impl_malloc(objspace, 0, gc_allowed)) != NULL) {
/*
* - OpenBSD's malloc(3) man page says that when 0 is passed, it
* returns a non-NULL pointer to an access-protected memory page.
Expand Down Expand Up @@ -8278,7 +8278,7 @@ rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_si
}
#endif

objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC);
objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC, gc_allowed);

RB_DEBUG_COUNTER_INC(heap_xrealloc);
return mem;
Expand All @@ -8290,10 +8290,10 @@ rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff)
rb_objspace_t *objspace = objspace_ptr;

if (diff > 0) {
objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC);
objspace_malloc_increase(objspace, 0, diff, 0, MEMOP_TYPE_REALLOC, true);
}
else if (diff < 0) {
objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC);
objspace_malloc_increase(objspace, 0, 0, -diff, MEMOP_TYPE_REALLOC, true);
}
}

Expand Down
6 changes: 3 additions & 3 deletions gc/gc_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size);
* memory just return NULL (with appropriate errno set).
* The caller side takes care of that situation.
*/
GC_IMPL_FN void *rb_gc_impl_malloc(void *objspace_ptr, size_t size);
GC_IMPL_FN void *rb_gc_impl_calloc(void *objspace_ptr, size_t size);
GC_IMPL_FN void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size);
GC_IMPL_FN void *rb_gc_impl_malloc(void *objspace_ptr, size_t size, bool gc_allowed);
GC_IMPL_FN void *rb_gc_impl_calloc(void *objspace_ptr, size_t size, bool gc_allowed);
GC_IMPL_FN void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size, bool gc_allowed);
GC_IMPL_FN void rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size);
GC_IMPL_FN void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff);
// Marking
Expand Down
Loading