Ref: https://bugs.ruby-lang.org/issues/20311
C global variable that contain references to Ruby object MUST be declared to the Ruby GC to prevent these objects from being collected or moved.
There are a few exceptions to that, such as classes defined using the C APIs such as `rb_define_class` etc.
Up to Ruby 3.4 however, there was a bug that caused classes created from Ruby with the `Struct.new("Name")` API to also be marked as immortal and immovable.
GRPC has been relying on this bug, which I fixed in Ruby 3.4, and now GRPC is crashing when `Struct::Status` is moved around by the GC.
```
-- C level backtrace information -------------------------------------------
ruby(rb_print_backtrace+0x14) [0x5577db219d41] ruby-3.4.0/vm_dump.c:820
ruby(rb_vm_bugreport) ruby-3.4.0/vm_dump.c:1151
ruby(rb_bug_for_fatal_signal+0xfc) [0x5577db3cc60c] ruby-3.4.0/error.c:1066
ruby(sigsegv+0x4d) [0x5577db16358d] ruby-3.4.0/signal.c:926
libc.so.6(0x7f5bacd32520) [0x7f5bacd32520]
ruby(rb_class_superclass+0x32) [0x5577db0a9152] ruby-3.4.0/object.c:2239
ruby(struct_ivar_get+0x2a) [0x5577db194e02] ruby-3.4.0/struct.c:49
ruby(struct_ivar_get) ruby-3.4.0/struct.c:40
ruby(num_members) ruby-3.4.0/struct.c:705
ruby(rb_struct_new+0x56) [0x5577db19a9d6] ruby-3.4.0/struct.c:848
lib/grpc/grpc_c.so(grpc_run_batch_stack_build_result+0xe6) [0x7f5b84bb6b96] /tmp/bundle/ruby/3.4.0+0/bundler/gems/grpc-5ed33ee673e3/src/ruby/ext/grpc/rb_call.c:780
lib/grpc/grpc_c.so(grpc_rb_call_run_batch_try) /tmp/bundle/ruby/3.4.0+0/bundler/gems/grpc-5ed33ee673e3/src/ruby/ext/grpc/rb_call.c:839
ruby(rb_ensure+0x10c) [0x5577db007d3c] ruby-3.4.0/eval.c:1000
/tmp/bundle/ruby/3.4.0+0/bundler/gems/grpc-5ed33ee673e3/src/ruby/lib/grpc/grpc_c.so(grpc_rb_call_run_batch+0xca) [0x7f5b84bb595a] /tmp/bundle/ruby/3.4.0+0/bundler/gems/grpc-5ed33ee673e3/src/ruby/ext/grpc/rb_call.c:893
ruby(vm_call_cfunc_with_frame_+0x117) [0x5577db1f4c77] ruby-3.4.0/vm_insnhelper.c:3524
```
cc @apolcyn @peterzhu2118 @k0kubun
Closes #36125
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/36125 from Shopify:fix-ruby-3.4-compat
|
||
|---|---|---|
| .. | ||
| bin | ||
| end2end | ||
| ext/grpc | ||
| lib | ||
| nativedebug | ||
| pb | ||
| qps | ||
| spec | ||
| stress | ||
| tools | ||
| .gitignore | ||
| .rubocop.yml | ||
| .rubocop_todo.yml | ||
| CHANGELOG.md | ||
| README.md | ||
README.md
gRPC Ruby
A Ruby implementation of gRPC.
PREREQUISITES
- Ruby 2.x. The gRPC API uses keyword args.
INSTALLATION
Linux and Mac OS X:
gem install grpc
If using a Gemfile and you wish to pull from a git repository or GitHub, make sure to tell bundler to retrieve submodules:
gem 'grpc', github: 'grpc/grpc', submodules: true
BUILD FROM SOURCE
-
Clone this repository
-
Init submodules
git submodule update --init
- Install Ruby 2.x. Consider doing this with RVM, it's a nice way of controlling the exact ruby version that's used.
$ command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
$ \curl -sSL https://get.rvm.io | bash -s stable --ruby=ruby-2
$
$ # follow the instructions to ensure that your're using the latest stable version of Ruby
$ # and that the rvm command is installed
-
Make sure your run
source $HOME/.rvm/scripts/rvmas instructed to complete the set up of RVM -
Install bundler
$ gem install bundler
- Finally, build and install the gRPC gem locally.
$ # from this directory
$ bundle install # creates the ruby bundle, including building the grpc extension
$ rake # runs the unit tests, see rake -T for other options
DOCUMENTATION
- rubydoc for the gRPC gem is available online at rubydoc.
- the gRPC Ruby reference documentation is available online at grpc.io
CONTENTS
Directory structure is the layout for ruby extensions
-
ext: the gRPC ruby extension
-
lib: the entrypoint gRPC ruby library to be used in a 'require' statement
-
spec: Rspec unittests
-
bin: example gRPC clients and servers, e.g,
stub = Math::Math::Stub.new('my.test.math.server.com:8080', :this_channel_is_insecure) req = Math::DivArgs.new(dividend: 7, divisor: 3) GRPC.logger.info("div(7/3): req=#{req.inspect}") resp = stub.div(req) GRPC.logger.info("Answer: #{resp.inspect}")