Module: Henitai::InheritedFdRegistry
- Defined in:
- lib/henitai/inherited_fd_registry.rb
Overview
Tracks parent-process file handles that a forked child must not keep open.
The motivating case is the reports-directory lock. flock is held on the
open file description, which parent and child share after a fork. If a
child outlives its parent while holding an inherited copy of that handle,
the lock stays held by a process that is no longer running a mutation run,
and every later invocation fails with a ConcurrentRunError naming a dead
pid. Closing the child's copy immediately after fork means the lock dies
with the parent, as intended.
Registration is parent-side; #close_all! is the child-side call. The registry deliberately holds IO objects rather than file descriptor numbers: a child that recreated the handle via IO.for_fd would leave the original object alive, and its finalizer could later close a descriptor number the child had since reused for something else.
Class Method Summary collapse
-
.close_all! ⇒ Object
Closes every tracked handle.
- .register(io) ⇒ Object
-
.registered ⇒ Array<IO>
Copy of the tracked handles.
- .unregister(io) ⇒ Object
Class Method Details
.close_all! ⇒ Object
Closes every tracked handle. Call this in the child, immediately after Process.fork.
This deliberately does NOT take the mutex. fork can land while
another thread in the parent holds it -- the coverage bootstrap thread
and the scheduler's worker threads both run concurrently with spawning
-- and only the forking thread survives into the child. Waiting on a
mutex whose owner does not exist there would hang the child forever.
Reading a stale snapshot is harmless; deadlocking is not.
48 49 50 51 52 53 |
# File 'lib/henitai/inherited_fd_registry.rb', line 48 def close_all! ios = @ios.dup @ios = [] ios.each { |io| close_quietly(io) } nil end |
.register(io) ⇒ Object
24 25 26 27 |
# File 'lib/henitai/inherited_fd_registry.rb', line 24 def register(io) @mutex.synchronize { @ios << io } io end |
.registered ⇒ Array<IO>
Returns copy of the tracked handles.
35 36 37 |
# File 'lib/henitai/inherited_fd_registry.rb', line 35 def registered @mutex.synchronize { @ios.dup } end |
.unregister(io) ⇒ Object
29 30 31 32 |
# File 'lib/henitai/inherited_fd_registry.rb', line 29 def unregister(io) @mutex.synchronize { @ios.delete(io) } io end |