I am trying to spawn a Ruby process on Windows using something like this:
p1 = spawn('ruby', 'loop.rb', [:out, :err] => ['process.log', "w"], :new_pgroup => true)
I also then detach from the process via:
p1.detach
This should as far I understand create a new process which is independent of the parent. I am even using new_pgroup parameter to make sure that new process gets its own process group.
When I execute my script, the sub-process gets started and keeps running. The execution of the script spawning the sub process also completes. However, when I now close the shell, the sub-process dies. I would expect it to continue running (it does on OS X and Linux). I cannot figure out whether this is a bug in the Ruby runtime on Windows or whether this is a limitation of Windows and how it handles processes.
For completeness the full Ruby code of what I am trying to do:
spawner.rb: can be executed via ruby spawner.rb
and just spawns a new sub-process. The process creates is loop.rb which is just an endless loop. Depending on the OS it specifies a different parameter for the process group creation.
require "tempfile"
require 'rbconfig'
class SpawnTest
def self.spawn_process
if os == :windows
p1 = spawn('ruby', 'loop.rb', [:out, :err] => ['process.log', "w"], :new_pgroup => true)
else
p1 = spawn('ruby', 'loop.rb', [:out, :err] => ['process.log', "w"], :pgroup => true)
end
# Detach from the processes so they will keep running
puts p1
Process.detach(p1)
end
def self.os
@os ||= (
host_os = RbConfig::CONFIG['host_os']
case host_os
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
:windows
when /darwin|mac os/
:macosx
when /linux/
:linux
when /solaris|bsd/
:unix
else
raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
end
)
end
end
if __FILE__ == $0
SpawnTest.spawn_process
end
loop.rb
$stdout.sync = true
$stderr.sync = true
i = 0
while i < 10000
$stdout.puts "Iteration #{i}"
sleep 1
i = i + 1
end
$stdout.puts "Bye from #{Process.pid}"
I found the win32-process gem during my investigations. It seems to be using win32 API calls to spawn processes. Does anyone know whether this library would fix the problem?
Any help appreciated.
I couldn't find any use of process groups in Windows except for dispatching CTRL
events to multiple processes as it states in Process Creation Flags MSDN docs:
Process groups are used by the GenerateConsoleCtrlEvent function to enable sending a CTRL+BREAK signal to a group of console processes.
What actually happens when CREATE_NEW_PROCESS_GROUP
flag is specified is...
...an implicit call to SetConsoleCtrlHandler(NULL,TRUE) is made on behalf of the new process; this means that the new process has CTRL+C disabled. This lets shells handle CTRL+C themselves, and selectively pass that signal on to sub-processes. CTRL+BREAK is not disabled, and may be used to interrupt the process/process group.
From CreateProcess MSDN docs.
Note that this is not the same as closing the console window with the x
button. In the latter case the process receives CTRL_CLOSE_EVENT
,
A signal that the system sends to all processes attached to a console when the user closes the console (either by clicking Close on the console window's window menu, or by clicking the End Task button command from Task Manager).
From HandlerRoutine callback MSDN docs.
Handling of this event is not affected by SetConsoleCtrlHandler(NULL,TRUE)
set by CREATE_NEW_PROCESS_GROUP
flag when creating the process.
All above means CREATE_NEW_PROCESS_GROUP
flag has nothing to do with the behaviour you are observing.
By default a child process inherits parent's console window and IO handles. Therefore when you close the shell by clicking on the x
button, it receives CTRL_CLOSE_EVENT
and does not have an option to not terminate.
To avoid that, the child process should not be attached to the parent console. This is done by supplying DETACHED_PROCESS
flag to CreateProcess
Windows API.
By default, a console process inherits its parent's console ... Console processes are not attached to a console if they are created using CreateProcess with DETACHED_PROCESS.
From Creation of a Console MSDN docs.
This is what I believe Ruby's spawn
and win32-process
gem do differently. I haven't try to debug both in order to see the specific difference in the flags they supply to CreateProcess
although that would be an interesting thing to do.
There are other ways to create a child process without inheriting the console or IO handles of the parent. Examples are CREATE_NO_WINDOW
or CREATE_NEW_CONSOLE
flags, Job Objects, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With