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