Home | History | Annotate | Download | only in grpc
      1 /*
      2  *
      3  * Copyright 2015 gRPC authors.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  */
     18 
     19 #include <ruby/ruby.h>
     20 
     21 #include "rb_grpc.h"
     22 #include "rb_grpc_imports.generated.h"
     23 
     24 #include <math.h>
     25 #include <ruby/vm.h>
     26 #include <stdbool.h>
     27 #include <sys/time.h>
     28 #include <sys/types.h>
     29 #include <unistd.h>
     30 
     31 #include <grpc/grpc.h>
     32 #include <grpc/support/log.h>
     33 #include <grpc/support/time.h>
     34 #include "rb_call.h"
     35 #include "rb_call_credentials.h"
     36 #include "rb_channel.h"
     37 #include "rb_channel_credentials.h"
     38 #include "rb_compression_options.h"
     39 #include "rb_event_thread.h"
     40 #include "rb_loader.h"
     41 #include "rb_server.h"
     42 #include "rb_server_credentials.h"
     43 
     44 static VALUE grpc_rb_cTimeVal = Qnil;
     45 
     46 static rb_data_type_t grpc_rb_timespec_data_type = {
     47     "gpr_timespec",
     48     {GRPC_RB_GC_NOT_MARKED,
     49      GRPC_RB_GC_DONT_FREE,
     50      GRPC_RB_MEMSIZE_UNAVAILABLE,
     51      {NULL, NULL}},
     52     NULL,
     53     NULL,
     54 #ifdef RUBY_TYPED_FREE_IMMEDIATELY
     55     RUBY_TYPED_FREE_IMMEDIATELY
     56 #endif
     57 };
     58 
     59 /* Alloc func that blocks allocation of a given object by raising an
     60  * exception. */
     61 VALUE grpc_rb_cannot_alloc(VALUE cls) {
     62   rb_raise(rb_eTypeError,
     63            "allocation of %s only allowed from the gRPC native layer",
     64            rb_class2name(cls));
     65   return Qnil;
     66 }
     67 
     68 /* Init func that fails by raising an exception. */
     69 VALUE grpc_rb_cannot_init(VALUE self) {
     70   rb_raise(rb_eTypeError,
     71            "initialization of %s only allowed from the gRPC native layer",
     72            rb_obj_classname(self));
     73   return Qnil;
     74 }
     75 
     76 /* Init/Clone func that fails by raising an exception. */
     77 VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
     78   (void)self;
     79   rb_raise(rb_eTypeError, "Copy initialization of %s is not supported",
     80            rb_obj_classname(copy));
     81   return Qnil;
     82 }
     83 
     84 /* id_tv_{,u}sec are accessor methods on Ruby Time instances. */
     85 static ID id_tv_sec;
     86 static ID id_tv_nsec;
     87 
     88 /**
     89  * grpc_rb_time_timeval creates a timeval from a ruby time object.
     90  *
     91  * This func is copied from ruby source, MRI/source/time.c, which is published
     92  * under the same license as the ruby.h, on which the entire extensions is
     93  * based.
     94  */
     95 gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
     96   gpr_timespec t;
     97   gpr_timespec* time_const;
     98   const char* tstr = interval ? "time interval" : "time";
     99   const char* want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
    100 
    101   t.clock_type = GPR_CLOCK_REALTIME;
    102   switch (TYPE(time)) {
    103     case T_DATA:
    104       if (CLASS_OF(time) == grpc_rb_cTimeVal) {
    105         TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type,
    106                              time_const);
    107         t = *time_const;
    108       } else if (CLASS_OF(time) == rb_cTime) {
    109         t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0));
    110         t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0));
    111       } else {
    112         rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
    113                  rb_obj_classname(time), want);
    114       }
    115       break;
    116 
    117     case T_FIXNUM:
    118       t.tv_sec = FIX2LONG(time);
    119       if (interval && t.tv_sec < 0)
    120         rb_raise(rb_eArgError, "%s must be positive", tstr);
    121       t.tv_nsec = 0;
    122       break;
    123 
    124     case T_FLOAT:
    125       if (interval && RFLOAT_VALUE(time) < 0.0)
    126         rb_raise(rb_eArgError, "%s must be positive", tstr);
    127       else {
    128         double f, d;
    129 
    130         d = modf(RFLOAT_VALUE(time), &f);
    131         if (d < 0) {
    132           d += 1;
    133           f -= 1;
    134         }
    135         t.tv_sec = (int64_t)f;
    136         if (f != t.tv_sec) {
    137           rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(time));
    138         }
    139         t.tv_nsec = (int)(d * 1e9 + 0.5);
    140       }
    141       break;
    142 
    143     case T_BIGNUM:
    144       t.tv_sec = NUM2LONG(time);
    145       if (interval && t.tv_sec < 0)
    146         rb_raise(rb_eArgError, "%s must be positive", tstr);
    147       t.tv_nsec = 0;
    148       break;
    149 
    150     default:
    151       rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
    152                rb_obj_classname(time), want);
    153       break;
    154   }
    155   return t;
    156 }
    157 
    158 static void Init_grpc_status_codes() {
    159   /* Constants representing the status codes or grpc_status_code in status.h */
    160   VALUE grpc_rb_mStatusCodes =
    161       rb_define_module_under(grpc_rb_mGrpcCore, "StatusCodes");
    162   rb_define_const(grpc_rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
    163   rb_define_const(grpc_rb_mStatusCodes, "CANCELLED",
    164                   INT2NUM(GRPC_STATUS_CANCELLED));
    165   rb_define_const(grpc_rb_mStatusCodes, "UNKNOWN",
    166                   INT2NUM(GRPC_STATUS_UNKNOWN));
    167   rb_define_const(grpc_rb_mStatusCodes, "INVALID_ARGUMENT",
    168                   INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
    169   rb_define_const(grpc_rb_mStatusCodes, "DEADLINE_EXCEEDED",
    170                   INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
    171   rb_define_const(grpc_rb_mStatusCodes, "NOT_FOUND",
    172                   INT2NUM(GRPC_STATUS_NOT_FOUND));
    173   rb_define_const(grpc_rb_mStatusCodes, "ALREADY_EXISTS",
    174                   INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
    175   rb_define_const(grpc_rb_mStatusCodes, "PERMISSION_DENIED",
    176                   INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
    177   rb_define_const(grpc_rb_mStatusCodes, "UNAUTHENTICATED",
    178                   INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
    179   rb_define_const(grpc_rb_mStatusCodes, "RESOURCE_EXHAUSTED",
    180                   INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
    181   rb_define_const(grpc_rb_mStatusCodes, "FAILED_PRECONDITION",
    182                   INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
    183   rb_define_const(grpc_rb_mStatusCodes, "ABORTED",
    184                   INT2NUM(GRPC_STATUS_ABORTED));
    185   rb_define_const(grpc_rb_mStatusCodes, "OUT_OF_RANGE",
    186                   INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
    187   rb_define_const(grpc_rb_mStatusCodes, "UNIMPLEMENTED",
    188                   INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
    189   rb_define_const(grpc_rb_mStatusCodes, "INTERNAL",
    190                   INT2NUM(GRPC_STATUS_INTERNAL));
    191   rb_define_const(grpc_rb_mStatusCodes, "UNAVAILABLE",
    192                   INT2NUM(GRPC_STATUS_UNAVAILABLE));
    193   rb_define_const(grpc_rb_mStatusCodes, "DATA_LOSS",
    194                   INT2NUM(GRPC_STATUS_DATA_LOSS));
    195 }
    196 
    197 /* id_at is the constructor method of the ruby standard Time class. */
    198 static ID id_at;
    199 
    200 /* id_inspect is the inspect method found on various ruby objects. */
    201 static ID id_inspect;
    202 
    203 /* id_to_s is the to_s method found on various ruby objects. */
    204 static ID id_to_s;
    205 
    206 /* Converts a wrapped time constant to a standard time. */
    207 static VALUE grpc_rb_time_val_to_time(VALUE self) {
    208   gpr_timespec* time_const = NULL;
    209   gpr_timespec real_time;
    210   TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type,
    211                        time_const);
    212   real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME);
    213   return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec),
    214                     INT2NUM(real_time.tv_nsec / 1000));
    215 }
    216 
    217 /* Invokes inspect on the ctime version of the time val. */
    218 static VALUE grpc_rb_time_val_inspect(VALUE self) {
    219   return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0);
    220 }
    221 
    222 /* Invokes to_s on the ctime version of the time val. */
    223 static VALUE grpc_rb_time_val_to_s(VALUE self) {
    224   return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
    225 }
    226 
    227 static gpr_timespec zero_realtime;
    228 static gpr_timespec inf_future_realtime;
    229 static gpr_timespec inf_past_realtime;
    230 
    231 /* Adds a module with constants that map to gpr's static timeval structs. */
    232 static void Init_grpc_time_consts() {
    233   VALUE grpc_rb_mTimeConsts =
    234       rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
    235   grpc_rb_cTimeVal =
    236       rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
    237   zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME);
    238   inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME);
    239   inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME);
    240   rb_define_const(
    241       grpc_rb_mTimeConsts, "ZERO",
    242       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
    243                             (void*)&zero_realtime));
    244   rb_define_const(
    245       grpc_rb_mTimeConsts, "INFINITE_FUTURE",
    246       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
    247                             (void*)&inf_future_realtime));
    248   rb_define_const(
    249       grpc_rb_mTimeConsts, "INFINITE_PAST",
    250       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
    251                             (void*)&inf_past_realtime));
    252   rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
    253   rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
    254   rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
    255   id_at = rb_intern("at");
    256   id_inspect = rb_intern("inspect");
    257   id_to_s = rb_intern("to_s");
    258   id_tv_sec = rb_intern("tv_sec");
    259   id_tv_nsec = rb_intern("tv_nsec");
    260 }
    261 
    262 #if GPR_WINDOWS
    263 static void grpc_ruby_set_init_pid(void) {}
    264 static bool grpc_ruby_forked_after_init(void) { return false; }
    265 #else
    266 static pid_t grpc_init_pid;
    267 
    268 static void grpc_ruby_set_init_pid(void) {
    269   GPR_ASSERT(grpc_init_pid == 0);
    270   grpc_init_pid = getpid();
    271 }
    272 
    273 static bool grpc_ruby_forked_after_init(void) {
    274   GPR_ASSERT(grpc_init_pid != 0);
    275   return grpc_init_pid != getpid();
    276 }
    277 #endif
    278 
    279 static void grpc_rb_shutdown(void) {
    280   if (!grpc_ruby_forked_after_init()) grpc_shutdown();
    281 }
    282 
    283 /* Initialize the GRPC module structs */
    284 
    285 /* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */
    286 VALUE grpc_rb_sNewServerRpc = Qnil;
    287 /* grpc_rb_sStatus is the struct that holds status details. */
    288 VALUE grpc_rb_sStatus = Qnil;
    289 
    290 /* Initialize the GRPC module. */
    291 VALUE grpc_rb_mGRPC = Qnil;
    292 VALUE grpc_rb_mGrpcCore = Qnil;
    293 
    294 /* cached Symbols for members in Status struct */
    295 VALUE sym_code = Qundef;
    296 VALUE sym_details = Qundef;
    297 VALUE sym_metadata = Qundef;
    298 
    299 static gpr_once g_once_init = GPR_ONCE_INIT;
    300 
    301 static void grpc_ruby_once_init_internal() {
    302   grpc_ruby_set_init_pid();
    303   grpc_init();
    304   atexit(grpc_rb_shutdown);
    305 }
    306 
    307 void grpc_ruby_fork_guard() {
    308   if (grpc_ruby_forked_after_init()) {
    309     rb_raise(rb_eRuntimeError, "grpc cannot be used before and after forking");
    310   }
    311 }
    312 
    313 static VALUE bg_thread_init_rb_mu = Qundef;
    314 static int bg_thread_init_done = 0;
    315 
    316 void grpc_ruby_once_init() {
    317   /* ruby_vm_at_exit doesn't seem to be working. It would crash once every
    318    * blue moon, and some users are getting it repeatedly. See the discussions
    319    *  - https://github.com/grpc/grpc/pull/5337
    320    *  - https://bugs.ruby-lang.org/issues/12095
    321    *
    322    * In order to still be able to handle the (unlikely) situation where the
    323    * extension is loaded by a first Ruby VM that is subsequently destroyed,
    324    * then loaded again by another VM within the same process, we need to
    325    * schedule our initialization and destruction only once.
    326    */
    327   gpr_once_init(&g_once_init, grpc_ruby_once_init_internal);
    328 
    329   // Avoid calling calling into ruby library (when creating threads here)
    330   // in gpr_once_init. In general, it appears to be unsafe to call
    331   // into the ruby library while holding a non-ruby mutex, because a gil yield
    332   // could end up trying to lock onto that same mutex and deadlocking.
    333   rb_mutex_lock(bg_thread_init_rb_mu);
    334   if (!bg_thread_init_done) {
    335     grpc_rb_event_queue_thread_start();
    336     grpc_rb_channel_polling_thread_start();
    337     bg_thread_init_done = 1;
    338   }
    339   rb_mutex_unlock(bg_thread_init_rb_mu);
    340 }
    341 
    342 void Init_grpc_c() {
    343   if (!grpc_rb_load_core()) {
    344     rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core");
    345     return;
    346   }
    347 
    348   rb_global_variable(&bg_thread_init_rb_mu);
    349   bg_thread_init_rb_mu = rb_mutex_new();
    350 
    351   grpc_rb_mGRPC = rb_define_module("GRPC");
    352   grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
    353   grpc_rb_sNewServerRpc = rb_struct_define(
    354       "NewServerRpc", "method", "host", "deadline", "metadata", "call", NULL);
    355   grpc_rb_sStatus =
    356       rb_struct_define("Status", "code", "details", "metadata", NULL);
    357   sym_code = ID2SYM(rb_intern("code"));
    358   sym_details = ID2SYM(rb_intern("details"));
    359   sym_metadata = ID2SYM(rb_intern("metadata"));
    360 
    361   Init_grpc_channel();
    362   Init_grpc_call();
    363   Init_grpc_call_credentials();
    364   Init_grpc_channel_credentials();
    365   Init_grpc_server();
    366   Init_grpc_server_credentials();
    367   Init_grpc_status_codes();
    368   Init_grpc_time_consts();
    369   Init_grpc_compression_options();
    370 }
    371