diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2020-12-23 19:08:52 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-23 19:08:52 +0900 |
commit | f150e592af1d77bf43a807fd6787764f1b5bd1a2 (patch) | |
tree | 0a39a33b9d00d11aa4964710739c5016c828e08c | |
parent | e2a628c7d3e5561940a3ac42239fff70f8e0cda5 (diff) | |
parent | 7da26358f18ec6d070d43a14b04a862517cc2f06 (diff) | |
download | psych-f150e592af1d77bf43a807fd6787764f1b5bd1a2.zip |
Merge pull request #471 from ruby/ractor
Ractor support
-rw-r--r-- | ext/psych/psych.c | 3 | ||||
-rw-r--r-- | lib/psych.rb | 48 | ||||
-rw-r--r-- | lib/psych/class_loader.rb | 10 | ||||
-rw-r--r-- | lib/psych/visitors/visitor.rb | 20 | ||||
-rw-r--r-- | lib/psych/visitors/yaml_tree.rb | 2 | ||||
-rw-r--r-- | test/psych/test_ractor.rb | 50 |
6 files changed, 111 insertions, 22 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 c3292d9..cedf0a4 100644 --- a/lib/psych.rb +++ b/lib/psych.rb @@ -233,9 +233,9 @@ require 'psych/class_loader' module Psych # The version of libyaml Psych is using - LIBYAML_VERSION = Psych.libyaml_version.join '.' + LIBYAML_VERSION = Psych.libyaml_version.join('.').freeze # Deprecation guard - NOT_GIVEN = Object.new + NOT_GIVEN = Object.new.freeze private_constant :NOT_GIVEN ### @@ -595,28 +595,25 @@ module Psych end # :stopdoc: - @domain_types = {} def self.add_domain_type domain, type_tag, &block key = ['tag', domain, type_tag].join ':' - @domain_types[key] = [key, block] - @domain_types["tag:#{type_tag}"] = [key, block] + domain_types[key] = [key, block] + domain_types["tag:#{type_tag}"] = [key, block] end def self.add_builtin_type type_tag, &block domain = 'yaml.org,2002' key = ['tag', domain, type_tag].join ':' - @domain_types[key] = [key, block] + domain_types[key] = [key, block] end def self.remove_type type_tag - @domain_types.delete type_tag + domain_types.delete type_tag end - @load_tags = {} - @dump_tags = {} def self.add_tag tag, klass - @load_tags[tag] = klass.name - @dump_tags[klass] = tag + load_tags[tag] = klass.name + dump_tags[klass] = tag end # Workaround for emulating `warn '...', uplevel: 1` in Ruby 2.4 or lower. @@ -635,9 +632,32 @@ 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 = {} + self.domain_types = {} # :startdoc: end diff --git a/lib/psych/class_loader.rb b/lib/psych/class_loader.rb index cfca868..a5d1a7a 100644 --- a/lib/psych/class_loader.rb +++ b/lib/psych/class_loader.rb @@ -35,9 +35,11 @@ module Psych constants.each do |const| konst = const_get const - define_method(const.to_s.downcase) do - load konst - end + class_eval <<~RUBY + def #{const.to_s.downcase} + load #{konst.inspect} + end + RUBY end private @@ -69,7 +71,7 @@ module Psych rescue nil end - }.compact] + }.compact].freeze class Restricted < ClassLoader def initialize classes, symbols diff --git a/lib/psych/visitors/visitor.rb b/lib/psych/visitors/visitor.rb index 3f4ba64..e2585c0 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.compare_by_identity + 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/lib/psych/visitors/yaml_tree.rb b/lib/psych/visitors/yaml_tree.rb index 986c57b..ac6777a 100644 --- a/lib/psych/visitors/yaml_tree.rb +++ b/lib/psych/visitors/yaml_tree.rb @@ -80,7 +80,7 @@ module Psych raise(TypeError, "Can't dump #{target.class}") unless method h[klass] = method - end + end.compare_by_identity end def start encoding = Nodes::Stream::UTF8 diff --git a/test/psych/test_ractor.rb b/test/psych/test_ractor.rb new file mode 100644 index 0000000..c6bed7c --- /dev/null +++ b/test/psych/test_ractor.rb @@ -0,0 +1,50 @@ +# 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 + # Config is ractor-local + # Test is to make sure it works, even though usage is probably very low. + # The methods are not documented and might be deprecated one day + 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 if defined?(Test::Unit::TestCase) |