rutie icon indicating copy to clipboard operation
rutie copied to clipboard

Is Rutie#init necessary?

Open ball-hayden opened this issue 3 years ago • 4 comments

Thank you for the project, and apologies for the Q&A/Discussion issue. Rutie's really interesting, and has helped myself and @saty9 get a Ruby/Rust integration going pretty quickly.

An issue we've come across is that Rails becomes very upset when loading a gem that has a call out to Rutie#init (specifically, we get Bus Errors). I suspect the issue is with Spring's forking, but it could also be an issue with Zeitwerk, or some of Rails' other loading "magic".

This has led us to investigate a bit further about Ruby's initialisation of native extensions. What we've found is that Ruby will call an initialisation function when requireing a shared library, removing the need for Rutie#init [source].

For example, we can adapt the example in https://github.com/danielpclark/rutie/tree/master/examples/rutie_ruby_example slightly and get the correct behaviour:

--- a/examples/rutie_ruby_example/Rakefile
+++ b/examples/rutie_ruby_example/Rakefile
@@ -18,6 +18,8 @@ task :build_lib do
   else
     sh 'cargo build --release'
   end
+
+  sh "mv target/release/librutie_ruby_example.#{RbConfig::CONFIG['SOEXT']} lib/rutie_ruby_example/rutie_ruby_example_ext.#{RbConfig::CONFIG['DLEXT']}"
 end

 desc 'bundle install'
diff --git a/examples/rutie_ruby_example/lib/rutie_ruby_example.rb b/examples/rutie_ruby_example/lib/rutie_ruby_example.rb
index e0483a0..cd73617 100644
--- a/examples/rutie_ruby_example/lib/rutie_ruby_example.rb
+++ b/examples/rutie_ruby_example/lib/rutie_ruby_example.rb
@@ -1,6 +1,6 @@
 require 'rutie_ruby_example/version'
-require 'rutie'
+require 'rutie_ruby_example/rutie_ruby_example_ext'
+

 module RutieRubyExample
-  Rutie.new(:rutie_ruby_example).init 'Init_rutie_ruby_example', __dir__
 end
diff --git a/examples/rutie_ruby_example/src/lib.rs b/examples/rutie_ruby_example/src/lib.rs
index 7f79ca8..d6d16e0 100644
--- a/examples/rutie_ruby_example/src/lib.rs
+++ b/examples/rutie_ruby_example/src/lib.rs
@@ -26,7 +26,7 @@ methods!(

 #[allow(non_snake_case)]
 #[no_mangle]
-pub extern "C" fn Init_rutie_ruby_example() {
+pub extern "C" fn Init_rutie_ruby_example_ext() {
     Class::new("RutieExample", None).define(|klass| {
         klass.def_self("reverse", pub_reverse);
     });
cd examples/rutie_ruby_example
bundle exec rake
<snip>
RutieRubyExampleTest
 PASS (0.00s) :: test_it_reverses

Finished in 0.00085s
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

This seems to play more nicely with Rails. My question is: is this safe, or are we missing something?

I'm happy to open a PR if it is safe and you'd like to adopt this pattern.

ball-hayden avatar Apr 07 '21 20:04 ball-hayden