Module: Pikuri::Finalizers
- Defined in:
- lib/pikuri/finalizers.rb
Overview
Process-global teardown registry: one at_exit for the whole process,
with everything owning a resource that needs orderly shutdown (agents,
VectorDb::Server::Chroma, background workers) registering here instead of
growing its own at_exit. Agent#on_close promoted per-agent → per-process
— the same LIFO + per-handler-rescue + idempotent shape.
Why one chokepoint
Independent at_exit hooks fire in file-load order — invisible and fragile.
One registry makes the order explicit: the SIGTERM-the-strays backstop
(Subprocess.cleanup!) registers at load time, so it sits at the bottom
of the LIFO stack and runs last — after agents and servers (registered at
construction time) close gracefully, while the subprocess machinery they
shell out to during close (a docker stop) is still live.
Contract
A registrant MUST respond to #close, idempotent and tolerant of running at
exit (the host may have closed it earlier). Pass a block instead for
teardown with no natural #close
(+Finalizers.register { Pikuri::Subprocess.cleanup! }+).
Order: LIFO
Last registered, first closed. A registrant depending on an earlier one (a background indexer writing into VectorDb::Server::Chroma) registers later and tears down first — registration order is dependency order.
Errors are contained
Each #close runs in its own rescue (a raise is logged, the sweep
continues). Finalizers.run! drains the registry, so a second call closes nothing.
Defined Under Namespace
Classes: Closer
Constant Summary collapse
- LOGGER =
Returns subsystem logger for contained teardown failures.
Pikuri.logger_for('Finalizers')
Class Method Summary collapse
-
.register(closeable = nil) { ... } ⇒ #close
Register a closeable (or a block) to tear down at exit.
-
.run! ⇒ void
Close every registrant in LIFO order, each guarded by its own
rescue. -
.unregister(handle) ⇒ void
Drop a previously-registered handle.
Class Method Details
.register(closeable = nil) { ... } ⇒ #close
Register a closeable (or a block) to tear down at exit. Returns the handle so the caller can later unregister it — a resource closed explicitly before exit should drop out to be GC'd, not pinned alive.
62 63 64 65 66 67 68 69 70 |
# File 'lib/pikuri/finalizers.rb', line 62 def register(closeable = nil, &block) unless closeable || block raise ArgumentError, 'Finalizers.register requires an object or a block' end handle = closeable || Closer.new(block) @mutex.synchronize { @registered << handle } handle end |
.run! ⇒ void
This method returns an undefined value.
Close every registrant in LIFO order, each guarded by its own rescue.
Wired to at_exit below. Draining under the lock makes a repeat call a
no-op and stays safe against a concurrent caller.
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/pikuri/finalizers.rb', line 86 def run! handles = @mutex.synchronize do taken = @registered.reverse @registered.clear taken end handles.each do |handle| handle.close rescue StandardError => e LOGGER.warn("finalizer #{handle.class} raised #{e.class}: #{e.}") end end |
.unregister(handle) ⇒ void
This method returns an undefined value.
Drop a previously-registered handle. Idempotent.
76 77 78 79 |
# File 'lib/pikuri/finalizers.rb', line 76 def unregister(handle) @mutex.synchronize { @registered.delete(handle) } nil end |