B #-: Fix ZMQ context finalizer deadlock on shutdown (#4083)

On openSUSE (Ruby 3.4), opennebula-flow, opennebula-hem and one_hm hang
on shutdown until systemd SIGKILLs them after 90s.

Root cause: during Ruby's exit sequence, GC finalizers run on unreachable
ZMQ::Context objects calling zmq_ctx_term(), which blocks until all
sockets from that context are closed. But the subscriber sockets were
never explicitly closed (threads were killed by Ruby before cleanup),
so the context finalizer deadlocks with the pending socket finalizers.

Fix: register an at_exit handler that removes ZMQ::Context finalizers
via ObjectSpace.undefine_finalizer. This prevents the blocking
zmq_ctx_term call from ever running during GC, letting socket finalizers
run zmq_close independently. The OS reclaims all remaining ZMQ resources
on process exit.
This commit is contained in:
Jan Orel authored and GitHub committed 2026-02-25 17:08:52 +01:00
1 parent 838542b30a
commit acf180b579
3 files changed
+25

No files matched your search

+9
View File
@@ -45,6 +45,15 @@ $LOAD_PATH << RUBY_LIB_LOCATION + '/cloud'
$LOAD_PATH << LIB_LOCATION + '/oneflow/lib'
require 'rubygems'
require 'ffi-rzmq'
# Prevent ZMQ context finalizer deadlock during shutdown.
# Remove context finalizers so zmq_ctx_term (which blocks until all sockets
# are closed) never runs from GC. Socket finalizers can then run zmq_close
# freely, and the OS cleans up remaining resources on process exit.
at_exit do
ObjectSpace.each_object(ZMQ::Context) {|c| ObjectSpace.undefine_finalizer(c) }
end
require 'sinatra'
require 'yaml'
+8
View File
@@ -45,6 +45,14 @@ require 'load_opennebula_paths'
$LOAD_PATH << RUBY_LIB_LOCATION
require 'ffi-rzmq'
# Prevent ZMQ context finalizer deadlock during shutdown.
# Remove context finalizers so zmq_ctx_term (which blocks until all sockets
# are closed) never runs from GC. Socket finalizers can then run zmq_close
# freely, and the OS cleans up remaining resources on process exit.
at_exit do
ObjectSpace.each_object(ZMQ::Context) {|c| ObjectSpace.undefine_finalizer(c) }
end
require 'nokogiri'
require 'yaml'
require 'logger'
+8
View File
@@ -38,6 +38,14 @@ require 'OpenNebulaDriver'
require 'getoptlong'
require 'rubygems'
require 'ffi-rzmq'
# Prevent ZMQ context finalizer deadlock during shutdown.
# Remove context finalizers so zmq_ctx_term (which blocks until all sockets
# are closed) never runs from GC. Socket finalizers can then run zmq_close
# freely, and the OS cleans up remaining resources on process exit.
at_exit do
ObjectSpace.each_object(ZMQ::Context) {|c| ObjectSpace.undefine_finalizer(c) }
end
require 'base64'
require 'nokogiri'