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
- #[]=(key, obj) ⇒ Object
- #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
32 33 34 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 32 def [] key super(key.__id__) end |
#[]=(key, obj) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 19 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
96 97 98 99 100 101 102 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 96 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.
71 72 73 74 75 76 77 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 71 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.
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 84 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.
41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 41 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.
58 59 60 |
# File 'lib/contrast/agent/assess/finalizers/hash.rb', line 58 def tracked? key key?(key.__id__) && fetch(key.__id__, nil)&.tracked? end |