Skip to content

Commit

Permalink
[ruby/psych] Make Ractor-ready.
Browse files Browse the repository at this point in the history
Config is Ractor-local.

Benchmarking reveals that using `Ractor.local_storage` for storing cache
is similar to accessing a constant (~15% slower).
  • Loading branch information
marcandre committed Dec 23, 2020
1 parent aea439e commit 3a85cdb
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 6 deletions.
26 changes: 23 additions & 3 deletions ext/psych/lib/psych.rb
Original file line number Diff line number Diff line change
Expand Up @@ -632,9 +632,29 @@ def self.parse_caller(at)
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 = {}
Expand Down
20 changes: 17 additions & 3 deletions ext/psych/lib/psych/visitors/visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@ def accept target

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
Expand Down
3 changes: 3 additions & 0 deletions ext/psych/psych.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
47 changes: 47 additions & 0 deletions test/psych/test_ractor.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 3a85cdb

Please sign in to comment.