Fix: at-fork handlers to prevent gRPC deadlock in child processes
Pipelines that use os.fork() (directly or via multiprocessing / cadcutils) could deadlock permanently when fork happened while a BatchSpanProcessor or BatchLogRecordProcessor background thread held an internal gRPC mutex. The child inherited the locked mutex but the thread was dead, causing any subsequent gRPC call in the child to wait forever.
What changed
Added helixobs/_fork.py with os.register_at_fork() handlers:
- before fork:
force_flush(timeout_millis=500)on all OTel providers — drains the export queue and releases locks before fork copies memory state - after fork in child:
shutdown()on all providers — stops background threads in the child so it never tries to export telemetry
Both configure_logging(otlp=True) and Instrument.__init__ automatically register their providers. No-op on Windows where os.register_at_fork is unavailable.