Vips hangs when drawing an image after a `fork` call
malomalo opened this issue · 4 comments
ruby-vips
hangs when drawing a image after a fork
call.
Confirmed on both MacOS 14.2.1 and 13.6.6. Likely also affects linux as our CI system has the same issue.
I've attached the following test file which will hang; once you terminate the script you will also want to terminate the rouge process left behind from the fork.
require 'tempfile'
require 'vips'
class Image
class << self
def png(**kwargs)
f = Tempfile.new(['image', '.png'], nil, encoding: 'ascii-8bit')
draw_image(**kwargs).pngsave(f.path)
end
def draw_image(width: nil, height: nil, depth: 8, alpha: false)
width ||= rand(256) + 1
height ||= rand(256) + 1
bands = alpha ? 4 : 3
image = Vips::Image.black(width, height, bands: bands)
(0...width).each do |x|
color = Array.new(bands) { rand(2^depth) }
y1, y2 = Array.new(2) { rand(height) }.sort
# After fork gets stuck here and process lingers even after terminating
# parent process
image = image.draw_line(color, x, y1, x, y2)
end
image
end
end
end
Image.png # Works
fork { Image.png }
# Won't terminate because inside the fork hangs
Process.wait
This might be related to #155, but I don't believe so.
Hello @malomalo,
libvips is very threaded, so like all threaded libraries, you must be very careful when mixing it with fork.
When a process with several running threads forks, none of the threads copy over, instead you will have a new process with a single main thread. mutexes are in an indeterminate state, confusion is everywhere, nothing will work.
It's safest to only require 'ruby-vips'
in the child and after the fork. On some platforms you can do the require, then fork, and then process an image, but not all. You can never process some images, fork, and then process some more.
Interesting
I'm running into this in my Rails test env when trying to parallelize test. I will look into what is happening before the fork to cause this as error #155 mentions it must be required before the fork
Is there anyway to reset vips after a fork perhaps?
You're right, I'd forgotten all the detail around appkit.
No, libvips doesn't support unload and reload, unfortunately.
This is an aside, but have you seen draw_line!
? It's usually many times faster than plain draw_line
since it can avoid a large copy on each operation.
Ah...
We have a Rails gem with an initializer that uses Vips to know image sizes on boot. Then Rails forks for testing and this issue presents itself.
Thanks re the mutate
, new to me.