Class: Contrast::Agent::Assess::Finalizers::Hash
- Defined in:
- lib/contrast/agent/assess/finalizers/hash.rb
Overview
An extension of Hash that doesn’t impact GC of the object being stored by storing its ID as a Key to lookup and registering a finalizer on the object to remove its entry from the Hash immediately after it’s GC’d.
NOTE: PROPERTIES_HASH is called from C
Constant Summary collapse
- FROZEN_FINALIZED_IDS =
Set.new
- KEEP_AGE =
10 minutes
600_000.cs__freeze
Instance Method Summary collapse
-
#[](key) ⇒ Object?
Retrieves the Object stored in the Hash by its __id__.
-
#[]=(key, obj) ⇒ Object
Store the given Object in the Hash, using its __id__ as the key.
- #cleanup! ⇒ Object
-
#finalizing_proc ⇒ Proc
Create a Proc to remove the given key from our frozen and properties tracking during finalization of the Object to which the given key_id pertains.
-
#pre_freeze(key) ⇒ Object
Frozen things cannot be finalized.
-
#trackable?(key) ⇒ Boolean
Something is trackable if it is not a collection and either not frozen or it was frozen after we put a finalizer on it.
-
#tracked?(key) ⇒ Boolean
Determine if the given Object is tracked, meaning it has a known set of properties and those properties are tracked.
Instance Method Details
#[](key) ⇒ Object?
Retrieves the Object stored in the Hash by its __id__.
39 40 41 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 39 def [] key super(key.__id__) end |
#[]=(key, obj) ⇒ Object
Store the given Object in the Hash, using its __id__ as the key. If the Object is frozen, we need to pre-finalize it and store its __id__ in our tracking Set.
23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 23 def []= key, obj return unless obj return unless ::Contrast::AGENT.enabled? && ::Contrast::ASSESS.enabled? # We can't finalize frozen things, so only act on those that went through .pre_freeze if key.cs__frozen? return unless FROZEN_FINALIZED_IDS.include?(key.__id__) else ObjectSpace.define_finalizer(key, finalizing_proc) end super(key.__id__, obj) end |
#cleanup! ⇒ Object
103 104 105 106 107 108 109 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 103 def cleanup! ids = keys.dup ids.each do |key| properties = fetch(key.__id__, nil) delete(key) unless properties&.event && (Contrast::Utils::Timer.now_ms - properties.event.time) < KEEP_AGE end end |
#finalizing_proc ⇒ Proc
Create a Proc to remove the given key from our frozen and properties tracking during finalization of the Object to which the given key_id pertains. The ObjectSpace’s finalizer mechanism will handle passing this ID in, so we only need to define the Proc once.
NOTE: by necessity, this is the only method which takes the __id__, not the Object itself. You CANNOT pass the Object to this as a finalizer cannot hold reference to the Object being finalized; that prevents GC, which introduces a memory leak and defeats the entire purpose of this.
78 79 80 81 82 83 84 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 78 def finalizing_proc @_finalizing_proc ||= proc do |key_id| FROZEN_FINALIZED_IDS.delete(key_id) Contrast::Agent::Assess::Policy::PropagationMethod.instance_variable_get(:@properties).delete(key_id) delete(key_id) end end |
#pre_freeze(key) ⇒ Object
Frozen things cannot be finalized. To avoid any issue here, we intercept the #freeze call and set finalizers on the Object. To ensure later we know it’s been pre-finalized, we add it’s __id__ to our tracking.
91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 91 def pre_freeze key return unless ::Contrast::AGENT.enabled? && ::Contrast::ASSESS.enabled? return if key.cs__frozen? return if FROZEN_FINALIZED_IDS.include?(key.__id__) ObjectSpace.define_finalizer(key, finalizing_proc) FROZEN_FINALIZED_IDS << key.__id__ rescue StandardError => _e nil end |
#trackable?(key) ⇒ Boolean
Something is trackable if it is not a collection and either not frozen or it was frozen after we put a finalizer on it.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 48 def trackable? key return false unless key # Track things in these, not them themselves. return false if Contrast::Utils::DuckUtils.iterable_hash?(key) return false if Contrast::Utils::DuckUtils.iterable_enumerable?(key) # If it's not frozen, we can finalize/ track it. return true unless key.cs__frozen? # Otherwise, we can only track it if we've finalized it in our freeze patch. FROZEN_FINALIZED_IDS.include?(key.__id__) end |
#tracked?(key) ⇒ Boolean
Determine if the given Object is tracked, meaning it has a known set of properties and those properties are tracked.
65 66 67 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 65 def tracked? key key?(key.__id__) && fetch(key.__id__, nil)&.tracked? end |