Skip to content

Commit 7fba76e

Browse files
committed
Avoid use of id2ref for weak mapping on JRuby
This change eliminates the use of ObjectSpace_id2ref on JRuby, due to the overhead caused when enabing support for it in JRuby's version of ObjectSpace. The new implementation uses Weakref with a simple Hash to build a weak mapping from object IDs to weak references. This Hash is periodically cleaned with a full search, due to the lack of reference queue support in Ruby's Weakref implementation. This originally was proposed in https://bugs.ruby-lang.org/issues/15711 but was never actually fixed in the drb library. A partial merge of the WeakIdConv class was done at some point, but seems to have been lost and did not make it into the ruby/drb repository. That commit did not actually make use of the new data structure, so it did not solve the issue anyway. This issue came up again in jruby/jruby#8323. JRuby used to patch our copy of drb.rb but when we moved to the gem we lost that patch.
1 parent 10fceea commit 7fba76e

File tree

1 file changed

+77
-16
lines changed

1 file changed

+77
-16
lines changed

lib/drb/drb.rb

+77-16
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
require 'socket'
5050
require 'io/wait'
5151
require 'monitor'
52+
require 'weakref'
5253
require_relative 'eq'
5354
require_relative 'version'
5455

@@ -357,27 +358,87 @@ class DRbConnError < DRbError; end
357358
#
358359
# For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb
359360
# and DRbNameIdConv in sample/name.rb in the full drb distribution.
361+
#
362+
# JRuby Notes:
363+
#
364+
# In JRuby, id2ref is tracked manually in a weak hashing structure,
365+
# which causes it to have a large performance hit and often minor
366+
# behavioral differences from MRI. As a result, it is normally not
367+
# enabled unless ObjectSpace is enabled.
368+
#
369+
# Instead of using _id2ref directly, we implement a similar mechanism
370+
# here to localize the performance hit to only those objects being
371+
# tracked for DRb purposes.
360372
class DRbIdConv
361373

362-
# Convert an object reference id to an object.
363-
#
364-
# This implementation looks up the reference id in the local object
365-
# space and returns the object it refers to.
366-
def to_obj(ref)
367-
ObjectSpace._id2ref(ref)
368-
end
374+
if RUBY_ENGINE == "jruby"
369375

370-
# Convert an object into a reference id.
371-
#
372-
# This implementation returns the object's __id__ in the local
373-
# object space.
374-
def to_id(obj)
375-
case obj
376-
when Object
377-
obj.nil? ? nil : obj.__id__
378-
when BasicObject
376+
def initialize
377+
@id2ref = {}
378+
end
379+
380+
# Convert an object reference id to an object.
381+
#
382+
# This implementation looks up the reference id in the local object
383+
# space and returns the object it refers to.
384+
def to_obj(ref)
385+
_get(ref)
386+
end
387+
388+
# Convert an object into a reference id.
389+
#
390+
# This implementation returns the object's __id__ in the local
391+
# object space.
392+
def to_id(obj)
393+
_put(obj) unless obj == nil
394+
end
395+
396+
def _clean
397+
@id2ref.delete_if {|id,weakref| !weakref.weakref_alive?}
398+
end
399+
400+
def _put(obj)
401+
_clean
402+
@id2ref[obj.__id__] = WeakRef.new(obj)
379403
obj.__id__
380404
end
405+
406+
def _get(id)
407+
weakref = @id2ref[id]
408+
if weakref
409+
result = weakref.__getobj__ rescue nil
410+
if result
411+
return result
412+
else
413+
@id2ref.delete id
414+
end
415+
end
416+
nil
417+
end
418+
private :_clean, :_put, :_get
419+
420+
else
421+
422+
# Convert an object reference id to an object.
423+
#
424+
# This implementation looks up the reference id in the local object
425+
# space and returns the object it refers to.
426+
def to_obj(ref)
427+
ObjectSpace._id2ref(ref)
428+
end
429+
430+
# Convert an object into a reference id.
431+
#
432+
# This implementation returns the object's __id__ in the local
433+
# object space.
434+
def to_id(obj)
435+
case obj
436+
when Object
437+
obj.nil? ? nil : obj.__id__
438+
when BasicObject
439+
obj.__id__
440+
end
441+
end
381442
end
382443
end
383444

0 commit comments

Comments
 (0)