Imported Upstream version 1.41.0
[platform/upstream/grpc.git] / src / ruby / ext / grpc / rb_grpc.c
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
23 #include <math.h>
24 #include <ruby/vm.h>
25 #include <stdbool.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include "rb_call.h"
31 #include "rb_call_credentials.h"
32 #include "rb_channel.h"
33 #include "rb_channel_credentials.h"
34 #include "rb_compression_options.h"
35 #include "rb_event_thread.h"
36 #include "rb_grpc_imports.generated.h"
37 #include "rb_loader.h"
38 #include "rb_server.h"
39 #include "rb_server_credentials.h"
40 #include "rb_xds_channel_credentials.h"
41 #include "rb_xds_server_credentials.h"
42
43 #include <grpc/grpc.h>
44 #include <grpc/support/log.h>
45 #include <grpc/support/time.h>
46
47 static VALUE grpc_rb_cTimeVal = Qnil;
48
49 static rb_data_type_t grpc_rb_timespec_data_type = {
50     "gpr_timespec",
51     {GRPC_RB_GC_NOT_MARKED,
52      GRPC_RB_GC_DONT_FREE,
53      GRPC_RB_MEMSIZE_UNAVAILABLE,
54      {NULL, NULL}},
55     NULL,
56     NULL,
57 #ifdef RUBY_TYPED_FREE_IMMEDIATELY
58     RUBY_TYPED_FREE_IMMEDIATELY
59 #endif
60 };
61
62 /* Alloc func that blocks allocation of a given object by raising an
63  * exception. */
64 VALUE grpc_rb_cannot_alloc(VALUE cls) {
65   rb_raise(rb_eTypeError,
66            "allocation of %s only allowed from the gRPC native layer",
67            rb_class2name(cls));
68   return Qnil;
69 }
70
71 /* Init func that fails by raising an exception. */
72 VALUE grpc_rb_cannot_init(VALUE self) {
73   rb_raise(rb_eTypeError,
74            "initialization of %s only allowed from the gRPC native layer",
75            rb_obj_classname(self));
76   return Qnil;
77 }
78
79 /* Init/Clone func that fails by raising an exception. */
80 VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
81   (void)self;
82   rb_raise(rb_eTypeError, "Copy initialization of %s is not supported",
83            rb_obj_classname(copy));
84   return Qnil;
85 }
86
87 /* id_tv_{,u}sec are accessor methods on Ruby Time instances. */
88 static ID id_tv_sec;
89 static ID id_tv_nsec;
90
91 /**
92  * grpc_rb_time_timeval creates a timeval from a ruby time object.
93  *
94  * This func is copied from ruby source, MRI/source/time.c, which is published
95  * under the same license as the ruby.h, on which the entire extensions is
96  * based.
97  */
98 gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
99   gpr_timespec t;
100   gpr_timespec* time_const;
101   const char* tstr = interval ? "time interval" : "time";
102   const char* want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
103
104   t.clock_type = GPR_CLOCK_REALTIME;
105   switch (TYPE(time)) {
106     case T_DATA:
107       if (CLASS_OF(time) == grpc_rb_cTimeVal) {
108         TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type,
109                              time_const);
110         t = *time_const;
111       } else if (CLASS_OF(time) == rb_cTime) {
112         t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0));
113         t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0));
114       } else {
115         rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
116                  rb_obj_classname(time), want);
117       }
118       break;
119
120     case T_FIXNUM:
121       t.tv_sec = FIX2LONG(time);
122       if (interval && t.tv_sec < 0)
123         rb_raise(rb_eArgError, "%s must be positive", tstr);
124       t.tv_nsec = 0;
125       break;
126
127     case T_FLOAT:
128       if (interval && RFLOAT_VALUE(time) < 0.0)
129         rb_raise(rb_eArgError, "%s must be positive", tstr);
130       else {
131         double f, d;
132
133         d = modf(RFLOAT_VALUE(time), &f);
134         if (d < 0) {
135           d += 1;
136           f -= 1;
137         }
138         t.tv_sec = (int64_t)f;
139         if (f != t.tv_sec) {
140           rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(time));
141         }
142         t.tv_nsec = (int)(d * 1e9 + 0.5);
143       }
144       break;
145
146     case T_BIGNUM:
147       t.tv_sec = NUM2LONG(time);
148       if (interval && t.tv_sec < 0)
149         rb_raise(rb_eArgError, "%s must be positive", tstr);
150       t.tv_nsec = 0;
151       break;
152
153     default:
154       rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
155                rb_obj_classname(time), want);
156       break;
157   }
158   return t;
159 }
160
161 /* id_at is the constructor method of the ruby standard Time class. */
162 static ID id_at;
163
164 /* id_inspect is the inspect method found on various ruby objects. */
165 static ID id_inspect;
166
167 /* id_to_s is the to_s method found on various ruby objects. */
168 static ID id_to_s;
169
170 /* Converts a wrapped time constant to a standard time. */
171 static VALUE grpc_rb_time_val_to_time(VALUE self) {
172   gpr_timespec* time_const = NULL;
173   gpr_timespec real_time;
174   TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type,
175                        time_const);
176   real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME);
177   return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec),
178                     INT2NUM(real_time.tv_nsec / 1000));
179 }
180
181 /* Invokes inspect on the ctime version of the time val. */
182 static VALUE grpc_rb_time_val_inspect(VALUE self) {
183   return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0);
184 }
185
186 /* Invokes to_s on the ctime version of the time val. */
187 static VALUE grpc_rb_time_val_to_s(VALUE self) {
188   return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
189 }
190
191 static gpr_timespec zero_realtime;
192 static gpr_timespec inf_future_realtime;
193 static gpr_timespec inf_past_realtime;
194
195 /* Adds a module with constants that map to gpr's static timeval structs. */
196 static void Init_grpc_time_consts() {
197   VALUE grpc_rb_mTimeConsts =
198       rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
199   grpc_rb_cTimeVal =
200       rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
201   zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME);
202   inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME);
203   inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME);
204   rb_define_const(
205       grpc_rb_mTimeConsts, "ZERO",
206       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
207                             (void*)&zero_realtime));
208   rb_define_const(
209       grpc_rb_mTimeConsts, "INFINITE_FUTURE",
210       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
211                             (void*)&inf_future_realtime));
212   rb_define_const(
213       grpc_rb_mTimeConsts, "INFINITE_PAST",
214       TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
215                             (void*)&inf_past_realtime));
216   rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
217   rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
218   rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
219   id_at = rb_intern("at");
220   id_inspect = rb_intern("inspect");
221   id_to_s = rb_intern("to_s");
222   id_tv_sec = rb_intern("tv_sec");
223   id_tv_nsec = rb_intern("tv_nsec");
224 }
225
226 #if GPR_WINDOWS
227 static void grpc_ruby_set_init_pid(void) {}
228 static bool grpc_ruby_forked_after_init(void) { return false; }
229 #else
230 static pid_t grpc_init_pid;
231
232 static void grpc_ruby_set_init_pid(void) {
233   GPR_ASSERT(grpc_init_pid == 0);
234   grpc_init_pid = getpid();
235 }
236
237 static bool grpc_ruby_forked_after_init(void) {
238   GPR_ASSERT(grpc_init_pid != 0);
239   return grpc_init_pid != getpid();
240 }
241 #endif
242
243 /* Initialize the GRPC module structs */
244
245 /* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */
246 VALUE grpc_rb_sNewServerRpc = Qnil;
247 /* grpc_rb_sStatus is the struct that holds status details. */
248 VALUE grpc_rb_sStatus = Qnil;
249
250 /* Initialize the GRPC module. */
251 VALUE grpc_rb_mGRPC = Qnil;
252 VALUE grpc_rb_mGrpcCore = Qnil;
253
254 /* cached Symbols for members in Status struct */
255 VALUE sym_code = Qundef;
256 VALUE sym_details = Qundef;
257 VALUE sym_metadata = Qundef;
258
259 static gpr_once g_once_init = GPR_ONCE_INIT;
260
261 void grpc_ruby_fork_guard() {
262   if (grpc_ruby_forked_after_init()) {
263     rb_raise(rb_eRuntimeError, "grpc cannot be used before and after forking");
264   }
265 }
266
267 static VALUE bg_thread_init_rb_mu = Qundef;
268 static int bg_thread_init_done = 0;
269
270 static void grpc_ruby_init_threads() {
271   // Avoid calling into ruby library (when creating threads here)
272   // in gpr_once_init. In general, it appears to be unsafe to call
273   // into the ruby library while holding a non-ruby mutex, because a gil yield
274   // could end up trying to lock onto that same mutex and deadlocking.
275   rb_mutex_lock(bg_thread_init_rb_mu);
276   if (!bg_thread_init_done) {
277     grpc_rb_event_queue_thread_start();
278     grpc_rb_channel_polling_thread_start();
279     bg_thread_init_done = 1;
280   }
281   rb_mutex_unlock(bg_thread_init_rb_mu);
282 }
283
284 static int64_t g_grpc_ruby_init_count;
285
286 void grpc_ruby_init() {
287   gpr_once_init(&g_once_init, grpc_ruby_set_init_pid);
288   grpc_init();
289   grpc_ruby_init_threads();
290   // (only gpr_log after logging has been initialized)
291   gpr_log(GPR_DEBUG,
292           "GRPC_RUBY: grpc_ruby_init - prev g_grpc_ruby_init_count:%" PRId64,
293           g_grpc_ruby_init_count++);
294 }
295
296 void grpc_ruby_shutdown() {
297   GPR_ASSERT(g_grpc_ruby_init_count > 0);
298   if (!grpc_ruby_forked_after_init()) grpc_shutdown();
299   gpr_log(
300       GPR_DEBUG,
301       "GRPC_RUBY: grpc_ruby_shutdown - prev g_grpc_ruby_init_count:%" PRId64,
302       g_grpc_ruby_init_count--);
303 }
304
305 void Init_grpc_c() {
306   if (!grpc_rb_load_core()) {
307     rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core");
308     return;
309   }
310
311   rb_global_variable(&bg_thread_init_rb_mu);
312   bg_thread_init_rb_mu = rb_mutex_new();
313
314   grpc_rb_mGRPC = rb_define_module("GRPC");
315   grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
316   grpc_rb_sNewServerRpc = rb_struct_define(
317       "NewServerRpc", "method", "host", "deadline", "metadata", "call", NULL);
318   grpc_rb_sStatus = rb_const_get(rb_cStruct, rb_intern("Status"));
319   sym_code = ID2SYM(rb_intern("code"));
320   sym_details = ID2SYM(rb_intern("details"));
321   sym_metadata = ID2SYM(rb_intern("metadata"));
322
323   Init_grpc_channel();
324   Init_grpc_call();
325   Init_grpc_call_credentials();
326   Init_grpc_channel_credentials();
327   Init_grpc_xds_channel_credentials();
328   Init_grpc_server();
329   Init_grpc_server_credentials();
330   Init_grpc_xds_server_credentials();
331   Init_grpc_time_consts();
332   Init_grpc_compression_options();
333 }