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

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

.registeredArray<IO>

Returns copy of the tracked handles.

Returns:

  • (Array<IO>)

    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