Index: third_party/grpc/src/ruby/ext/grpc/rb_grpc.c |
diff --git a/third_party/grpc/src/ruby/ext/grpc/rb_grpc.c b/third_party/grpc/src/ruby/ext/grpc/rb_grpc.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0f9b18fa212a44278d80222f653bc59f483d44d1 |
--- /dev/null |
+++ b/third_party/grpc/src/ruby/ext/grpc/rb_grpc.c |
@@ -0,0 +1,337 @@ |
+/* |
+ * |
+ * Copyright 2015-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. |
+ * |
+ */ |
+ |
+#include <ruby/ruby.h> |
+#include "rb_grpc_imports.generated.h" |
+#include "rb_grpc.h" |
+ |
+#include <math.h> |
+#include <ruby/ruby.h> |
+#include <ruby/vm.h> |
+#include <sys/time.h> |
+ |
+#include <grpc/grpc.h> |
+#include <grpc/support/time.h> |
+#include "rb_call.h" |
+#include "rb_call_credentials.h" |
+#include "rb_channel.h" |
+#include "rb_channel_credentials.h" |
+#include "rb_completion_queue.h" |
+#include "rb_loader.h" |
+#include "rb_server.h" |
+#include "rb_server_credentials.h" |
+ |
+static VALUE grpc_rb_cTimeVal = Qnil; |
+ |
+static rb_data_type_t grpc_rb_timespec_data_type = { |
+ "gpr_timespec", |
+ {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE, |
+ {NULL, NULL}}, |
+ NULL, |
+ NULL, |
+#ifdef RUBY_TYPED_FREE_IMMEDIATELY |
+ RUBY_TYPED_FREE_IMMEDIATELY |
+#endif |
+}; |
+ |
+/* Alloc func that blocks allocation of a given object by raising an |
+ * exception. */ |
+VALUE grpc_rb_cannot_alloc(VALUE cls) { |
+ rb_raise(rb_eTypeError, |
+ "allocation of %s only allowed from the gRPC native layer", |
+ rb_class2name(cls)); |
+ return Qnil; |
+} |
+ |
+/* Init func that fails by raising an exception. */ |
+VALUE grpc_rb_cannot_init(VALUE self) { |
+ rb_raise(rb_eTypeError, |
+ "initialization of %s only allowed from the gRPC native layer", |
+ rb_obj_classname(self)); |
+ return Qnil; |
+} |
+ |
+/* Init/Clone func that fails by raising an exception. */ |
+VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) { |
+ (void)self; |
+ rb_raise(rb_eTypeError, |
+ "initialization of %s only allowed from the gRPC native layer", |
+ rb_obj_classname(copy)); |
+ return Qnil; |
+} |
+ |
+/* id_tv_{,u}sec are accessor methods on Ruby Time instances. */ |
+static ID id_tv_sec; |
+static ID id_tv_nsec; |
+ |
+/** |
+ * grpc_rb_time_timeval creates a timeval from a ruby time object. |
+ * |
+ * This func is copied from ruby source, MRI/source/time.c, which is published |
+ * under the same license as the ruby.h, on which the entire extensions is |
+ * based. |
+ */ |
+gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { |
+ gpr_timespec t; |
+ gpr_timespec *time_const; |
+ const char *tstr = interval ? "time interval" : "time"; |
+ const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>"; |
+ |
+ t.clock_type = GPR_CLOCK_REALTIME; |
+ switch (TYPE(time)) { |
+ case T_DATA: |
+ if (CLASS_OF(time) == grpc_rb_cTimeVal) { |
+ TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type, |
+ time_const); |
+ t = *time_const; |
+ } else if (CLASS_OF(time) == rb_cTime) { |
+ t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0)); |
+ t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0)); |
+ } else { |
+ rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, |
+ rb_obj_classname(time), want); |
+ } |
+ break; |
+ |
+ case T_FIXNUM: |
+ t.tv_sec = FIX2LONG(time); |
+ if (interval && t.tv_sec < 0) |
+ rb_raise(rb_eArgError, "%s must be positive", tstr); |
+ t.tv_nsec = 0; |
+ break; |
+ |
+ case T_FLOAT: |
+ if (interval && RFLOAT_VALUE(time) < 0.0) |
+ rb_raise(rb_eArgError, "%s must be positive", tstr); |
+ else { |
+ double f, d; |
+ |
+ d = modf(RFLOAT_VALUE(time), &f); |
+ if (d < 0) { |
+ d += 1; |
+ f -= 1; |
+ } |
+ t.tv_sec = (int64_t)f; |
+ if (f != t.tv_sec) { |
+ rb_raise(rb_eRangeError, "%f out of Time range", |
+ RFLOAT_VALUE(time)); |
+ } |
+ t.tv_nsec = (int)(d * 1e9 + 0.5); |
+ } |
+ break; |
+ |
+ case T_BIGNUM: |
+ t.tv_sec = NUM2LONG(time); |
+ if (interval && t.tv_sec < 0) |
+ rb_raise(rb_eArgError, "%s must be positive", tstr); |
+ t.tv_nsec = 0; |
+ break; |
+ |
+ default: |
+ rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, |
+ rb_obj_classname(time), want); |
+ break; |
+ } |
+ return t; |
+} |
+ |
+static void Init_grpc_status_codes() { |
+ /* Constants representing the status codes or grpc_status_code in status.h */ |
+ VALUE grpc_rb_mStatusCodes = |
+ rb_define_module_under(grpc_rb_mGrpcCore, "StatusCodes"); |
+ rb_define_const(grpc_rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK)); |
+ rb_define_const(grpc_rb_mStatusCodes, "CANCELLED", |
+ INT2NUM(GRPC_STATUS_CANCELLED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "UNKNOWN", |
+ INT2NUM(GRPC_STATUS_UNKNOWN)); |
+ rb_define_const(grpc_rb_mStatusCodes, "INVALID_ARGUMENT", |
+ INT2NUM(GRPC_STATUS_INVALID_ARGUMENT)); |
+ rb_define_const(grpc_rb_mStatusCodes, "DEADLINE_EXCEEDED", |
+ INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "NOT_FOUND", |
+ INT2NUM(GRPC_STATUS_NOT_FOUND)); |
+ rb_define_const(grpc_rb_mStatusCodes, "ALREADY_EXISTS", |
+ INT2NUM(GRPC_STATUS_ALREADY_EXISTS)); |
+ rb_define_const(grpc_rb_mStatusCodes, "PERMISSION_DENIED", |
+ INT2NUM(GRPC_STATUS_PERMISSION_DENIED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "UNAUTHENTICATED", |
+ INT2NUM(GRPC_STATUS_UNAUTHENTICATED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "RESOURCE_EXHAUSTED", |
+ INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "FAILED_PRECONDITION", |
+ INT2NUM(GRPC_STATUS_FAILED_PRECONDITION)); |
+ rb_define_const(grpc_rb_mStatusCodes, "ABORTED", |
+ INT2NUM(GRPC_STATUS_ABORTED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "OUT_OF_RANGE", |
+ INT2NUM(GRPC_STATUS_OUT_OF_RANGE)); |
+ rb_define_const(grpc_rb_mStatusCodes, "UNIMPLEMENTED", |
+ INT2NUM(GRPC_STATUS_UNIMPLEMENTED)); |
+ rb_define_const(grpc_rb_mStatusCodes, "INTERNAL", |
+ INT2NUM(GRPC_STATUS_INTERNAL)); |
+ rb_define_const(grpc_rb_mStatusCodes, "UNAVAILABLE", |
+ INT2NUM(GRPC_STATUS_UNAVAILABLE)); |
+ rb_define_const(grpc_rb_mStatusCodes, "DATA_LOSS", |
+ INT2NUM(GRPC_STATUS_DATA_LOSS)); |
+} |
+ |
+/* id_at is the constructor method of the ruby standard Time class. */ |
+static ID id_at; |
+ |
+/* id_inspect is the inspect method found on various ruby objects. */ |
+static ID id_inspect; |
+ |
+/* id_to_s is the to_s method found on various ruby objects. */ |
+static ID id_to_s; |
+ |
+/* Converts a wrapped time constant to a standard time. */ |
+static VALUE grpc_rb_time_val_to_time(VALUE self) { |
+ gpr_timespec *time_const = NULL; |
+ gpr_timespec real_time; |
+ TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type, |
+ time_const); |
+ real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME); |
+ return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec), |
+ INT2NUM(real_time.tv_nsec)); |
+} |
+ |
+/* Invokes inspect on the ctime version of the time val. */ |
+static VALUE grpc_rb_time_val_inspect(VALUE self) { |
+ return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0); |
+} |
+ |
+/* Invokes to_s on the ctime version of the time val. */ |
+static VALUE grpc_rb_time_val_to_s(VALUE self) { |
+ return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0); |
+} |
+ |
+static gpr_timespec zero_realtime; |
+static gpr_timespec inf_future_realtime; |
+static gpr_timespec inf_past_realtime; |
+ |
+/* Adds a module with constants that map to gpr's static timeval structs. */ |
+static void Init_grpc_time_consts() { |
+ VALUE grpc_rb_mTimeConsts = |
+ rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts"); |
+ grpc_rb_cTimeVal = |
+ rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject); |
+ zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME); |
+ inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME); |
+ inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME); |
+ rb_define_const( |
+ grpc_rb_mTimeConsts, "ZERO", |
+ TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
+ (void *)&zero_realtime)); |
+ rb_define_const( |
+ grpc_rb_mTimeConsts, "INFINITE_FUTURE", |
+ TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
+ (void *)&inf_future_realtime)); |
+ rb_define_const( |
+ grpc_rb_mTimeConsts, "INFINITE_PAST", |
+ TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
+ (void *)&inf_past_realtime)); |
+ rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0); |
+ rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0); |
+ rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0); |
+ id_at = rb_intern("at"); |
+ id_inspect = rb_intern("inspect"); |
+ id_to_s = rb_intern("to_s"); |
+ id_tv_sec = rb_intern("tv_sec"); |
+ id_tv_nsec = rb_intern("tv_nsec"); |
+} |
+ |
+static void grpc_rb_shutdown(void) { |
+ grpc_shutdown(); |
+} |
+ |
+/* Initialize the GRPC module structs */ |
+ |
+/* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */ |
+VALUE grpc_rb_sNewServerRpc = Qnil; |
+/* grpc_rb_sStatus is the struct that holds status details. */ |
+VALUE grpc_rb_sStatus = Qnil; |
+ |
+/* Initialize the GRPC module. */ |
+VALUE grpc_rb_mGRPC = Qnil; |
+VALUE grpc_rb_mGrpcCore = Qnil; |
+ |
+/* cached Symbols for members in Status struct */ |
+VALUE sym_code = Qundef; |
+VALUE sym_details = Qundef; |
+VALUE sym_metadata = Qundef; |
+ |
+static gpr_once g_once_init = GPR_ONCE_INIT; |
+ |
+static void grpc_ruby_once_init() { |
+ grpc_init(); |
+ 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; |
+ } |
+ |
+ /* 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 |
+ * - https://bugs.ruby-lang.org/issues/12095 |
+ * |
+ * In order to still be able to handle the (unlikely) situation where the |
+ * extension is loaded by a first Ruby VM that is subsequently destroyed, |
+ * 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); |
+ |
+ grpc_rb_mGRPC = rb_define_module("GRPC"); |
+ grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core"); |
+ grpc_rb_sNewServerRpc = |
+ rb_struct_define("NewServerRpc", "method", "host", |
+ "deadline", "metadata", "call", NULL); |
+ grpc_rb_sStatus = |
+ rb_struct_define("Status", "code", "details", "metadata", NULL); |
+ sym_code = ID2SYM(rb_intern("code")); |
+ sym_details = ID2SYM(rb_intern("details")); |
+ sym_metadata = ID2SYM(rb_intern("metadata")); |
+ |
+ Init_grpc_channel(); |
+ Init_grpc_completion_queue(); |
+ Init_grpc_call(); |
+ Init_grpc_call_credentials(); |
+ Init_grpc_channel_credentials(); |
+ Init_grpc_server(); |
+ Init_grpc_server_credentials(); |
+ Init_grpc_status_codes(); |
+ Init_grpc_time_consts(); |
+} |