ObjectSpace.{dump,dump_all,dump_shapes} needs vm barrier. (#15569)

This allows these methods to be called from ractors.

Add new exported function `rb_vm_lock_with_barrier()` that requires
users to include "vm_sync.h"
This commit is contained in:
Luke Gruber 2025-12-17 12:17:30 -05:00 committed by GitHub
parent 41e24aeb1a
commit 56b67f1684
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2025-12-17 17:17:58 +00:00
Merged-By: luke-gru <luke.gru@gmail.com>
4 changed files with 85 additions and 7 deletions

View File

@ -29,7 +29,7 @@
#include "ruby/util.h"
#include "ruby/io.h"
#include "vm_callinfo.h"
#include "vm_core.h"
#include "vm_sync.h"
RUBY_EXTERN const char ruby_hexdigits[];
@ -771,15 +771,16 @@ dump_result(struct dump_config *dc)
return dc->given_output;
}
/* :nodoc: */
static VALUE
objspace_dump(VALUE os, VALUE obj, VALUE output)
dump_locked(void *args_p)
{
struct dump_config dc = {0,};
VALUE obj = ((VALUE*)args_p)[0];
VALUE output = ((VALUE*)args_p)[1];
if (!RB_SPECIAL_CONST_P(obj)) {
dc.cur_page_slot_size = rb_gc_obj_slot_size(obj);
}
dump_output(&dc, output, Qnil, Qnil, Qnil);
dump_object(obj, &dc);
@ -787,6 +788,16 @@ objspace_dump(VALUE os, VALUE obj, VALUE output)
return dump_result(&dc);
}
/* :nodoc: */
static VALUE
objspace_dump(VALUE os, VALUE obj, VALUE output)
{
VALUE args[2];
args[0] = obj;
args[1] = output;
return rb_vm_lock_with_barrier(dump_locked, (void*)args);
}
static void
shape_id_i(shape_id_t shape_id, void *data)
{
@ -835,11 +846,15 @@ shape_id_i(shape_id_t shape_id, void *data)
dump_append(dc, "}\n");
}
/* :nodoc: */
static VALUE
objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes)
dump_all_locked(void *args_p)
{
struct dump_config dc = {0,};
VALUE output = ((VALUE*)args_p)[0];
VALUE full = ((VALUE*)args_p)[1];
VALUE since = ((VALUE*)args_p)[2];
VALUE shapes = ((VALUE*)args_p)[3];
dump_output(&dc, output, full, since, shapes);
if (!dc.partial_dump || dc.since == 0) {
@ -860,9 +875,23 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes)
/* :nodoc: */
static VALUE
objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes)
objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
VALUE args[4];
args[0] = output;
args[1] = full;
args[2] = since;
args[3] = shapes;
return rb_vm_lock_with_barrier(dump_all_locked, (void*)args);
}
static VALUE
dump_shapes_locked(void *args_p)
{
struct dump_config dc = {0,};
VALUE output = ((VALUE*)args_p)[0];
VALUE shapes = ((VALUE*)args_p)[1];
dump_output(&dc, output, Qfalse, Qnil, shapes);
if (RTEST(shapes)) {
@ -871,6 +900,16 @@ objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes)
return dump_result(&dc);
}
/* :nodoc: */
static VALUE
objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes)
{
VALUE args[2];
args[0] = output;
args[1] = shapes;
return rb_vm_lock_with_barrier(dump_shapes_locked, (void*)args);
}
void
Init_objspace_dump(VALUE rb_mObjSpace)
{
@ -878,6 +917,9 @@ Init_objspace_dump(VALUE rb_mObjSpace)
#if 0
rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
#endif
#ifdef HAVE_RB_EXT_RACTOR_SAFE
RB_EXT_RACTOR_SAFE(true);
#endif
rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2);
rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4);

View File

@ -827,6 +827,27 @@ class TestObjSpace < Test::Unit::TestCase
end
end
def test_dump_all_with_ractors
assert_ractor("#{<<-"begin;"}#{<<-'end;'}")
begin;
require "objspace"
require "tempfile"
require "json"
rs = 4.times.map do
Ractor.new do
Tempfile.create do |f|
ObjectSpace.dump_all(output: f)
f.close
File.readlines(f.path).each do |line|
JSON.parse(line)
end
end
end
end
rs.each(&:join)
end;
end
def test_dump_uninitialized_file
assert_in_out_err(%[-robjspace], <<-RUBY) do |(output), (error)|
puts ObjectSpace.dump(File.allocate)

View File

@ -297,3 +297,14 @@ rb_ec_vm_lock_rec_release(const rb_execution_context_t *ec,
VM_ASSERT(recorded_lock_rec == rb_ec_vm_lock_rec(ec));
}
VALUE
rb_vm_lock_with_barrier(VALUE (*func)(void *args), void *args)
{
VALUE result = 0;
RB_VM_LOCKING() {
rb_vm_barrier();
result = func(args);
}
return result;
}

View File

@ -28,6 +28,10 @@ void rb_vm_lock_leave_body_nb(unsigned int *lev APPEND_LOCATION_ARGS);
void rb_vm_lock_leave_body(unsigned int *lev APPEND_LOCATION_ARGS);
void rb_vm_barrier(void);
RUBY_SYMBOL_EXPORT_BEGIN
VALUE rb_vm_lock_with_barrier(VALUE (*func)(void *args), void *args);
RUBY_SYMBOL_EXPORT_END
#if RUBY_DEBUG
// GET_VM()
#include "vm_core.h"