-
Notifications
You must be signed in to change notification settings - Fork 60
Segfault if Vips constants are accessed before Vips::Image #98
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
Comments
Hi Felix, sorry for the delay in getting to this, I've been clearing decks after my holiday. I tried adding this to your test program: require "vips"
Vips::set_debug TRUE
# Important for Crash: Then I ran under gdb like this:
So your line: VIPS_ACCESS = Vips::Access::SEQUENTIAL_UNBUFFERED seems to trigger https://github.com/jcupitt/ruby-vips/blob/master/lib/vips.rb#L44 I can step a few lines and see the hash table being built:
If I now continue, I see a crash:
So the hash table is vanishing some time between vips init and its use. vips has no code that NULLs out this table, so it must be some bit of Ruby machinery that's doing it. Could this be caused by the way the unit tester works? Perhaps it is doing the init in the main process, but forking off a process to run the test after the If I change your program to be: require "tempfile"
require "vips"
Vips::set_debug TRUE
# Important for Crash:
# Access Vips constant before initializing vips
# via Vips::Image.new_from_file.
VIPS_ACCESS = Vips::Access::SEQUENTIAL_UNBUFFERED
tmp = Tempfile.new ['', '.pbm']
tmp.write "P1\n1 1\n0" # 1x1 Black PBM
tmp.flush
Vips::Image.new_from_file tmp.path, access: VIPS_ACCESS It seems to work OK. |
If I change your program to be: class TestVipsCrash < MiniTest::Test
def test_segfault
# Important for Crash:
# Access Vips constant before initializing vips
# via Vips::Image.new_from_file.
vips_access = Vips::Access::SEQUENTIAL_UNBUFFERED
tmp = Tempfile.new ['', '.pbm']
tmp.write "P1\n1 1\n0" # 1x1 Black PBM
tmp.flush
Vips::Image.new_from_file tmp.path, access: vips_access
end
end it works too. So I think I blame minitest :( I like the bundler inline mode, I didn't know it could do that! |
I think minitest can run tests in parallel, could this be a thread safety issue similar to the shutdown crashs on python? |
I have added some debug code to print the current PID ( Also disabling garbage collection at the beginning of the script with Maybe this is a bug in the gobject-introspection gem. |
I had a good idea on this. at_exit {
Vips::shutdown if Vips.respond_to? :shutdown
} https://github.com/jcupitt/ruby-vips/blob/master/lib/vips.rb#L142 Perhaps this is being triggered by minitest as a result of fork? Could you try deleting this code? |
Hey John, nice to hear from you! The trouble with fork is usually related to file descriptor sharing and is not specific to ruby. Let's say you have a database connection open before you fork, then both the parent and child will access the same connection which is certain to cause trouble. So the usual approach is to close the connection before forking and reconnect after fork in both the parent and the child. Such an issue would be easy to debug: # Initialize Vips and do stuff with it
Process.wait fork do
# work with vips here
end
# Continue in parent As soon as the child exits it will call all However I already mentioned above:
What looks more dangerous is that you are using In fact I just found a way to reproduce the error from issue #107: q=Queue.new
Thread.new { require "vips"; r = Vips::Access::SEQUENTIAL rescue $!; q << r }
Thread.new { require "vips"; r = Vips::Image.new_from_array([0]) rescue $!; q << r }
q.pop
=> #<NoMethodError: undefined method `new_from_array' for Vips::Image:Class
q.pop
=> #<Vips::Access sequential> This clearly shows a race condition during concurrent init. |
Hey, Felix! I agree, concurrent init will certainly fail. This seems like a simple thing to fix, fortunately. |
I can confirm this crash as fixed by #114. |
In one of my projects I have a unit test, which always crashes in
Vips::Image.new_from_file
as long as aVips::Access
constant is access beforeVips::Image
, so it is likely related to load order / initialization of gobject introspection.I have the following test case, which always crashes until I move the Vips::Access location after Vips::Image in the source code or use a symbol instead of the constant (eg.
:sequential_unbuffered
instead ofVips::Access::SEQUENTIAL_UNBUFFERED
.The crash seems to be related to the following error:
I tested with both ruby-2.3.3 and ruby-2.4.0 on macOS 10.12.3 x64 / Homebrew / RVM 1.28.0.
The test case requires bundler, so you need to do
gem install bundler
followed byruby testcase.rb
.The test case uses a PBM file, but any image format should do.
The following code would not crash:
Here's the crash data on ruby-2.4.0:
The text was updated successfully, but these errors were encountered: