Module: WeakSet::StrongKeys
- Defined in:
- lib/weak_set/strong_keys.rb
Overview
This WeakSet strategy targets JRuby >= 9.4.6.0 and TruffleRuby >= 22. Older versions require additional indirections implemented in StrongSecondaryKeys:
The ObjectSpace::WeakMap on JRuby and TruffleRuby has strong keys and weak values. Thus, only the value object can be garbage collected to remove the entry while the key defines a strong object reference which prevents the key object from being garbage collected.
As a workaround, we use the element's object_id as a key. Being an Integer, the object_id is generally is not garbage collected anyway but allows to uniquely identity the object.
The WeakMaps do not allow to explicitly delete entries. We emulate this by setting the garbage-collectible value of a deleted entry to a simple new object. This value will be garbage collected on the next GC run which will then remove the entry. When accessing elements, we delete and filter out these recently deleted entries.
Class Method Summary collapse
-
.usable? ⇒ Bool
Checks if this strategy is usable for the current Ruby version.
Instance Method Summary collapse
-
#add(obj) ⇒ self
Adds the given object to the weak set and return
self
. -
#clear ⇒ self
Removes all elements and returns
self
. -
#delete?(obj) ⇒ self?
Deletes the given object from
self
and returnsself
if it was present in the set. -
#each {|element| ... } ⇒ self, Enumerator
Calls the given block once for each live element in
self
, passing that element as a parameter. -
#include?(obj) ⇒ Bool
true
if the given object is included inself
,false
otherwise. -
#initialize ⇒ void
initialize the weak map.
-
#size ⇒ Integer
The number of live elements in
self
. -
#to_a ⇒ Array
The live elements contained in
self
as anArray
.
Class Method Details
.usable? ⇒ Bool
Checks if this strategy is usable for the current Ruby version.
39 40 41 42 43 44 45 46 |
# File 'lib/weak_set/strong_keys.rb', line 39 def self.usable? case RUBY_ENGINE when "ruby", "truffleruby" true when "jruby" Gem::Version.new(RUBY_ENGINE_VERSION) >= Gem::Version.new("9.4.6.0") end end |
Instance Method Details
#add(obj) ⇒ self
Adds the given object to the weak set and return self
. Use WeakSet#merge to
add many elements at once.
In contrast to other "regular" objects, we will not retain a strong reference to the added object. Unless some other live objects still references the object, it will eventually be garbage-collected.
55 56 57 58 |
# File 'lib/weak_set/strong_keys.rb', line 55 def add(obj) @map[obj.__id__] = obj self end |
#clear ⇒ self
Removes all elements and returns self
61 62 63 64 |
# File 'lib/weak_set/strong_keys.rb', line 61 def clear @map = ObjectSpace::WeakMap.new self end |
#delete?(obj) ⇒ self?
WeakSet does not test member equality with ==
or eql?
. Instead,
it always checks strict object equality, so that, e.g., different
strings are not considered equal, even if they may contain the same
string content.
Deletes the given object from self
and returns self
if it was present
in the set. If the object was not in the set, returns nil
.
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/weak_set/strong_keys.rb', line 67 def delete?(obj) key = obj.__id__ return unless @map.key?(key) && @map[key].equal?(obj) # If there is a valid value in the WeakMap (with a strong object_id # key), we replace the value of the strong key with a temporary # DeletedEntry object. As we do not keep any strong reference to this # object, this will cause the key/value entry to vanish from the WeakMap # when the DeletedEntry object is eventually garbage collected. @map[key] = DeletedEntry.new self end |
#each {|element| ... } ⇒ self, Enumerator
Calls the given block once for each live element in self
, passing that
element as a parameter. Returns the weak set itself.
If no block is given, an Enumerator
is returned instead.
81 82 83 84 85 86 87 88 |
# File 'lib/weak_set/strong_keys.rb', line 81 def each return enum_for(__method__) { size } unless block_given? @map.values.each do |obj| yield(obj) unless DeletedEntry === obj end self end |
#include?(obj) ⇒ Bool
WeakSet does not test member equality with ==
or eql?
. Instead,
it always checks strict object equality, so that, e.g., different
strings are not considered equal, even if they may contain the same
string content.
Returns true
if the given object is included in self
, false
otherwise.
91 92 93 94 |
# File 'lib/weak_set/strong_keys.rb', line 91 def include?(obj) key = obj.__id__ !!(@map.key?(key) && @map[key].equal?(obj)) end |
#initialize ⇒ void
initialize the weak map
50 51 52 |
# File 'lib/weak_set/strong_keys.rb', line 50 def initialize @map = ObjectSpace::WeakMap.new end |
#size ⇒ Integer
Returns the number of live elements in self
.
97 98 99 100 101 102 103 |
# File 'lib/weak_set/strong_keys.rb', line 97 def size # Compared to using WeakMap#each_value like we do in WeakKeys, this # version is # * ~12% faster on JRuby >= 9.4.6.0 # * sam-ish on TruffleRuby 24 with a slight advantage to this version @map.values.delete_if { |obj| DeletedEntry === obj }.size end |
#to_a ⇒ Array
The order of elements on the returned Array
is non-deterministic.
We do not preserve preserve insertion order.
Returns the live elements contained in self
as an Array
.
106 107 108 |
# File 'lib/weak_set/strong_keys.rb', line 106 def to_a @map.values.delete_if { |obj| DeletedEntry === obj } end |