summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-Andre Lafortune <github@marc-andre.ca>2020-12-20 21:42:25 -0500
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2020-12-23 18:36:08 +0900
commitd4861854ecde00540c527f36aaacd257e63ed5a4 (patch)
treee618e0a88000d4dac45983be2465e522d9e8d1c8
parent0eb2c0c9a2cb66da217b5157419af7b912acb947 (diff)
downloadpsych-d4861854ecde00540c527f36aaacd257e63ed5a4.zip
[ruby/psych] Make Ractor-ready.
Config is Ractor-local. Benchmarking reveals that using `Ractor.local_storage` for storing cache is similar to accessing a constant (~15% slower).
-rw-r--r--ext/psych/psych.c3
-rw-r--r--lib/psych.rb26
-rw-r--r--lib/psych/visitors/visitor.rb20
-rw-r--r--test/psych/test_ractor.rb47
4 files changed, 90 insertions, 6 deletions
diff --git a/ext/psych/psych.c b/ext/psych/psych.c
index 0afd731..8af0bb6 100644
--- a/ext/psych/psych.c
+++ b/ext/psych/psych.c
@@ -22,6 +22,9 @@ VALUE mPsych;
void Init_psych(void)
{
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
+ RB_EXT_RACTOR_SAFE(true);
+ #endif
mPsych = rb_define_module("Psych");
rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0);
diff --git a/lib/psych.rb b/lib/psych.rb
index 0b62362..cedf0a4 100644
--- a/lib/psych.rb
+++ b/lib/psych.rb
@@ -632,9 +632,29 @@ module Psych
private_class_method :warn_with_uplevel, :parse_caller
class << self
- attr_accessor :load_tags
- attr_accessor :dump_tags
- attr_accessor :domain_types
+ if defined?(Ractor)
+ require 'forwardable'
+ extend Forwardable
+
+ class Config
+ attr_accessor :load_tags, :dump_tags, :domain_types
+ def initialize
+ @load_tags = {}
+ @dump_tags = {}
+ @domain_types = {}
+ end
+ end
+
+ def config
+ Ractor.current[:PsychConfig] ||= Config.new
+ end
+
+ def_delegators :config, :load_tags, :dump_tags, :domain_types, :load_tags=, :dump_tags=, :domain_types=
+ else
+ attr_accessor :load_tags
+ attr_accessor :dump_tags
+ attr_accessor :domain_types
+ end
end
self.load_tags = {}
self.dump_tags = {}
diff --git a/lib/psych/visitors/visitor.rb b/lib/psych/visitors/visitor.rb
index 3f4ba64..35f8f81 100644
--- a/lib/psych/visitors/visitor.rb
+++ b/lib/psych/visitors/visitor.rb
@@ -8,12 +8,26 @@ module Psych
private
- DISPATCH = Hash.new do |hash, klass|
- hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
+ # @api private
+ def self.dispatch_cache
+ Hash.new do |hash, klass|
+ hash[klass] = :"visit_#{klass.name.gsub('::', '_')}"
+ end
+ end
+
+ if defined?(Ractor)
+ def dispatch
+ Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache
+ end
+ else
+ DISPATCH = dispatch_cache
+ def dispatch
+ DISPATCH
+ end
end
def visit target
- send DISPATCH[target.class], target
+ send dispatch[target.class], target
end
end
end
diff --git a/test/psych/test_ractor.rb b/test/psych/test_ractor.rb
new file mode 100644
index 0000000..90e4036
--- /dev/null
+++ b/test/psych/test_ractor.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+require_relative 'helper'
+
+class TestPsychRactor < Test::Unit::TestCase
+ def test_ractor_round_trip
+ assert_ractor(<<~RUBY, require_relative: 'helper')
+ obj = {foo: [42]}
+ obj2 = Ractor.new(obj) do |obj|
+ Psych.load(Psych.dump(obj))
+ end.take
+ assert_equal obj, obj2
+ RUBY
+ end
+
+ def test_not_shareable
+ # There's no point in making these frozen / shareable
+ # and the C-ext disregards begin frozen
+ assert_ractor(<<~RUBY, require_relative: 'helper')
+ parser = Psych::Parser.new
+ emitter = Psych::Emitter.new(nil)
+ assert_raise(Ractor::Error) { Ractor.make_shareable(parser) }
+ assert_raise(Ractor::Error) { Ractor.make_shareable(emitter) }
+ RUBY
+ end
+
+ def test_ractor_config
+ assert_ractor(<<~RUBY, require_relative: 'helper')
+ r = Ractor.new do
+ Psych.add_builtin_type 'omap' do |type, val|
+ val * 2
+ end
+ Psych.load('--- !!omap hello')
+ end.take
+ assert_equal 'hellohello', r
+ assert_equal 'hello', Psych.load('--- !!omap hello')
+ RUBY
+ end
+
+ def test_ractor_constants
+ assert_ractor(<<~RUBY, require_relative: 'helper')
+ r = Ractor.new do
+ Psych.libyaml_version.join('.') == Psych::LIBYAML_VERSION
+ end.take
+ assert_equal true, r
+ RUBY
+ end
+end