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

FFI: break from within a callback block behaves differently than on MRI #3559

Closed
paddor opened this issue Dec 21, 2015 · 2 comments
Closed

Comments

@paddor
Copy link

paddor commented Dec 21, 2015

MRI correctly aborts the execution of of a C function when the FFI::Function passed to it breaks. On JRuby (and Rubinius, for that matter), this doesn't happen. If the C function has a loop that would return early in case the callback returned -1, it even keeps calling the callback.

Consider this C code, which simply calls a callback 10 times, unless the callback function returns -1:

#include <stdio.h>
typedef int (callback_fn) (void);

void
ten_times(callback_fn *callback)
{
    for (int i = 1; i <= 10; i++) {
        printf("libcallback: call #%i ...\n", i);
        int rc = (* callback)();
        if (rc == -1) {
            printf("libcallback: early return because rc == -1\n");
            return;
        }
    }
}

And here the Ruby script which attaches the function via FFI and verifies the bug with minitest:

require 'minitest/autorun'
require 'ffi'

module LibCallback
  extend ::FFI::Library

  lib_name = 'libcallback'
  ffi_lib "#{lib_name}.#{::FFI::Platform::LIBSUFFIX}"

  attach_function :ten_times, [:pointer], :void, blocking: true
end

def mk_callback
  FFI::Function.new(:void, [], blocking: true) do
    yield
  end
end

describe FFI::Function do
  def foo
    callback = mk_callback  do
      yield
    end
    LibCallback.ten_times(callback)
  end

  describe "with non-breaking block" do
    it "calls block 10 times" do
      called = 0
      foo { called += 1 }
      assert_equal 10, called
    end
  end

  describe "with breaking block" do

    it "doesn't call block anymore" do
      called = 0
      foo { called += 1; break }
      assert_equal 1, called
    end
  end
end

describe "pure Ruby block" do
  def foo
    10.times { yield }
  end

  describe "with non-breaking block" do
    it "calls block 10 times" do
      called = 0
      foo { called += 1 }
      assert_equal 10, called
    end
  end

  describe "with breaking block" do

    it "doesn't call block anymore" do
      called = 0
      foo { called += 1; break }
      assert_equal 1, called
    end
  end
end

Here what I expect, as when run on Ruby 2.2.4:

Run options: --seed 41083

# Running:

..libcallback: call #1 ...
libcallback: call #2 ...
libcallback: call #3 ...
libcallback: call #4 ...
libcallback: call #5 ...
libcallback: call #6 ...
libcallback: call #7 ...
libcallback: call #8 ...
libcallback: call #9 ...
libcallback: call #10 ...
.libcallback: call #1 ...
.

Finished in 0.002047s, 1954.3283 runs/s, 1954.3283 assertions/s.

4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

Here the actual output on JRuby 9.0.4.0 (and also Rubinius 2.5.8):

Run options: --seed 3230

# Running:

.libcallback: call #1 ...
libcallback: call #2 ...
libcallback: call #3 ...
libcallback: call #4 ...
libcallback: call #5 ...
libcallback: call #6 ...
libcallback: call #7 ...
libcallback: call #8 ...
libcallback: call #9 ...
libcallback: call #10 ...
.libcallback: call #1 ...
libcallback: call #2 ...
libcallback: call #3 ...
libcallback: call #4 ...
libcallback: call #5 ...
libcallback: call #6 ...
libcallback: call #7 ...
libcallback: call #8 ...
libcallback: call #9 ...
libcallback: call #10 ...
F.

Finished in 0.023067s, 173.4047 runs/s, 173.4047 assertions/s.

  1) Failure:
FFI::Function::with breaking block#test_0001_doesn't call block anymore [test.rb:40]:
Expected: 1
  Actual: 10

4 runs, 4 assertions, 1 failures, 0 errors, 0 skips
ruby test.rb  6.48s user 0.29s system 330% cpu 2.048 total

The files needed to reproduce this bug, including a Makefile to build on OSX, are in this gist.

@paddor paddor changed the title FFI::Function callback doesn't return block value to C FFI: break from within a callback block behaves differently than on MRI Dec 21, 2015
@paddor paddor closed this as not planned Won't fix, can't repro, duplicate, stale Jul 16, 2024
@headius
Copy link
Member

headius commented Jul 17, 2024

I see you closed this. Is it still a real issue though? We generally don't close old issues, but I understand you wanting to clean up your queue.

@paddor
Copy link
Author

paddor commented Jul 17, 2024

@headius I don't know. I didn't check. Just wanted to clean up my queue. You can reopen if you like.

@enebo enebo added this to the Invalid or Duplicate milestone Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants