diff --git a/lib/drb/weakidconv.rb b/lib/drb/weakidconv.rb new file mode 100644 index 0000000000..7f973dc8cc --- /dev/null +++ b/lib/drb/weakidconv.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: false +require_relative 'drb' +require 'monitor' + +module DRb + + # To use WeakIdConv: + # + # DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new}) + + class WeakIdConv < DRbIdConv + class WeakSet + include MonitorMixin + def initialize + super() + @immutable = {} + @map = ObjectSpace::WeakMap.new + end + + def add(obj) + synchronize do + begin + @map[obj] = self + rescue ArgumentError + @immutable[obj.__id__] = obj + end + return obj.__id__ + end + end + + def fetch(ref) + synchronize do + @immutable.fetch(ref) { + @map.each { |key, _| + return key if key.__id__ == ref + } + raise RangeError.new("invalid reference") + } + end + end + end + + def initialize() + super() + @weak_set = WeakSet.new + end + + def to_obj(ref) # :nodoc: + return super if ref.nil? + @weak_set.fetch(ref) + end + + def to_id(obj) # :nodoc: + return @weak_set.add(obj) + end + end +end + +# DRb.install_id_conv(WeakIdConv.new) diff --git a/test/drb/test_drbobject.rb b/test/drb/test_drbobject.rb index 3c7b0c1971..6b88087070 100644 --- a/test/drb/test_drbobject.rb +++ b/test/drb/test_drbobject.rb @@ -1,6 +1,7 @@ require 'test/unit' require 'drb' require 'drb/timeridconv' +require 'drb/weakidconv' module DRbObjectTest class Foo @@ -45,3 +46,19 @@ class TestDRbObjectTimerIdConv < Test::Unit::TestCase DRb.start_service(nil, nil, {:idconv => DRb::TimerIdConv.new}) end end + +class TestDRbObjectWeakIdConv < Test::Unit::TestCase + include DRbObjectTest + + def setup + DRb.start_service(nil, nil, {:idconv => DRb::WeakIdConv.new}) + end + + def test_RangeError + proxy = DRbObject.new("string".dup) + GC.start + assert_raise(RangeError) { + DRb.to_obj(proxy.__drbref) + } + end +end