Skip to content

Commit 0a71bb9

Browse files
committed
Better handling of ready: true.
1 parent 481118f commit 0a71bb9

3 files changed

Lines changed: 29 additions & 5 deletions

File tree

fixtures/async/container/a_container.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ module Container
107107
)
108108
end
109109
end
110+
111+
it "can exec with ready: true without premature termination" do
112+
container.spawn(restart: false) do |instance|
113+
# Using exec with ready: true should not cause the process to be killed
114+
# by hang prevention, even though the notification pipe stays open.
115+
instance.exec("sleep", "1", ready: true)
116+
end
117+
118+
# Wait for the process to become ready:
119+
container.wait_until_ready
120+
121+
# Sleep longer than the hang prevention timeout (0.1s) to verify
122+
# the process isn't prematurely killed:
123+
sleep(0.2)
124+
125+
# The process should still be running (not killed by hang prevention):
126+
expect(container).to be(:running?)
127+
128+
# Now stop the container:
129+
container.stop(false)
130+
end
110131
end
111132

112133
with "#sleep" do

lib/async/container/forked.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ def name
8080
# This method replaces the child process with the new executable, thus this method never returns.
8181
#
8282
# @parameter arguments [Array] The arguments to pass to the new process.
83-
# @parameter ready [Boolean] If true, informs the parent process that the child is ready. Otherwise, the child process will need to use a notification protocol to inform the parent process that it is ready.
83+
# @parameter ready [Boolean] If true, informs the parent process that the child is ready before exec. The notification pipe will still be passed to the exec'd process to prevent premature termination.
8484
# @parameter options [Hash] Additional options to pass to {::Process.exec}.
8585
def exec(*arguments, ready: true, **options)
86+
# Always set up the notification pipe to be inherited by the exec'd process.
87+
# This prevents the pipe from closing, which would trigger hang prevention and SIGKILL.
88+
self.before_spawn(arguments, options)
89+
8690
if ready
8791
self.ready!(status: "(exec)")
88-
else
89-
self.before_spawn(arguments, options)
9092
end
9193

9294
::Process.exec(*arguments, **options)

lib/async/container/threaded.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,11 @@ def name
9191
# Execute a child process using {::Process.spawn}. In order to simulate {::Process.exec}, an {Exit} instance is raised to propagage exit status.
9292
# This creates the illusion that this method does not return (normally).
9393
def exec(*arguments, ready: true, **options)
94+
# Always set up the notification pipe to be inherited by the spawned process.
95+
self.before_spawn(arguments, options)
96+
9497
if ready
9598
self.ready!(status: "(spawn)")
96-
else
97-
self.before_spawn(arguments, options)
9899
end
99100

100101
begin

0 commit comments

Comments
 (0)