@@ -510,40 +510,32 @@ class S8F32S32 < FFI::Struct
510
510
expect ( v ) . to eq ( -1 )
511
511
end
512
512
513
- def testCallbackU8rV ( value , & block )
513
+ def testCallbackU8rV ( value )
514
514
v1 = 0xdeadbeef
515
- LibTest . testCallbackU8rV ( value ) { |i | v1 = yield ( i ) }
515
+ LibTest . testCallbackU8rV ( value ) { |i | v1 = i }
516
+ expect ( v1 ) . to eq ( value )
516
517
518
+ # Using a FFI::Function (v2) should be consistent with the direct callback (v1)
517
519
v2 = 0xdeadbeef
518
- fun = FFI ::Function . new ( :void , [ :uchar ] ) { |i | v2 = yield ( i ) }
520
+ fun = FFI ::Function . new ( :void , [ :uchar ] ) { |i | v2 = i }
519
521
LibTest . testCallbackU8rV ( fun , value )
520
- raise "FFI::Function (#{ v2 } ) not consistent with direct callback (#{ v1 } )" unless v1 == v2
521
-
522
- v1
522
+ expect ( v2 ) . to eq ( value )
523
523
end
524
524
525
525
it ":uchar (0) argument" do
526
- v = 0xdeadbeef
527
- testCallbackU8rV ( 0 ) { |i | v = i }
528
- expect ( v ) . to eq ( 0 )
526
+ testCallbackU8rV ( 0 )
529
527
end
530
528
531
529
it ":uchar (127) argument" do
532
- v = 0xdeadbeef
533
- testCallbackU8rV ( 127 ) { |i | v = i }
534
- expect ( v ) . to eq ( 127 )
530
+ testCallbackU8rV ( 127 )
535
531
end
536
532
537
533
it ":uchar (128) argument" do
538
- v = 0xdeadbeef
539
- testCallbackU8rV ( 128 ) { |i | v = i }
540
- expect ( v ) . to eq ( 128 )
534
+ testCallbackU8rV ( 128 )
541
535
end
542
536
543
537
it ":uchar (255) argument" do
544
- v = 0xdeadbeef
545
- testCallbackU8rV ( 255 ) { |i | v = i }
546
- expect ( v ) . to eq ( 255 )
538
+ testCallbackU8rV ( 255 )
547
539
end
548
540
549
541
it ":short (0) argument" do
@@ -794,3 +786,103 @@ module LibTestStdcall
794
786
end
795
787
end
796
788
end
789
+
790
+ describe "Callback interop" do
791
+ # require 'fiddle'
792
+ # require 'fiddle/import'
793
+ require 'timeout'
794
+
795
+ module LibTestFFI
796
+ extend FFI ::Library
797
+ ffi_lib TestLibrary ::PATH
798
+ attach_function :testCallbackVrV , :testClosureVrV , [ :pointer ] , :void
799
+ attach_function :testCallbackVrV_blocking , :testClosureVrV , [ :pointer ] , :void , blocking : true
800
+ end
801
+
802
+ # module LibTestFiddle
803
+ # extend Fiddle::Importer
804
+ # dlload TestLibrary::PATH
805
+ # extern 'void testClosureVrV(void *fp)'
806
+ # end
807
+
808
+ def assert_callback_in_same_thread_called_once
809
+ called = 0
810
+ thread = nil
811
+ yield proc {
812
+ called += 1
813
+ thread = Thread . current
814
+ }
815
+ expect ( called ) . to eq ( 1 )
816
+ expect ( thread ) . to eq ( Thread . current )
817
+ end
818
+
819
+ it "from ffi to ffi" do
820
+ assert_callback_in_same_thread_called_once do |block |
821
+ func = FFI ::Function . new ( :void , [ :pointer ] , &block )
822
+ LibTestFFI . testCallbackVrV ( FFI ::Pointer . new ( func . to_i ) )
823
+ end
824
+ end
825
+
826
+ it "from ffi to ffi with blocking:true" do
827
+ assert_callback_in_same_thread_called_once do |block |
828
+ func = FFI ::Function . new ( :void , [ :pointer ] , &block )
829
+ LibTestFFI . testCallbackVrV_blocking ( FFI ::Pointer . new ( func . to_i ) )
830
+ end
831
+ end
832
+
833
+ # https://github.com/ffi/ffi/issues/527
834
+ if RUBY_VERSION . split ( '.' ) . map ( &:to_i ) . pack ( "C*" ) >= [ 2 , 3 , 0 ] . pack ( "C*" ) || RUBY_PLATFORM =~ /java/
835
+ it "from fiddle to ffi" do
836
+ next # Fiddle
837
+ assert_callback_in_same_thread_called_once do |block |
838
+ func = FFI ::Function . new ( :void , [ :pointer ] , &block )
839
+ LibTestFiddle . testClosureVrV ( Fiddle ::Pointer [ func . to_i ] )
840
+ end
841
+ end
842
+ end
843
+
844
+ it "from ffi to fiddle" do
845
+ next # Fiddle
846
+ assert_callback_in_same_thread_called_once do |block |
847
+ func = LibTestFiddle . bind_function ( :cbVrV , Fiddle ::TYPE_VOID , [ ] , &block )
848
+ LibTestFFI . testCallbackVrV ( FFI ::Pointer . new ( func . to_i ) )
849
+ end
850
+ end
851
+
852
+ it "from ffi to fiddle with blocking:true" do
853
+ next # Fiddle
854
+ assert_callback_in_same_thread_called_once do |block |
855
+ func = LibTestFiddle . bind_function ( :cbVrV , Fiddle ::TYPE_VOID , [ ] , &block )
856
+ LibTestFFI . testCallbackVrV_blocking ( FFI ::Pointer . new ( func . to_i ) )
857
+ end
858
+ end
859
+
860
+ it "from fiddle to fiddle" do
861
+ next # Fiddle
862
+ assert_callback_in_same_thread_called_once do |block |
863
+ func = LibTestFiddle . bind_function ( :cbVrV , Fiddle ::TYPE_VOID , [ ] , &block )
864
+ LibTestFiddle . testClosureVrV ( Fiddle ::Pointer [ func . to_i ] )
865
+ end
866
+ end
867
+
868
+ # https://github.com/ffi/ffi/issues/527
869
+ if RUBY_ENGINE == 'ruby' && RUBY_VERSION . split ( '.' ) . map ( &:to_i ) . pack ( "C*" ) >= [ 2 , 3 , 0 ] . pack ( "C*" )
870
+ it "C outside ffi call stack does not deadlock [#527]" do
871
+ next # takes a while, fails on MRI
872
+ path = File . join ( File . dirname ( __FILE__ ) , "embed-test/embed-test.rb" )
873
+ pid = spawn ( RbConfig . ruby , "-Ilib" , path , { [ :out , :err ] => "embed-test.log" } )
874
+ begin
875
+ Timeout . timeout ( 10 ) { Process . wait ( pid ) }
876
+ rescue Timeout ::Error
877
+ Process . kill ( 9 , pid )
878
+ raise
879
+ else
880
+ if $?. exitstatus != 0
881
+ raise "external process failed:\n #{ File . read ( "embed-test.log" ) } "
882
+ end
883
+ end
884
+
885
+ expect ( File . read ( "embed-test.log" ) ) . to match ( /callback called with \[ "hello", 5, 0\] / )
886
+ end
887
+ end
888
+ end
0 commit comments