• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
grpc_rb_cannot_alloc(VALUE cls)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. */
grpc_rb_cannot_init(VALUE self)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. */
grpc_rb_cannot_init_copy(VALUE copy,VALUE self)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  */
grpc_rb_time_timeval(VALUE time,int interval)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 
Init_grpc_status_codes()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. */
grpc_rb_time_val_to_time(VALUE self)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. */
grpc_rb_time_val_inspect(VALUE self)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. */
grpc_rb_time_val_to_s(VALUE self)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. */
Init_grpc_time_consts()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
grpc_ruby_set_init_pid(void)263 static void grpc_ruby_set_init_pid(void) {}
grpc_ruby_forked_after_init(void)264 static bool grpc_ruby_forked_after_init(void) { return false; }
265 #else
266 static pid_t grpc_init_pid;
267 
grpc_ruby_set_init_pid(void)268 static void grpc_ruby_set_init_pid(void) {
269   GPR_ASSERT(grpc_init_pid == 0);
270   grpc_init_pid = getpid();
271 }
272 
grpc_ruby_forked_after_init(void)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 
grpc_rb_shutdown(void)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 
grpc_ruby_once_init_internal()301 static void grpc_ruby_once_init_internal() {
302   grpc_ruby_set_init_pid();
303   grpc_init();
304   atexit(grpc_rb_shutdown);
305 }
306 
grpc_ruby_fork_guard()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 
grpc_ruby_once_init()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 
Init_grpc_c()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