Use Marshal compat from Ractor courier

For some types (probably most notably Set), Marshal loading is defined
with compatibility functions. We should use those when set.
This commit is contained in:
John Hawthorn committed 2026-09-17 09:33:28 -07:00
1 parent 742699d231
commit 1bd863dd23
4 files changed
+57
Notes: git 2026-09-17 16:34:12 +00:00

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
#ifndef INTERNAL_MARSHAL_H /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_MARSHAL_H
/**
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Internal header for Marshal.
*/
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for VALUE */
/* marshal.c */
bool rb_marshal_compat_lookup(VALUE klass, VALUE (**dumper)(VALUE), VALUE (**loader)(VALUE, VALUE));
#endif /* INTERNAL_MARSHAL_H */
+16
View File
@@ -28,6 +28,7 @@
#include "internal/encoding.h"
#include "internal/error.h"
#include "internal/hash.h"
#include "internal/marshal.h"
#include "internal/numeric.h"
#include "internal/object.h"
#include "internal/re.h"
@@ -155,6 +156,21 @@ rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE (*dumper)(VALUE),
RB_OBJ_WRITTEN(compat_allocator_tbl_wrapper, Qundef, oldclass);
}
/* The rb_marshal_define_compat entry for an instance of klass, if any, so the Ractor
* courier can dump and load it the way Marshal does. */
bool
rb_marshal_compat_lookup(VALUE klass, VALUE (**dumper)(VALUE), VALUE (**loader)(VALUE, VALUE))
{
st_data_t data;
rb_alloc_func_t allocator = RCLASS_SINGLETON_P(klass) ? 0 : rb_get_alloc_func(klass);
if (!allocator || !st_lookup(compat_allocator_tbl, (st_data_t)allocator, &data)) return false;
marshal_compat_t *compat = (marshal_compat_t *)data;
if (!compat->dumper || !compat->loader) return false;
if (dumper) *dumper = compat->dumper;
if (loader) *loader = compat->loader;
return true;
}
struct dump_arg {
VALUE str, dest;
st_table *symbols;
+15
View File
@@ -21,6 +21,7 @@
#include "internal/variable.h"
#include "eval_intern.h"
#include "internal/io.h"
#include "internal/marshal.h"
#include "internal/ractor.h"
#include "internal/rational.h"
#include "internal/re.h"
@@ -2396,6 +2397,7 @@ enum courier_hook {
COURIER_HOOK_NONE,
COURIER_HOOK_MARSHAL_DUMP, /* marshal_dump -> alloc + marshal_load */
COURIER_HOOK_DUMP, /* _dump -> klass._load */
COURIER_HOOK_COMPAT, /* rb_marshal_define_compat dumper -> alloc + loader (Set, Process::Status) */
COURIER_HOOK_DUMP_DATA, /* _dump_data -> alloc + _load_data */
};
@@ -2405,6 +2407,7 @@ courier_hook_of(VALUE obj)
{
if (rb_obj_respond_to(obj, id_marshal_dump, TRUE)) return COURIER_HOOK_MARSHAL_DUMP;
if (rb_obj_respond_to(obj, id_dump, TRUE)) return COURIER_HOOK_DUMP;
if (rb_marshal_compat_lookup(CLASS_OF(obj), NULL, NULL)) return COURIER_HOOK_COMPAT;
if (BUILTIN_TYPE(obj) == T_DATA && rb_obj_respond_to(obj, id_dump_data, TRUE)) return COURIER_HOOK_DUMP_DATA;
return COURIER_HOOK_NONE;
}
@@ -2711,6 +2714,12 @@ courier_capture_hooked(struct courier_build *b, VALUE obj, uint32_t id, enum cou
case COURIER_HOOK_DUMP_DATA:
payload = rb_funcallv(obj, id_dump_data, 0, 0);
break;
case COURIER_HOOK_COMPAT: {
VALUE (*dumper)(VALUE);
rb_marshal_compat_lookup(klass, &dumper, NULL);
payload = dumper(obj);
break;
}
default:
rb_bug("courier_capture_hooked: no dump protocol");
}
@@ -3390,6 +3399,12 @@ rb_ractor_courier_materialize(struct rb_ractor_courier *c)
}
rb_funcallv(shell, mid, 1, &payload);
break;
case COURIER_HOOK_COMPAT: {
VALUE (*loader)(VALUE, VALUE);
rb_marshal_compat_lookup(klass, NULL, &loader);
loader(shell, payload);
break;
}
default:
rb_bug("rb_ractor_courier_materialize: no dump protocol");
}
+9
View File
@@ -271,6 +271,15 @@ class TestRactor < Test::Unit::TestCase
assert_copy [Time.now, Ractor::Port.new]
# Dump hooks run after the courier is sized, so enough of them make it grow.
assert_copy Array.new(2000) { |i| Time.at(i) }
# Set has no dump hook of its own; it goes through its rb_marshal_define_compat entry.
assert_copy Set.new
assert_copy Set[1,2,3]
assert_copy Set[+"a", [+"b"], {+"c" => Set[+"d"]}]
assert_copy Set[1].compare_by_identity
assert_copy Class.new(Set)[1, 2]
assert_equal true, echo(Set[1].compare_by_identity).compare_by_identity?
set = Set[Ractor::Port.new, Ractor::Port.new] # Marshal cannot carry a Port
assert_equal set.to_a, echo(set).to_a
# Time#_dump keeps these as ivars on the dumped string; Time#== ignores the last two.
time = Time.at(0, 123456789, :nsec, in: "+09:00")