1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/fiddle/test_function.rb
normal 15476c695d fiddle: release GVL for ffi_call
Some external functions I wish to call may take a long time
and unnecessarily block other threads.  This may lead to performance
regressions for fast functions as releasing/acquiring the GVL is not
cheap, but can improve performance for long-running functions
in multi-threaded applications.

This also means we must reacquire the GVL when calling Ruby-defined
callbacks for Fiddle::Closure, meaning we must detect whether the
current thread has the GVL by exporting ruby_thread_has_gvl_p
in internal.h

* ext/fiddle/function.c (struct nogvl_ffi_call_args):
  new struct for GVL release
  (nogvl_ffi_call): new function
  (function_call): adjust for GVL release
  [ruby-core:71642] [Feature #11607]
* ext/fiddle/closure.c (struct callback_args):
  new struct for GVL acquire
  (with_gvl_callback): adjusted original callback function
  (callback): wrapper for conditional GVL acquire
* ext/fiddle/depend: add dependencies
* ext/fiddle/extconf.rb: include top_srcdir for internal.h
* internal.h (ruby_thread_has_gvl_p): expose for fiddle
* vm_core.h (ruby_thread_has_gvl_p): moved to internal.h
* test/fiddle/test_function.rb (test_nogvl_poll): new test

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52723 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-23 21:20:56 +00:00

101 lines
2.8 KiB
Ruby

begin
require_relative 'helper'
rescue LoadError
end
module Fiddle
class TestFunction < Fiddle::TestCase
include Test::Unit::Assertions
def setup
super
Fiddle.last_error = nil
end
def test_default_abi
func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
assert_equal Function::DEFAULT, func.abi
end
def test_name
func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE, name: 'sin')
assert_equal 'sin', func.name
end
def test_argument_errors
assert_raise(TypeError) do
Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
end
assert_raise(TypeError) do
Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE)
end
assert_raise(TypeError) do
Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo')
end
end
def test_call
func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
assert_in_delta 1.0, func.call(90 * Math::PI / 180), 0.0001
end
def test_argument_count
closure = Class.new(Closure) {
def call one
10 + one
end
}.new(TYPE_INT, [TYPE_INT])
func = Function.new(closure, [TYPE_INT], TYPE_INT)
assert_raise(ArgumentError) do
func.call(1,2,3)
end
assert_raise(ArgumentError) do
func.call
end
end
def test_last_error
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
assert_nil Fiddle.last_error
func.call("000", "123")
refute_nil Fiddle.last_error
end
def test_strcpy
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
buff = "000"
str = f.call(buff, "123")
assert_equal("123", buff)
assert_equal("123", str.to_s)
end
def test_nogvl_poll
begin
poll = @libc['poll']
rescue Fiddle::DLError
skip 'poll(2) not available'
end
f = Function.new(poll, [TYPE_VOIDP, TYPE_INT, TYPE_INT], TYPE_INT)
msec = 200
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
th = Thread.new { f.call(nil, 0, msec) }
n1 = f.call(nil, 0, msec)
n2 = th.value
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
assert_in_delta(msec, t1 - t0, 100, 'slept correct amount of time')
assert_equal(0, n1, 'poll(2) called correctly main-thread')
assert_equal(0, n2, 'poll(2) called correctly in sub-thread')
end
def test_no_memory_leak
prep = 'r = Fiddle::Function.new(Fiddle.dlopen(nil)["rb_obj_tainted"], [Fiddle::TYPE_UINTPTR_T], Fiddle::TYPE_UINTPTR_T); a = "a"'
code = 'begin r.call(a); rescue TypeError; end'
assert_no_memory_leak(%w[-W0 -rfiddle], "#{prep}\n1000.times{#{code}}", "10_000.times {#{code}}", limit: 1.2)
end
end
end if defined?(Fiddle)