Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vips hangs when drawing an image after a fork call #396

Closed
malomalo opened this issue May 15, 2024 · 4 comments
Closed

Vips hangs when drawing an image after a fork call #396

malomalo opened this issue May 15, 2024 · 4 comments
Labels

Comments

@malomalo
Copy link

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.

@malomalo malomalo added the bug label May 15, 2024
@jcupitt
Copy link
Member

jcupitt commented May 15, 2024

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.

@jcupitt jcupitt added question and removed bug labels May 15, 2024
@malomalo
Copy link
Author

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?

@jcupitt
Copy link
Member

jcupitt commented May 16, 2024

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.

https://www.libvips.org/2021/03/08/ruby-vips-mutate.html

@malomalo
Copy link
Author

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.

malomalo added a commit to malomalo/bob_ross that referenced this issue May 16, 2024
Because of libvips/ruby-vips#396
and libvips/ruby-vips#155 we'll identify
in a fork to allow for forking of the server process in production
and/or parrallel testing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants