aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'src/ruby')
-rwxr-xr-xsrc/ruby/end2end/forking_client_client.rb69
-rwxr-xr-xsrc/ruby/end2end/forking_client_driver.rb69
-rwxr-xr-xsrc/ruby/end2end/grpc_class_init_client.rb77
-rwxr-xr-xsrc/ruby/end2end/grpc_class_init_driver.rb67
-rw-r--r--src/ruby/ext/grpc/extconf.rb3
-rw-r--r--src/ruby/ext/grpc/rb_call.c2
-rw-r--r--src/ruby/ext/grpc/rb_call_credentials.c4
-rw-r--r--src/ruby/ext/grpc/rb_channel.c75
-rw-r--r--src/ruby/ext/grpc/rb_channel.h2
-rw-r--r--src/ruby/ext/grpc/rb_channel_credentials.c3
-rw-r--r--src/ruby/ext/grpc/rb_compression_options.c7
-rw-r--r--src/ruby/ext/grpc/rb_grpc.c21
-rw-r--r--src/ruby/ext/grpc/rb_grpc.h2
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.c18
-rw-r--r--src/ruby/ext/grpc/rb_grpc_imports.generated.h27
-rw-r--r--src/ruby/ext/grpc/rb_server.c6
16 files changed, 419 insertions, 33 deletions
diff --git a/src/ruby/end2end/forking_client_client.rb b/src/ruby/end2end/forking_client_client.rb
new file mode 100755
index 0000000000..c358d79f59
--- /dev/null
+++ b/src/ruby/end2end/forking_client_client.rb
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby
+
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Prompted by and minimal repro of https://github.com/grpc/grpc/issues/10658
+
+require_relative './end2end_common'
+
+def main
+ server_port = ''
+ OptionParser.new do |opts|
+ opts.on('--client_control_port=P', String) do
+ STDERR.puts 'client control port not used'
+ end
+ opts.on('--server_port=P', String) do |p|
+ server_port = p
+ end
+ end.parse!
+
+ p = fork do
+ stub = Echo::EchoServer::Stub.new("localhost:#{server_port}",
+ :this_channel_is_insecure)
+ stub.echo(Echo::EchoRequest.new(request: 'hello'))
+ end
+
+ begin
+ Timeout.timeout(10) do
+ Process.wait(p)
+ end
+ rescue Timeout::Error
+ STDERR.puts "timeout waiting for forked process #{p}"
+ Process.kill('SIGKILL', p)
+ Process.wait(p)
+ raise 'Timed out waiting for client process. ' \
+ 'It likely hangs when using gRPC after loading it and then forking'
+ end
+
+ client_exit_code = $CHILD_STATUS
+ fail "forked process failed #{client_exit_code}" if client_exit_code != 0
+end
+
+main
diff --git a/src/ruby/end2end/forking_client_driver.rb b/src/ruby/end2end/forking_client_driver.rb
new file mode 100755
index 0000000000..2c67b33590
--- /dev/null
+++ b/src/ruby/end2end/forking_client_driver.rb
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby
+
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require_relative './end2end_common'
+
+def main
+ STDERR.puts 'start server'
+ server_runner = ServerRunner.new(EchoServerImpl)
+ server_port = server_runner.run
+
+ # TODO(apolcyn) Can we get rid of this sleep?
+ # Without it, an immediate call to the just started EchoServer
+ # fails with UNAVAILABLE
+ sleep 1
+
+ STDERR.puts 'start client'
+ _, client_pid = start_client('forking_client_client.rb',
+ server_port)
+
+ begin
+ Timeout.timeout(10) do
+ Process.wait(client_pid)
+ end
+ rescue Timeout::Error
+ STDERR.puts "timeout wait for client pid #{client_pid}"
+ Process.kill('SIGKILL', client_pid)
+ Process.wait(client_pid)
+ STDERR.puts 'killed client child'
+ raise 'Timed out waiting for client process. ' \
+ 'It likely hangs when requiring grpc, then forking, then using grpc '
+ end
+
+ client_exit_code = $CHILD_STATUS
+ if client_exit_code != 0
+ fail "forking client client failed, exit code #{client_exit_code}"
+ end
+
+ server_runner.stop
+end
+
+main
diff --git a/src/ruby/end2end/grpc_class_init_client.rb b/src/ruby/end2end/grpc_class_init_client.rb
new file mode 100755
index 0000000000..ee79292119
--- /dev/null
+++ b/src/ruby/end2end/grpc_class_init_client.rb
@@ -0,0 +1,77 @@
+#!/usr/bin/env ruby
+
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# For GRPC::Core classes, which use the grpc c-core, object init
+# is interesting because it's related to overall library init.
+
+require_relative './end2end_common'
+
+def main
+ grpc_class = ''
+ OptionParser.new do |opts|
+ opts.on('--grpc_class=P', String) do |p|
+ grpc_class = p
+ end
+ end.parse!
+
+ test_proc = nil
+
+ case grpc_class
+ when 'channel'
+ test_proc = proc do
+ GRPC::Core::Channel.new('dummy_host', nil, :this_channel_is_insecure)
+ end
+ when 'server'
+ test_proc = proc do
+ GRPC::Core::Server.new({})
+ end
+ when 'channel_credentials'
+ test_proc = proc do
+ GRPC::Core::ChannelCredentials.new
+ end
+ when 'call_credentials'
+ test_proc = proc do
+ GRPC::Core::CallCredentials.new(proc { |noop| noop })
+ end
+ when 'compression_options'
+ test_proc = proc do
+ GRPC::Core::CompressionOptions.new
+ end
+ else
+ fail "bad --grpc_class=#{grpc_class} param"
+ end
+
+ th = Thread.new { test_proc.call }
+ test_proc.call
+ th.join
+end
+
+main
diff --git a/src/ruby/end2end/grpc_class_init_driver.rb b/src/ruby/end2end/grpc_class_init_driver.rb
new file mode 100755
index 0000000000..764d029f14
--- /dev/null
+++ b/src/ruby/end2end/grpc_class_init_driver.rb
@@ -0,0 +1,67 @@
+#!/usr/bin/env ruby
+
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require_relative './end2end_common'
+
+def main
+ native_grpc_classes = %w( channel
+ server
+ channel_credentials
+ call_credentials
+ compression_options )
+
+ native_grpc_classes.each do |grpc_class|
+ STDERR.puts 'start client'
+ this_dir = File.expand_path(File.dirname(__FILE__))
+ client_path = File.join(this_dir, 'grpc_class_init_client.rb')
+ client_pid = Process.spawn(RbConfig.ruby,
+ client_path,
+ "--grpc_class=#{grpc_class}")
+ begin
+ Timeout.timeout(10) do
+ Process.wait(client_pid)
+ end
+ rescue Timeout::Error
+ STDERR.puts "timeout waiting for client pid #{client_pid}"
+ Process.kill('SIGKILL', client_pid)
+ Process.wait(client_pid)
+ STDERR.puts 'killed client child'
+ raise 'Timed out waiting for client process. ' \
+ 'It likely hangs when the first constructed gRPC object has ' \
+ "type: #{grpc_class}"
+ end
+
+ client_exit_code = $CHILD_STATUS
+ fail "client failed, exit code #{client_exit_code}" if client_exit_code != 0
+ end
+end
+
+main
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 7067933832..6c0486f1a8 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -65,6 +65,7 @@ ENV['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
ENV['AR'] = RbConfig::CONFIG['AR'] + ' rcs'
ENV['CC'] = RbConfig::CONFIG['CC']
+ENV['CXX'] = RbConfig::CONFIG['CXX']
ENV['LD'] = ENV['CC']
ENV['AR'] = 'libtool -o' if RUBY_PLATFORM =~ /darwin/
@@ -84,7 +85,7 @@ unless windows
puts 'Building internal gRPC into ' + grpc_lib_dir
nproc = 4
nproc = Etc.nprocessors * 2 if Etc.respond_to? :nprocessors
- system("make -j#{nproc} -C #{grpc_root} #{grpc_lib_dir}/libgrpc.a CONFIG=#{grpc_config}")
+ system("make -j#{nproc} -C #{grpc_root} #{grpc_lib_dir}/libgrpc.a CONFIG=#{grpc_config} Q=")
exit 1 unless $? == 0
end
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index aef7175af9..0067e10866 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -101,7 +101,7 @@ typedef struct grpc_rb_call {
static void destroy_call(grpc_rb_call *call) {
/* Ensure that we only try to destroy the call once */
if (call->wrapped != NULL) {
- grpc_call_destroy(call->wrapped);
+ grpc_call_unref(call->wrapped);
call->wrapped = NULL;
grpc_rb_completion_queue_destroy(call->queue);
call->queue = NULL;
diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c
index d6545502b0..7a5a74580d 100644
--- a/src/ruby/ext/grpc/rb_call_credentials.c
+++ b/src/ruby/ext/grpc/rb_call_credentials.c
@@ -223,6 +223,8 @@ static VALUE grpc_rb_call_credentials_init(VALUE self, VALUE proc) {
grpc_call_credentials *creds = NULL;
grpc_metadata_credentials_plugin plugin;
+ grpc_ruby_once_init();
+
TypedData_Get_Struct(self, grpc_rb_call_credentials,
&grpc_rb_call_credentials_data_type, wrapper);
@@ -283,8 +285,6 @@ void Init_grpc_call_credentials() {
grpc_rb_call_credentials_compose, -1);
id_callback = rb_intern("__callback");
-
- grpc_rb_event_queue_thread_start();
}
/* Gets the wrapped grpc_call_credentials from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index ecfffd1839..a802183726 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -89,10 +89,14 @@ typedef struct grpc_rb_channel {
static void grpc_rb_channel_try_register_connection_polling(
grpc_rb_channel *wrapper);
static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper);
+static void *wait_until_channel_polling_thread_started_no_gil(void *);
+static void wait_until_channel_polling_thread_started_unblocking_func(void *);
static grpc_completion_queue *channel_polling_cq;
static gpr_mu global_connection_polling_mu;
+static gpr_cv global_connection_polling_cv;
static int abort_channel_polling = 0;
+static int channel_polling_thread_started = 0;
/* Destroys Channel instances. */
static void grpc_rb_channel_free(void *p) {
@@ -166,6 +170,11 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
grpc_channel_args args;
MEMZERO(&args, grpc_channel_args, 1);
+ grpc_ruby_once_init();
+ rb_thread_call_without_gvl(
+ wait_until_channel_polling_thread_started_no_gil, NULL,
+ wait_until_channel_polling_thread_started_unblocking_func, NULL);
+
/* "3" == 3 mandatory args */
rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials);
@@ -440,6 +449,7 @@ static void grpc_rb_channel_try_register_connection_polling(
}
gpr_mu_lock(&global_connection_polling_mu);
+ GPR_ASSERT(channel_polling_thread_started || abort_channel_polling);
conn_state = grpc_channel_check_connectivity_state(wrapper->wrapped, 0);
if (conn_state != wrapper->current_connectivity_state) {
wrapper->current_connectivity_state = conn_state;
@@ -473,7 +483,7 @@ static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) {
}
// Note this loop breaks out with a single call of
-// "grpc_rb_event_unblocking_func".
+// "run_poll_channels_loop_no_gil".
// This assumes that a ruby call the unblocking func
// indicates process shutdown.
// In the worst case, this stops polling channel connectivity
@@ -481,6 +491,14 @@ static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) {
static void *run_poll_channels_loop_no_gil(void *arg) {
grpc_event event;
(void)arg;
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - begin");
+
+ gpr_mu_lock(&global_connection_polling_mu);
+ GPR_ASSERT(!channel_polling_thread_started);
+ channel_polling_thread_started = 1;
+ gpr_cv_broadcast(&global_connection_polling_cv);
+ gpr_mu_unlock(&global_connection_polling_mu);
+
for (;;) {
event = grpc_completion_queue_next(
channel_polling_cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
@@ -500,7 +518,7 @@ static void *run_poll_channels_loop_no_gil(void *arg) {
}
// Notify the channel polling loop to cleanup and shutdown.
-static void grpc_rb_event_unblocking_func(void *arg) {
+static void run_poll_channels_loop_unblocking_func(void *arg) {
(void)arg;
gpr_mu_lock(&global_connection_polling_mu);
gpr_log(GPR_DEBUG,
@@ -518,10 +536,37 @@ static VALUE run_poll_channels_loop(VALUE arg) {
GPR_DEBUG,
"GRPC_RUBY: run_poll_channels_loop - create connection polling thread");
rb_thread_call_without_gvl(run_poll_channels_loop_no_gil, NULL,
- grpc_rb_event_unblocking_func, NULL);
+ run_poll_channels_loop_unblocking_func, NULL);
+
return Qnil;
}
+static void *wait_until_channel_polling_thread_started_no_gil(void *arg) {
+ (void)arg;
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: wait for channel polling thread to start");
+ gpr_mu_lock(&global_connection_polling_mu);
+ while (!channel_polling_thread_started && !abort_channel_polling) {
+ gpr_cv_wait(&global_connection_polling_cv, &global_connection_polling_mu,
+ gpr_inf_future(GPR_CLOCK_REALTIME));
+ }
+ gpr_mu_unlock(&global_connection_polling_mu);
+
+ return NULL;
+}
+
+static void wait_until_channel_polling_thread_started_unblocking_func(
+ void *arg) {
+ (void)arg;
+ gpr_mu_lock(&global_connection_polling_mu);
+ gpr_log(GPR_DEBUG,
+ "GRPC_RUBY: "
+ "wait_until_channel_polling_thread_started_unblocking_func - begin "
+ "aborting connection polling");
+ abort_channel_polling = 1;
+ gpr_cv_broadcast(&global_connection_polling_cv);
+ gpr_mu_unlock(&global_connection_polling_mu);
+}
+
/* Temporary fix for
* https://github.com/GoogleCloudPlatform/google-cloud-ruby/issues/899.
* Transports in idle channels can get destroyed. Normally c-core re-connects,
@@ -532,11 +577,26 @@ static VALUE run_poll_channels_loop(VALUE arg) {
* calls - so that c-core can reconnect if needed, when there aren't any RPC's.
* TODO(apolcyn) remove this when core handles new RPCs on dead connections.
*/
-static void start_poll_channels_loop() {
- channel_polling_cq = grpc_completion_queue_create_for_next(NULL);
+void grpc_rb_channel_polling_thread_start() {
+ VALUE background_thread = Qnil;
+
+ GPR_ASSERT(!abort_channel_polling);
+ GPR_ASSERT(!channel_polling_thread_started);
+ GPR_ASSERT(channel_polling_cq == NULL);
+
gpr_mu_init(&global_connection_polling_mu);
- abort_channel_polling = 0;
- rb_thread_create(run_poll_channels_loop, NULL);
+ gpr_cv_init(&global_connection_polling_cv);
+
+ channel_polling_cq = grpc_completion_queue_create_for_next(NULL);
+ background_thread = rb_thread_create(run_poll_channels_loop, NULL);
+
+ if (!RTEST(background_thread)) {
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: failed to spawn channel polling thread");
+ gpr_mu_lock(&global_connection_polling_mu);
+ abort_channel_polling = 1;
+ gpr_cv_broadcast(&global_connection_polling_cv);
+ gpr_mu_unlock(&global_connection_polling_mu);
+ }
}
static void Init_grpc_propagate_masks() {
@@ -608,7 +668,6 @@ void Init_grpc_channel() {
id_insecure_channel = rb_intern("this_channel_is_insecure");
Init_grpc_propagate_masks();
Init_grpc_connectivity_states();
- start_poll_channels_loop();
}
/* Gets the wrapped channel from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_channel.h b/src/ruby/ext/grpc/rb_channel.h
index 77e1f6acbc..fdceb79bca 100644
--- a/src/ruby/ext/grpc/rb_channel.h
+++ b/src/ruby/ext/grpc/rb_channel.h
@@ -41,6 +41,8 @@
/* Initializes the Channel class. */
void Init_grpc_channel();
+void grpc_rb_channel_polling_thread_start();
+
/* Gets the wrapped channel from the ruby wrapper */
grpc_channel* grpc_rb_get_wrapped_channel(VALUE v);
diff --git a/src/ruby/ext/grpc/rb_channel_credentials.c b/src/ruby/ext/grpc/rb_channel_credentials.c
index f30426b26d..db713ed821 100644
--- a/src/ruby/ext/grpc/rb_channel_credentials.c
+++ b/src/ruby/ext/grpc/rb_channel_credentials.c
@@ -161,6 +161,9 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE *argv,
grpc_ssl_pem_key_cert_pair key_cert_pair;
const char *pem_root_certs_cstr = NULL;
MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1);
+
+ grpc_ruby_once_init();
+
/* "03" == no mandatory arg, 3 optional */
rb_scan_args(argc, argv, "03", &pem_root_certs, &pem_private_key,
&pem_cert_chain);
diff --git a/src/ruby/ext/grpc/rb_compression_options.c b/src/ruby/ext/grpc/rb_compression_options.c
index 7de3c3c528..45c963dca6 100644
--- a/src/ruby/ext/grpc/rb_compression_options.c
+++ b/src/ruby/ext/grpc/rb_compression_options.c
@@ -100,8 +100,11 @@ static rb_data_type_t grpc_rb_compression_options_data_type = {
Allocate the wrapped grpc compression options and
initialize it here too. */
static VALUE grpc_rb_compression_options_alloc(VALUE cls) {
- grpc_rb_compression_options *wrapper =
- gpr_malloc(sizeof(grpc_rb_compression_options));
+ grpc_rb_compression_options *wrapper = NULL;
+
+ grpc_ruby_once_init();
+
+ wrapper = gpr_malloc(sizeof(grpc_rb_compression_options));
wrapper->wrapped = NULL;
wrapper->wrapped = gpr_malloc(sizeof(grpc_compression_options));
grpc_compression_options_init(wrapper->wrapped);
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index d45a5eaef8..5be8861e0c 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -47,6 +47,7 @@
#include "rb_channel.h"
#include "rb_channel_credentials.h"
#include "rb_compression_options.h"
+#include "rb_event_thread.h"
#include "rb_loader.h"
#include "rb_server.h"
#include "rb_server_credentials.h"
@@ -289,17 +290,14 @@ VALUE sym_metadata = Qundef;
static gpr_once g_once_init = GPR_ONCE_INIT;
-static void grpc_ruby_once_init() {
+static void grpc_ruby_once_init_internal() {
grpc_init();
+ grpc_rb_event_queue_thread_start();
+ grpc_rb_channel_polling_thread_start();
atexit(grpc_rb_shutdown);
}
-void Init_grpc_c() {
- if (!grpc_rb_load_core()) {
- rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core");
- return;
- }
-
+void grpc_ruby_once_init() {
/* ruby_vm_at_exit doesn't seem to be working. It would crash once every
* blue moon, and some users are getting it repeatedly. See the discussions
* - https://github.com/grpc/grpc/pull/5337
@@ -310,7 +308,14 @@ void Init_grpc_c() {
* then loaded again by another VM within the same process, we need to
* schedule our initialization and destruction only once.
*/
- gpr_once_init(&g_once_init, grpc_ruby_once_init);
+ gpr_once_init(&g_once_init, grpc_ruby_once_init_internal);
+}
+
+void Init_grpc_c() {
+ if (!grpc_rb_load_core()) {
+ rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core");
+ return;
+ }
grpc_rb_mGRPC = rb_define_module("GRPC");
grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
index 2ee1faaca5..8538a74211 100644
--- a/src/ruby/ext/grpc/rb_grpc.h
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -82,4 +82,6 @@ VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self);
/* grpc_rb_time_timeval creates a gpr_timespec from a ruby time object. */
gpr_timespec grpc_rb_time_timeval(VALUE time, int interval);
+void grpc_ruby_once_init();
+
#endif /* GRPC_RB_H_ */
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index f189e88198..221a1e14ec 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -108,6 +108,7 @@ grpc_channel_create_call_type grpc_channel_create_call_import;
grpc_channel_ping_type grpc_channel_ping_import;
grpc_channel_register_call_type grpc_channel_register_call_import;
grpc_channel_create_registered_call_type grpc_channel_create_registered_call_import;
+grpc_call_arena_alloc_type grpc_call_arena_alloc_import;
grpc_call_start_batch_type grpc_call_start_batch_import;
grpc_call_get_peer_type grpc_call_get_peer_import;
grpc_census_call_set_context_type grpc_census_call_set_context_import;
@@ -119,13 +120,13 @@ grpc_lame_client_channel_create_type grpc_lame_client_channel_create_import;
grpc_channel_destroy_type grpc_channel_destroy_import;
grpc_call_cancel_type grpc_call_cancel_import;
grpc_call_cancel_with_status_type grpc_call_cancel_with_status_import;
-grpc_call_destroy_type grpc_call_destroy_import;
+grpc_call_ref_type grpc_call_ref_import;
+grpc_call_unref_type grpc_call_unref_import;
grpc_server_request_call_type grpc_server_request_call_import;
grpc_server_register_method_type grpc_server_register_method_import;
grpc_server_request_registered_call_type grpc_server_request_registered_call_import;
grpc_server_create_type grpc_server_create_import;
grpc_server_register_completion_queue_type grpc_server_register_completion_queue_import;
-grpc_server_register_non_listening_completion_queue_type grpc_server_register_non_listening_completion_queue_import;
grpc_server_add_insecure_http2_port_type grpc_server_add_insecure_http2_port_import;
grpc_server_start_type grpc_server_start_import;
grpc_server_shutdown_and_notify_type grpc_server_shutdown_and_notify_import;
@@ -178,10 +179,12 @@ grpc_call_set_credentials_type grpc_call_set_credentials_import;
grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
grpc_slice_ref_type grpc_slice_ref_import;
grpc_slice_unref_type grpc_slice_unref_import;
+grpc_slice_copy_type grpc_slice_copy_import;
grpc_slice_new_type grpc_slice_new_import;
grpc_slice_new_with_user_data_type grpc_slice_new_with_user_data_import;
grpc_slice_new_with_len_type grpc_slice_new_with_len_import;
grpc_slice_malloc_type grpc_slice_malloc_import;
+grpc_slice_malloc_large_type grpc_slice_malloc_large_import;
grpc_slice_intern_type grpc_slice_intern_import;
grpc_slice_from_copied_string_type grpc_slice_from_copied_string_import;
grpc_slice_from_copied_buffer_type grpc_slice_from_copied_buffer_import;
@@ -190,6 +193,7 @@ grpc_slice_from_static_buffer_type grpc_slice_from_static_buffer_import;
grpc_slice_sub_type grpc_slice_sub_import;
grpc_slice_sub_no_ref_type grpc_slice_sub_no_ref_import;
grpc_slice_split_tail_type grpc_slice_split_tail_import;
+grpc_slice_split_tail_maybe_ref_type grpc_slice_split_tail_maybe_ref_import;
grpc_slice_split_head_type grpc_slice_split_head_import;
grpc_empty_slice_type grpc_empty_slice_import;
grpc_slice_default_hash_impl_type grpc_slice_default_hash_impl_import;
@@ -218,6 +222,7 @@ grpc_slice_buffer_swap_type grpc_slice_buffer_swap_import;
grpc_slice_buffer_move_into_type grpc_slice_buffer_move_into_import;
grpc_slice_buffer_trim_end_type grpc_slice_buffer_trim_end_import;
grpc_slice_buffer_move_first_type grpc_slice_buffer_move_first_import;
+grpc_slice_buffer_move_first_no_ref_type grpc_slice_buffer_move_first_no_ref_import;
grpc_slice_buffer_move_first_into_buffer_type grpc_slice_buffer_move_first_into_buffer_import;
grpc_slice_buffer_take_first_type grpc_slice_buffer_take_first_import;
grpc_slice_buffer_undo_take_first_type grpc_slice_buffer_undo_take_first_import;
@@ -404,6 +409,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_channel_ping_import = (grpc_channel_ping_type) GetProcAddress(library, "grpc_channel_ping");
grpc_channel_register_call_import = (grpc_channel_register_call_type) GetProcAddress(library, "grpc_channel_register_call");
grpc_channel_create_registered_call_import = (grpc_channel_create_registered_call_type) GetProcAddress(library, "grpc_channel_create_registered_call");
+ grpc_call_arena_alloc_import = (grpc_call_arena_alloc_type) GetProcAddress(library, "grpc_call_arena_alloc");
grpc_call_start_batch_import = (grpc_call_start_batch_type) GetProcAddress(library, "grpc_call_start_batch");
grpc_call_get_peer_import = (grpc_call_get_peer_type) GetProcAddress(library, "grpc_call_get_peer");
grpc_census_call_set_context_import = (grpc_census_call_set_context_type) GetProcAddress(library, "grpc_census_call_set_context");
@@ -415,13 +421,13 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_channel_destroy_import = (grpc_channel_destroy_type) GetProcAddress(library, "grpc_channel_destroy");
grpc_call_cancel_import = (grpc_call_cancel_type) GetProcAddress(library, "grpc_call_cancel");
grpc_call_cancel_with_status_import = (grpc_call_cancel_with_status_type) GetProcAddress(library, "grpc_call_cancel_with_status");
- grpc_call_destroy_import = (grpc_call_destroy_type) GetProcAddress(library, "grpc_call_destroy");
+ grpc_call_ref_import = (grpc_call_ref_type) GetProcAddress(library, "grpc_call_ref");
+ grpc_call_unref_import = (grpc_call_unref_type) GetProcAddress(library, "grpc_call_unref");
grpc_server_request_call_import = (grpc_server_request_call_type) GetProcAddress(library, "grpc_server_request_call");
grpc_server_register_method_import = (grpc_server_register_method_type) GetProcAddress(library, "grpc_server_register_method");
grpc_server_request_registered_call_import = (grpc_server_request_registered_call_type) GetProcAddress(library, "grpc_server_request_registered_call");
grpc_server_create_import = (grpc_server_create_type) GetProcAddress(library, "grpc_server_create");
grpc_server_register_completion_queue_import = (grpc_server_register_completion_queue_type) GetProcAddress(library, "grpc_server_register_completion_queue");
- grpc_server_register_non_listening_completion_queue_import = (grpc_server_register_non_listening_completion_queue_type) GetProcAddress(library, "grpc_server_register_non_listening_completion_queue");
grpc_server_add_insecure_http2_port_import = (grpc_server_add_insecure_http2_port_type) GetProcAddress(library, "grpc_server_add_insecure_http2_port");
grpc_server_start_import = (grpc_server_start_type) GetProcAddress(library, "grpc_server_start");
grpc_server_shutdown_and_notify_import = (grpc_server_shutdown_and_notify_type) GetProcAddress(library, "grpc_server_shutdown_and_notify");
@@ -474,10 +480,12 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_server_credentials_set_auth_metadata_processor_import = (grpc_server_credentials_set_auth_metadata_processor_type) GetProcAddress(library, "grpc_server_credentials_set_auth_metadata_processor");
grpc_slice_ref_import = (grpc_slice_ref_type) GetProcAddress(library, "grpc_slice_ref");
grpc_slice_unref_import = (grpc_slice_unref_type) GetProcAddress(library, "grpc_slice_unref");
+ grpc_slice_copy_import = (grpc_slice_copy_type) GetProcAddress(library, "grpc_slice_copy");
grpc_slice_new_import = (grpc_slice_new_type) GetProcAddress(library, "grpc_slice_new");
grpc_slice_new_with_user_data_import = (grpc_slice_new_with_user_data_type) GetProcAddress(library, "grpc_slice_new_with_user_data");
grpc_slice_new_with_len_import = (grpc_slice_new_with_len_type) GetProcAddress(library, "grpc_slice_new_with_len");
grpc_slice_malloc_import = (grpc_slice_malloc_type) GetProcAddress(library, "grpc_slice_malloc");
+ grpc_slice_malloc_large_import = (grpc_slice_malloc_large_type) GetProcAddress(library, "grpc_slice_malloc_large");
grpc_slice_intern_import = (grpc_slice_intern_type) GetProcAddress(library, "grpc_slice_intern");
grpc_slice_from_copied_string_import = (grpc_slice_from_copied_string_type) GetProcAddress(library, "grpc_slice_from_copied_string");
grpc_slice_from_copied_buffer_import = (grpc_slice_from_copied_buffer_type) GetProcAddress(library, "grpc_slice_from_copied_buffer");
@@ -486,6 +494,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_slice_sub_import = (grpc_slice_sub_type) GetProcAddress(library, "grpc_slice_sub");
grpc_slice_sub_no_ref_import = (grpc_slice_sub_no_ref_type) GetProcAddress(library, "grpc_slice_sub_no_ref");
grpc_slice_split_tail_import = (grpc_slice_split_tail_type) GetProcAddress(library, "grpc_slice_split_tail");
+ grpc_slice_split_tail_maybe_ref_import = (grpc_slice_split_tail_maybe_ref_type) GetProcAddress(library, "grpc_slice_split_tail_maybe_ref");
grpc_slice_split_head_import = (grpc_slice_split_head_type) GetProcAddress(library, "grpc_slice_split_head");
grpc_empty_slice_import = (grpc_empty_slice_type) GetProcAddress(library, "grpc_empty_slice");
grpc_slice_default_hash_impl_import = (grpc_slice_default_hash_impl_type) GetProcAddress(library, "grpc_slice_default_hash_impl");
@@ -514,6 +523,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_slice_buffer_move_into_import = (grpc_slice_buffer_move_into_type) GetProcAddress(library, "grpc_slice_buffer_move_into");
grpc_slice_buffer_trim_end_import = (grpc_slice_buffer_trim_end_type) GetProcAddress(library, "grpc_slice_buffer_trim_end");
grpc_slice_buffer_move_first_import = (grpc_slice_buffer_move_first_type) GetProcAddress(library, "grpc_slice_buffer_move_first");
+ grpc_slice_buffer_move_first_no_ref_import = (grpc_slice_buffer_move_first_no_ref_type) GetProcAddress(library, "grpc_slice_buffer_move_first_no_ref");
grpc_slice_buffer_move_first_into_buffer_import = (grpc_slice_buffer_move_first_into_buffer_type) GetProcAddress(library, "grpc_slice_buffer_move_first_into_buffer");
grpc_slice_buffer_take_first_import = (grpc_slice_buffer_take_first_type) GetProcAddress(library, "grpc_slice_buffer_take_first");
grpc_slice_buffer_undo_take_first_import = (grpc_slice_buffer_undo_take_first_type) GetProcAddress(library, "grpc_slice_buffer_undo_take_first");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index dcdc8063a2..f62b31e83d 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -275,6 +275,9 @@ extern grpc_channel_register_call_type grpc_channel_register_call_import;
typedef grpc_call *(*grpc_channel_create_registered_call_type)(grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, grpc_completion_queue *completion_queue, void *registered_call_handle, gpr_timespec deadline, void *reserved);
extern grpc_channel_create_registered_call_type grpc_channel_create_registered_call_import;
#define grpc_channel_create_registered_call grpc_channel_create_registered_call_import
+typedef void *(*grpc_call_arena_alloc_type)(grpc_call *call, size_t size);
+extern grpc_call_arena_alloc_type grpc_call_arena_alloc_import;
+#define grpc_call_arena_alloc grpc_call_arena_alloc_import
typedef grpc_call_error(*grpc_call_start_batch_type)(grpc_call *call, const grpc_op *ops, size_t nops, void *tag, void *reserved);
extern grpc_call_start_batch_type grpc_call_start_batch_import;
#define grpc_call_start_batch grpc_call_start_batch_import
@@ -308,9 +311,12 @@ extern grpc_call_cancel_type grpc_call_cancel_import;
typedef grpc_call_error(*grpc_call_cancel_with_status_type)(grpc_call *call, grpc_status_code status, const char *description, void *reserved);
extern grpc_call_cancel_with_status_type grpc_call_cancel_with_status_import;
#define grpc_call_cancel_with_status grpc_call_cancel_with_status_import
-typedef void(*grpc_call_destroy_type)(grpc_call *call);
-extern grpc_call_destroy_type grpc_call_destroy_import;
-#define grpc_call_destroy grpc_call_destroy_import
+typedef void(*grpc_call_ref_type)(grpc_call *call);
+extern grpc_call_ref_type grpc_call_ref_import;
+#define grpc_call_ref grpc_call_ref_import
+typedef void(*grpc_call_unref_type)(grpc_call *call);
+extern grpc_call_unref_type grpc_call_unref_import;
+#define grpc_call_unref grpc_call_unref_import
typedef grpc_call_error(*grpc_server_request_call_type)(grpc_server *server, grpc_call **call, grpc_call_details *details, grpc_metadata_array *request_metadata, grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void *tag_new);
extern grpc_server_request_call_type grpc_server_request_call_import;
#define grpc_server_request_call grpc_server_request_call_import
@@ -326,9 +332,6 @@ extern grpc_server_create_type grpc_server_create_import;
typedef void(*grpc_server_register_completion_queue_type)(grpc_server *server, grpc_completion_queue *cq, void *reserved);
extern grpc_server_register_completion_queue_type grpc_server_register_completion_queue_import;
#define grpc_server_register_completion_queue grpc_server_register_completion_queue_import
-typedef void(*grpc_server_register_non_listening_completion_queue_type)(grpc_server *server, grpc_completion_queue *q, void *reserved);
-extern grpc_server_register_non_listening_completion_queue_type grpc_server_register_non_listening_completion_queue_import;
-#define grpc_server_register_non_listening_completion_queue grpc_server_register_non_listening_completion_queue_import
typedef int(*grpc_server_add_insecure_http2_port_type)(grpc_server *server, const char *addr);
extern grpc_server_add_insecure_http2_port_type grpc_server_add_insecure_http2_port_import;
#define grpc_server_add_insecure_http2_port grpc_server_add_insecure_http2_port_import
@@ -485,6 +488,9 @@ extern grpc_slice_ref_type grpc_slice_ref_import;
typedef void(*grpc_slice_unref_type)(grpc_slice s);
extern grpc_slice_unref_type grpc_slice_unref_import;
#define grpc_slice_unref grpc_slice_unref_import
+typedef grpc_slice(*grpc_slice_copy_type)(grpc_slice s);
+extern grpc_slice_copy_type grpc_slice_copy_import;
+#define grpc_slice_copy grpc_slice_copy_import
typedef grpc_slice(*grpc_slice_new_type)(void *p, size_t len, void (*destroy)(void *));
extern grpc_slice_new_type grpc_slice_new_import;
#define grpc_slice_new grpc_slice_new_import
@@ -497,6 +503,9 @@ extern grpc_slice_new_with_len_type grpc_slice_new_with_len_import;
typedef grpc_slice(*grpc_slice_malloc_type)(size_t length);
extern grpc_slice_malloc_type grpc_slice_malloc_import;
#define grpc_slice_malloc grpc_slice_malloc_import
+typedef grpc_slice(*grpc_slice_malloc_large_type)(size_t length);
+extern grpc_slice_malloc_large_type grpc_slice_malloc_large_import;
+#define grpc_slice_malloc_large grpc_slice_malloc_large_import
typedef grpc_slice(*grpc_slice_intern_type)(grpc_slice slice);
extern grpc_slice_intern_type grpc_slice_intern_import;
#define grpc_slice_intern grpc_slice_intern_import
@@ -521,6 +530,9 @@ extern grpc_slice_sub_no_ref_type grpc_slice_sub_no_ref_import;
typedef grpc_slice(*grpc_slice_split_tail_type)(grpc_slice *s, size_t split);
extern grpc_slice_split_tail_type grpc_slice_split_tail_import;
#define grpc_slice_split_tail grpc_slice_split_tail_import
+typedef grpc_slice(*grpc_slice_split_tail_maybe_ref_type)(grpc_slice *s, size_t split, grpc_slice_ref_whom ref_whom);
+extern grpc_slice_split_tail_maybe_ref_type grpc_slice_split_tail_maybe_ref_import;
+#define grpc_slice_split_tail_maybe_ref grpc_slice_split_tail_maybe_ref_import
typedef grpc_slice(*grpc_slice_split_head_type)(grpc_slice *s, size_t split);
extern grpc_slice_split_head_type grpc_slice_split_head_import;
#define grpc_slice_split_head grpc_slice_split_head_import
@@ -605,6 +617,9 @@ extern grpc_slice_buffer_trim_end_type grpc_slice_buffer_trim_end_import;
typedef void(*grpc_slice_buffer_move_first_type)(grpc_slice_buffer *src, size_t n, grpc_slice_buffer *dst);
extern grpc_slice_buffer_move_first_type grpc_slice_buffer_move_first_import;
#define grpc_slice_buffer_move_first grpc_slice_buffer_move_first_import
+typedef void(*grpc_slice_buffer_move_first_no_ref_type)(grpc_slice_buffer *src, size_t n, grpc_slice_buffer *dst);
+extern grpc_slice_buffer_move_first_no_ref_type grpc_slice_buffer_move_first_no_ref_import;
+#define grpc_slice_buffer_move_first_no_ref grpc_slice_buffer_move_first_no_ref_import
typedef void(*grpc_slice_buffer_move_first_into_buffer_type)(grpc_exec_ctx *exec_ctx, grpc_slice_buffer *src, size_t n, void *dst);
extern grpc_slice_buffer_move_first_into_buffer_type grpc_slice_buffer_move_first_into_buffer_import;
#define grpc_slice_buffer_move_first_into_buffer grpc_slice_buffer_move_first_into_buffer_import
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index ef57d5b07e..d7408f683d 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -132,11 +132,15 @@ static VALUE grpc_rb_server_alloc(VALUE cls) {
Initializes server instances. */
static VALUE grpc_rb_server_init(VALUE self, VALUE channel_args) {
- grpc_completion_queue *cq = grpc_completion_queue_create_for_pluck(NULL);
+ grpc_completion_queue *cq = NULL;
grpc_rb_server *wrapper = NULL;
grpc_server *srv = NULL;
grpc_channel_args args;
MEMZERO(&args, grpc_channel_args, 1);
+
+ grpc_ruby_once_init();
+
+ cq = grpc_completion_queue_create_for_pluck(NULL);
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type,
wrapper);
grpc_rb_hash_convert_to_channel_args(channel_args, &args);