Class: Ignis::Shared::MemoryContract
- Inherits:
-
Object
- Object
- Ignis::Shared::MemoryContract
- Defined in:
- lib/nnw/shared/memory_contract.rb
Overview
MemoryContract — Pinned memory ownership enforcement.
Prevents double-free and use-after-free — the actual bugs that killed the previous Ignis build.
Rules:
- An NvArray may only be freed by its current owner.
- Ignis is the default owner after allocation.
- NvCCL must call acquire before operating, release when done.
- WNAIS must call acquire before NOVA I/O, release when done.
- Acquiring while ref_count > 1 raises MemoryContractViolation.
- Pinned host memory is always owned by Ignis::Memory. NvCCL may
read it but may not free it.
Thread-safe: all operations protected by Monitor.
Class Method Summary collapse
-
.acquire(array, by:) ⇒ Boolean
Acquire ownership of an NvArray.
-
.assert_owner!(array, expected_owner) ⇒ void
Assert that an NvArray is owned by the expected owner.
-
.audit ⇒ Array<Hash>
Audit all tracked arrays for potential leaks.
-
.instance ⇒ MemoryContract
Singleton instance.
-
.release(array, by:) ⇒ Boolean
Release ownership of an NvArray back to the default owner (:nvruby).
-
.reset! ⇒ void
Reset the singleton instance (for testing only).
-
.track(array) ⇒ void
Track an NvArray in the contract system.
-
.tracked_count ⇒ Integer
Get the number of tracked arrays.
-
.untrack(array) ⇒ void
Remove an NvArray from tracking (called after free).
Instance Method Summary collapse
- #acquire(array, by:) ⇒ Object
- #assert_owner!(array, expected_owner) ⇒ Object
- #audit ⇒ Object
-
#initialize ⇒ MemoryContract
constructor
Instance methods.
- #release(array, by:) ⇒ Object
- #track(array) ⇒ Object
- #tracked_count ⇒ Object
- #untrack(array) ⇒ Object
Constructor Details
#initialize ⇒ MemoryContract
Instance methods
99 100 101 102 |
# File 'lib/nnw/shared/memory_contract.rb', line 99 def initialize @monitor = Monitor.new @tracked = {} # id -> {array:, tracked_at:} end |
Class Method Details
.acquire(array, by:) ⇒ Boolean
Acquire ownership of an NvArray.
Transfers ownership from the current owner to the requesting layer. Fails if ref_count > 1 (concurrent shared references prevent safe transfer).
41 42 43 |
# File 'lib/nnw/shared/memory_contract.rb', line 41 def self.acquire(array, by:) instance.acquire(array, by: by) end |
.assert_owner!(array, expected_owner) ⇒ void
This method returns an undefined value.
Assert that an NvArray is owned by the expected owner.
61 62 63 |
# File 'lib/nnw/shared/memory_contract.rb', line 61 def self.assert_owner!(array, expected_owner) instance.assert_owner!(array, expected_owner) end |
.audit ⇒ Array<Hash>
Audit all tracked arrays for potential leaks.
Returns information about all live arrays that have been tracked and are older than 5 seconds.
71 72 73 |
# File 'lib/nnw/shared/memory_contract.rb', line 71 def self.audit instance.audit end |
.instance ⇒ MemoryContract
Returns singleton instance.
22 23 24 |
# File 'lib/nnw/shared/memory_contract.rb', line 22 def self.instance @instance ||= new end |
.release(array, by:) ⇒ Boolean
Release ownership of an NvArray back to the default owner (:nvruby).
51 52 53 |
# File 'lib/nnw/shared/memory_contract.rb', line 51 def self.release(array, by:) instance.release(array, by: by) end |
.reset! ⇒ void
This method returns an undefined value.
Reset the singleton instance (for testing only).
28 29 30 |
# File 'lib/nnw/shared/memory_contract.rb', line 28 def self.reset! @instance = new end |
.track(array) ⇒ void
This method returns an undefined value.
Track an NvArray in the contract system.
79 80 81 |
# File 'lib/nnw/shared/memory_contract.rb', line 79 def self.track(array) instance.track(array) end |
.tracked_count ⇒ Integer
Get the number of tracked arrays.
93 94 95 |
# File 'lib/nnw/shared/memory_contract.rb', line 93 def self.tracked_count instance.tracked_count end |
.untrack(array) ⇒ void
This method returns an undefined value.
Remove an NvArray from tracking (called after free).
87 88 89 |
# File 'lib/nnw/shared/memory_contract.rb', line 87 def self.untrack(array) instance.untrack(array) end |
Instance Method Details
#acquire(array, by:) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/nnw/shared/memory_contract.rb', line 104 def acquire(array, by:) validate_owner!(by) @monitor.synchronize do current_owner = array.owner if current_owner == by raise MemoryContractViolation, "NvArray##{array.id} is already owned by #{by.inspect} — double-acquire" end array.transfer_ownership(by) track_if_needed(array) true end end |
#assert_owner!(array, expected_owner) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/nnw/shared/memory_contract.rb', line 136 def assert_owner!(array, expected_owner) validate_owner!(expected_owner) actual_owner = @monitor.synchronize { array.owner } unless actual_owner == expected_owner raise MemoryContractViolation, "NvArray##{array.id} expected owner #{expected_owner.inspect}, " \ "actual #{actual_owner.inspect}" end end |
#audit ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/nnw/shared/memory_contract.rb', line 148 def audit now = Time.now threshold_seconds = 5.0 @monitor.synchronize do @tracked.values .select { |entry| (now - entry[:tracked_at]) > threshold_seconds } .map do |entry| arr = entry[:array] { array_id: arr.id, owner: arr.owner, age_ms: ((now - entry[:tracked_at]) * 1000).round, shape: arr.shape, dtype: arr.dtype, freed: arr.freed? } end end end |
#release(array, by:) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/nnw/shared/memory_contract.rb', line 121 def release(array, by:) validate_owner!(by) @monitor.synchronize do unless array.owner == by raise MemoryContractViolation, "NvArray##{array.id} is owned by #{array.owner.inspect}, " \ "not #{by.inspect} — cannot release" end array.transfer_ownership(:nvruby) true end end |
#track(array) ⇒ Object
169 170 171 172 173 |
# File 'lib/nnw/shared/memory_contract.rb', line 169 def track(array) @monitor.synchronize do @tracked[array.id] = { array: array, tracked_at: Time.now } end end |
#tracked_count ⇒ Object
181 182 183 |
# File 'lib/nnw/shared/memory_contract.rb', line 181 def tracked_count @monitor.synchronize { @tracked.size } end |
#untrack(array) ⇒ Object
175 176 177 178 179 |
# File 'lib/nnw/shared/memory_contract.rb', line 175 def untrack(array) @monitor.synchronize do @tracked.delete(array.id) end end |