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