OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * |
| 3 * Copyright 2015-2016, Google Inc. |
| 4 * All rights reserved. |
| 5 * |
| 6 * Redistribution and use in source and binary forms, with or without |
| 7 * modification, are permitted provided that the following conditions are |
| 8 * met: |
| 9 * |
| 10 * * Redistributions of source code must retain the above copyright |
| 11 * notice, this list of conditions and the following disclaimer. |
| 12 * * Redistributions in binary form must reproduce the above |
| 13 * copyright notice, this list of conditions and the following disclaimer |
| 14 * in the documentation and/or other materials provided with the |
| 15 * distribution. |
| 16 * * Neither the name of Google Inc. nor the names of its |
| 17 * contributors may be used to endorse or promote products derived from |
| 18 * this software without specific prior written permission. |
| 19 * |
| 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 * |
| 32 */ |
| 33 |
| 34 #include <ruby/ruby.h> |
| 35 #include "rb_grpc_imports.generated.h" |
| 36 #include "rb_grpc.h" |
| 37 |
| 38 #include <math.h> |
| 39 #include <ruby/ruby.h> |
| 40 #include <ruby/vm.h> |
| 41 #include <sys/time.h> |
| 42 |
| 43 #include <grpc/grpc.h> |
| 44 #include <grpc/support/time.h> |
| 45 #include "rb_call.h" |
| 46 #include "rb_call_credentials.h" |
| 47 #include "rb_channel.h" |
| 48 #include "rb_channel_credentials.h" |
| 49 #include "rb_completion_queue.h" |
| 50 #include "rb_loader.h" |
| 51 #include "rb_server.h" |
| 52 #include "rb_server_credentials.h" |
| 53 |
| 54 static VALUE grpc_rb_cTimeVal = Qnil; |
| 55 |
| 56 static rb_data_type_t grpc_rb_timespec_data_type = { |
| 57 "gpr_timespec", |
| 58 {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE, |
| 59 {NULL, NULL}}, |
| 60 NULL, |
| 61 NULL, |
| 62 #ifdef RUBY_TYPED_FREE_IMMEDIATELY |
| 63 RUBY_TYPED_FREE_IMMEDIATELY |
| 64 #endif |
| 65 }; |
| 66 |
| 67 /* Alloc func that blocks allocation of a given object by raising an |
| 68 * exception. */ |
| 69 VALUE grpc_rb_cannot_alloc(VALUE cls) { |
| 70 rb_raise(rb_eTypeError, |
| 71 "allocation of %s only allowed from the gRPC native layer", |
| 72 rb_class2name(cls)); |
| 73 return Qnil; |
| 74 } |
| 75 |
| 76 /* Init func that fails by raising an exception. */ |
| 77 VALUE grpc_rb_cannot_init(VALUE self) { |
| 78 rb_raise(rb_eTypeError, |
| 79 "initialization of %s only allowed from the gRPC native layer", |
| 80 rb_obj_classname(self)); |
| 81 return Qnil; |
| 82 } |
| 83 |
| 84 /* Init/Clone func that fails by raising an exception. */ |
| 85 VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) { |
| 86 (void)self; |
| 87 rb_raise(rb_eTypeError, |
| 88 "initialization of %s only allowed from the gRPC native layer", |
| 89 rb_obj_classname(copy)); |
| 90 return Qnil; |
| 91 } |
| 92 |
| 93 /* id_tv_{,u}sec are accessor methods on Ruby Time instances. */ |
| 94 static ID id_tv_sec; |
| 95 static ID id_tv_nsec; |
| 96 |
| 97 /** |
| 98 * grpc_rb_time_timeval creates a timeval from a ruby time object. |
| 99 * |
| 100 * This func is copied from ruby source, MRI/source/time.c, which is published |
| 101 * under the same license as the ruby.h, on which the entire extensions is |
| 102 * based. |
| 103 */ |
| 104 gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { |
| 105 gpr_timespec t; |
| 106 gpr_timespec *time_const; |
| 107 const char *tstr = interval ? "time interval" : "time"; |
| 108 const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>"; |
| 109 |
| 110 t.clock_type = GPR_CLOCK_REALTIME; |
| 111 switch (TYPE(time)) { |
| 112 case T_DATA: |
| 113 if (CLASS_OF(time) == grpc_rb_cTimeVal) { |
| 114 TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type, |
| 115 time_const); |
| 116 t = *time_const; |
| 117 } else if (CLASS_OF(time) == rb_cTime) { |
| 118 t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0)); |
| 119 t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0)); |
| 120 } else { |
| 121 rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, |
| 122 rb_obj_classname(time), want); |
| 123 } |
| 124 break; |
| 125 |
| 126 case T_FIXNUM: |
| 127 t.tv_sec = FIX2LONG(time); |
| 128 if (interval && t.tv_sec < 0) |
| 129 rb_raise(rb_eArgError, "%s must be positive", tstr); |
| 130 t.tv_nsec = 0; |
| 131 break; |
| 132 |
| 133 case T_FLOAT: |
| 134 if (interval && RFLOAT_VALUE(time) < 0.0) |
| 135 rb_raise(rb_eArgError, "%s must be positive", tstr); |
| 136 else { |
| 137 double f, d; |
| 138 |
| 139 d = modf(RFLOAT_VALUE(time), &f); |
| 140 if (d < 0) { |
| 141 d += 1; |
| 142 f -= 1; |
| 143 } |
| 144 t.tv_sec = (int64_t)f; |
| 145 if (f != t.tv_sec) { |
| 146 rb_raise(rb_eRangeError, "%f out of Time range", |
| 147 RFLOAT_VALUE(time)); |
| 148 } |
| 149 t.tv_nsec = (int)(d * 1e9 + 0.5); |
| 150 } |
| 151 break; |
| 152 |
| 153 case T_BIGNUM: |
| 154 t.tv_sec = NUM2LONG(time); |
| 155 if (interval && t.tv_sec < 0) |
| 156 rb_raise(rb_eArgError, "%s must be positive", tstr); |
| 157 t.tv_nsec = 0; |
| 158 break; |
| 159 |
| 160 default: |
| 161 rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr, |
| 162 rb_obj_classname(time), want); |
| 163 break; |
| 164 } |
| 165 return t; |
| 166 } |
| 167 |
| 168 static void Init_grpc_status_codes() { |
| 169 /* Constants representing the status codes or grpc_status_code in status.h */ |
| 170 VALUE grpc_rb_mStatusCodes = |
| 171 rb_define_module_under(grpc_rb_mGrpcCore, "StatusCodes"); |
| 172 rb_define_const(grpc_rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK)); |
| 173 rb_define_const(grpc_rb_mStatusCodes, "CANCELLED", |
| 174 INT2NUM(GRPC_STATUS_CANCELLED)); |
| 175 rb_define_const(grpc_rb_mStatusCodes, "UNKNOWN", |
| 176 INT2NUM(GRPC_STATUS_UNKNOWN)); |
| 177 rb_define_const(grpc_rb_mStatusCodes, "INVALID_ARGUMENT", |
| 178 INT2NUM(GRPC_STATUS_INVALID_ARGUMENT)); |
| 179 rb_define_const(grpc_rb_mStatusCodes, "DEADLINE_EXCEEDED", |
| 180 INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED)); |
| 181 rb_define_const(grpc_rb_mStatusCodes, "NOT_FOUND", |
| 182 INT2NUM(GRPC_STATUS_NOT_FOUND)); |
| 183 rb_define_const(grpc_rb_mStatusCodes, "ALREADY_EXISTS", |
| 184 INT2NUM(GRPC_STATUS_ALREADY_EXISTS)); |
| 185 rb_define_const(grpc_rb_mStatusCodes, "PERMISSION_DENIED", |
| 186 INT2NUM(GRPC_STATUS_PERMISSION_DENIED)); |
| 187 rb_define_const(grpc_rb_mStatusCodes, "UNAUTHENTICATED", |
| 188 INT2NUM(GRPC_STATUS_UNAUTHENTICATED)); |
| 189 rb_define_const(grpc_rb_mStatusCodes, "RESOURCE_EXHAUSTED", |
| 190 INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED)); |
| 191 rb_define_const(grpc_rb_mStatusCodes, "FAILED_PRECONDITION", |
| 192 INT2NUM(GRPC_STATUS_FAILED_PRECONDITION)); |
| 193 rb_define_const(grpc_rb_mStatusCodes, "ABORTED", |
| 194 INT2NUM(GRPC_STATUS_ABORTED)); |
| 195 rb_define_const(grpc_rb_mStatusCodes, "OUT_OF_RANGE", |
| 196 INT2NUM(GRPC_STATUS_OUT_OF_RANGE)); |
| 197 rb_define_const(grpc_rb_mStatusCodes, "UNIMPLEMENTED", |
| 198 INT2NUM(GRPC_STATUS_UNIMPLEMENTED)); |
| 199 rb_define_const(grpc_rb_mStatusCodes, "INTERNAL", |
| 200 INT2NUM(GRPC_STATUS_INTERNAL)); |
| 201 rb_define_const(grpc_rb_mStatusCodes, "UNAVAILABLE", |
| 202 INT2NUM(GRPC_STATUS_UNAVAILABLE)); |
| 203 rb_define_const(grpc_rb_mStatusCodes, "DATA_LOSS", |
| 204 INT2NUM(GRPC_STATUS_DATA_LOSS)); |
| 205 } |
| 206 |
| 207 /* id_at is the constructor method of the ruby standard Time class. */ |
| 208 static ID id_at; |
| 209 |
| 210 /* id_inspect is the inspect method found on various ruby objects. */ |
| 211 static ID id_inspect; |
| 212 |
| 213 /* id_to_s is the to_s method found on various ruby objects. */ |
| 214 static ID id_to_s; |
| 215 |
| 216 /* Converts a wrapped time constant to a standard time. */ |
| 217 static VALUE grpc_rb_time_val_to_time(VALUE self) { |
| 218 gpr_timespec *time_const = NULL; |
| 219 gpr_timespec real_time; |
| 220 TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type, |
| 221 time_const); |
| 222 real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME); |
| 223 return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec), |
| 224 INT2NUM(real_time.tv_nsec)); |
| 225 } |
| 226 |
| 227 /* Invokes inspect on the ctime version of the time val. */ |
| 228 static VALUE grpc_rb_time_val_inspect(VALUE self) { |
| 229 return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0); |
| 230 } |
| 231 |
| 232 /* Invokes to_s on the ctime version of the time val. */ |
| 233 static VALUE grpc_rb_time_val_to_s(VALUE self) { |
| 234 return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0); |
| 235 } |
| 236 |
| 237 static gpr_timespec zero_realtime; |
| 238 static gpr_timespec inf_future_realtime; |
| 239 static gpr_timespec inf_past_realtime; |
| 240 |
| 241 /* Adds a module with constants that map to gpr's static timeval structs. */ |
| 242 static void Init_grpc_time_consts() { |
| 243 VALUE grpc_rb_mTimeConsts = |
| 244 rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts"); |
| 245 grpc_rb_cTimeVal = |
| 246 rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject); |
| 247 zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME); |
| 248 inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME); |
| 249 inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME); |
| 250 rb_define_const( |
| 251 grpc_rb_mTimeConsts, "ZERO", |
| 252 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
| 253 (void *)&zero_realtime)); |
| 254 rb_define_const( |
| 255 grpc_rb_mTimeConsts, "INFINITE_FUTURE", |
| 256 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
| 257 (void *)&inf_future_realtime)); |
| 258 rb_define_const( |
| 259 grpc_rb_mTimeConsts, "INFINITE_PAST", |
| 260 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type, |
| 261 (void *)&inf_past_realtime)); |
| 262 rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0); |
| 263 rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0); |
| 264 rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0); |
| 265 id_at = rb_intern("at"); |
| 266 id_inspect = rb_intern("inspect"); |
| 267 id_to_s = rb_intern("to_s"); |
| 268 id_tv_sec = rb_intern("tv_sec"); |
| 269 id_tv_nsec = rb_intern("tv_nsec"); |
| 270 } |
| 271 |
| 272 static void grpc_rb_shutdown(void) { |
| 273 grpc_shutdown(); |
| 274 } |
| 275 |
| 276 /* Initialize the GRPC module structs */ |
| 277 |
| 278 /* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */ |
| 279 VALUE grpc_rb_sNewServerRpc = Qnil; |
| 280 /* grpc_rb_sStatus is the struct that holds status details. */ |
| 281 VALUE grpc_rb_sStatus = Qnil; |
| 282 |
| 283 /* Initialize the GRPC module. */ |
| 284 VALUE grpc_rb_mGRPC = Qnil; |
| 285 VALUE grpc_rb_mGrpcCore = Qnil; |
| 286 |
| 287 /* cached Symbols for members in Status struct */ |
| 288 VALUE sym_code = Qundef; |
| 289 VALUE sym_details = Qundef; |
| 290 VALUE sym_metadata = Qundef; |
| 291 |
| 292 static gpr_once g_once_init = GPR_ONCE_INIT; |
| 293 |
| 294 static void grpc_ruby_once_init() { |
| 295 grpc_init(); |
| 296 atexit(grpc_rb_shutdown); |
| 297 } |
| 298 |
| 299 void Init_grpc_c() { |
| 300 if (!grpc_rb_load_core()) { |
| 301 rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core"); |
| 302 return; |
| 303 } |
| 304 |
| 305 /* ruby_vm_at_exit doesn't seem to be working. It would crash once every |
| 306 * blue moon, and some users are getting it repeatedly. See the discussions |
| 307 * - https://github.com/grpc/grpc/pull/5337 |
| 308 * - https://bugs.ruby-lang.org/issues/12095 |
| 309 * |
| 310 * In order to still be able to handle the (unlikely) situation where the |
| 311 * extension is loaded by a first Ruby VM that is subsequently destroyed, |
| 312 * then loaded again by another VM within the same process, we need to |
| 313 * schedule our initialization and destruction only once. |
| 314 */ |
| 315 gpr_once_init(&g_once_init, grpc_ruby_once_init); |
| 316 |
| 317 grpc_rb_mGRPC = rb_define_module("GRPC"); |
| 318 grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core"); |
| 319 grpc_rb_sNewServerRpc = |
| 320 rb_struct_define("NewServerRpc", "method", "host", |
| 321 "deadline", "metadata", "call", NULL); |
| 322 grpc_rb_sStatus = |
| 323 rb_struct_define("Status", "code", "details", "metadata", NULL); |
| 324 sym_code = ID2SYM(rb_intern("code")); |
| 325 sym_details = ID2SYM(rb_intern("details")); |
| 326 sym_metadata = ID2SYM(rb_intern("metadata")); |
| 327 |
| 328 Init_grpc_channel(); |
| 329 Init_grpc_completion_queue(); |
| 330 Init_grpc_call(); |
| 331 Init_grpc_call_credentials(); |
| 332 Init_grpc_channel_credentials(); |
| 333 Init_grpc_server(); |
| 334 Init_grpc_server_credentials(); |
| 335 Init_grpc_status_codes(); |
| 336 Init_grpc_time_consts(); |
| 337 } |
OLD | NEW |