I've got an issue with Ruby. Specifically, the spawn()
and fork()
methods.
I'm developing a website using Nanoc on Windows, and upon trying to implement the nanoc-live
gem, I get the message that fork()
is unimplemented on this machine.
Upon researching what I could about this, the only thing I really came back with, was to replace it with spawn()
. However, I don't know Ruby. And the nanoc-live documentation is down. I have asked on what support forums they have with no luck.
I don't ask how to do this lightly because I like figuring out issues but this has me stumped, and I'm hardly a Ruby dev. How would I replace the below method call so it uses spawn()
? Or, if someone has a different solution in their back pocket, that would also be great.
Ruby version 3.2.2
Nanoc version 4.12.15
Windows 10
This is the offending method
def run_parent
# create initial child
pipe_read, pipe_write = IO.pipe
fork { run_child(pipe_write, pipe_read) { |s| yield(s) } } Here is the method call
pipe_read.close
changes = gen_lib_changes
puts 'Listening for lib/ changes…'
changes.each do |_e|
# stop child
pipe_write.write('q')
pipe_write.close
Process.wait
# create new child
pipe_read, pipe_write = IO.pipe
fork { run_child(pipe_write, pipe_read) { |s| yield(s) } }
pipe_read.close
end
rescue Interrupt
end
And this is the run_child method called by fork()
def run_child(pipe_write, pipe_read)
pipe_write.close
site = Nanoc::Core::SiteLoader.new.new_from_cwd
changes_enum = gen_changes_for_child(site)
yield(site)
quit = Object.new
parent_enum = Enumerator.new do |y|
pipe_read.read
y << quit
end
puts 'Listening for site changes…'
SlowEnumeratorTools.merge([parent_enum, changes_enum]).each do |e|
break if quit.equal?(e)
$stderr.print 'Reloading site… '
$stderr.flush
site_loader = Nanoc::Core::SiteLoader.new
site = Nanoc::Core::Site.new(
config: Nanoc::Core::ConfigLoader.new.new_from_cwd,
data_source: site_loader.gen_data_source_for_config(site.config),
code_snippets: site.code_snippets,
)
$stderr.puts 'done'
yield(site)
end
exit 0
rescue Interrupt
exit 0
end
Thanks In Advance.
You can use spawn like this to achieve what you want. However, as the answer details, process spawning like this in Windows is not optimal. You should consider threads.
spawn()
docs if you didn't have them already
Firstly, Linux supports forking
, but Windows does not; Windows only supports spawning
:
forking
= a parent process creates a copy of itself (a child) that has an exact copy of all the memory segments it uses
spawning
= a parent process creates an entirely new child process that does not share its memory and the parent process must wait for the child process to finish before continuing
To be clear, this is an issue fundamental to the Windows operating system in general and not to any specific programming language, including Ruby. For instance, I have run into an issue regarding this here when using Python.
Thus, the answer in the link provided by @amycodes unfortunately contains accurate information.
To mimic fork
, you will have to spawn
a new process/thread and then copy your data to the new process/thread. You will have to also check if your data can be copied in a threadsafe manner (e.g. generators cannot be marshaled across process boundaries in Python, and you are using a yield
in your source code, which makes me think what you want to do might not be possible). Since you are replacing a built-in operation in Linux OS's with a process/thread create plus a copy, please note that this will run slower than it does on Linux (such is the price to be paid for using Windows).
I am not particularly well-versed in Ruby, so I'm hoping someone else can double-check my answer.
(Best of luck my friend!)
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