diff --git a/internal/marshal.h b/internal/marshal.h new file mode 100644 index 00000000000..31b1bb3e21a --- /dev/null +++ b/internal/marshal.h @@ -0,0 +1,17 @@ +#ifndef INTERNAL_MARSHAL_H /*-*-C-*-vi:se ft=c:*/ +#define INTERNAL_MARSHAL_H +/** + * @author Ruby developers + * @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 */ diff --git a/marshal.c b/marshal.c index ec081e78b9e..b992b98cb3b 100644 --- a/marshal.c +++ b/marshal.c @@ -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; diff --git a/ractor.c b/ractor.c index 20e206de0a4..6c0cecb1b73 100644 --- a/ractor.c +++ b/ractor.c @@ -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"); } diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index c1779839c5a..81b40ca2a6d 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -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")