grpc 1.56.2-x86_64-linux → 1.57.0.pre1-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,6 +24,7 @@
24
24
  #include "rb_grpc_imports.generated.h"
25
25
 
26
26
  #include <grpc/grpc.h>
27
+ #include <grpc/support/log.h>
27
28
 
28
29
  static rb_data_type_t grpc_rb_channel_args_data_type = {
29
30
  "grpc_channel_args",
@@ -73,13 +74,14 @@ static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key,
73
74
  case T_SYMBOL:
74
75
  args->args[args->num_args - 1].type = GRPC_ARG_STRING;
75
76
  args->args[args->num_args - 1].value.string =
76
- (char*)rb_id2name(SYM2ID(val));
77
+ strdup(rb_id2name(SYM2ID(val)));
77
78
  --args->num_args;
78
79
  return ST_CONTINUE;
79
80
 
80
81
  case T_STRING:
81
82
  args->args[args->num_args - 1].type = GRPC_ARG_STRING;
82
- args->args[args->num_args - 1].value.string = StringValueCStr(val);
83
+ args->args[args->num_args - 1].value.string =
84
+ strdup(StringValueCStr(val));
83
85
  --args->num_args;
84
86
  return ST_CONTINUE;
85
87
 
@@ -154,3 +156,15 @@ void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
154
156
  rb_jump_tag(status);
155
157
  }
156
158
  }
159
+
160
+ void grpc_rb_channel_args_destroy(grpc_channel_args* args) {
161
+ GPR_ASSERT(args != NULL);
162
+ if (args->args == NULL) return;
163
+ for (int i = 0; i < args->num_args; i++) {
164
+ if (args->args[i].type == GRPC_ARG_STRING) {
165
+ // we own string pointers, which were created with strdup
166
+ free(args->args[i].value.string);
167
+ }
168
+ }
169
+ xfree(args->args);
170
+ }
@@ -35,4 +35,8 @@
35
35
  void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
36
36
  grpc_channel_args* dst);
37
37
 
38
+ /* Destroys inner fields of args (does not deallocate the args pointer itself)
39
+ */
40
+ void grpc_rb_channel_args_destroy(grpc_channel_args* args);
41
+
38
42
  #endif /* GRPC_RB_CHANNEL_ARGS_H_ */
@@ -63,7 +63,6 @@ static void grpc_rb_channel_credentials_free_internal(void* p) {
63
63
  /* Destroys the credentials instances. */
64
64
  static void grpc_rb_channel_credentials_free(void* p) {
65
65
  grpc_rb_channel_credentials_free_internal(p);
66
- grpc_ruby_shutdown();
67
66
  }
68
67
 
69
68
  /* Protects the mark object from GC */
@@ -70,7 +70,6 @@ static void grpc_rb_compression_options_free_internal(void* p) {
70
70
  * wrapped grpc compression options. */
71
71
  static void grpc_rb_compression_options_free(void* p) {
72
72
  grpc_rb_compression_options_free_internal(p);
73
- grpc_ruby_shutdown();
74
73
  }
75
74
 
76
75
  /* Ruby recognized data type for the CompressionOptions class. */
@@ -51,6 +51,8 @@ typedef struct grpc_rb_event_queue {
51
51
  } grpc_rb_event_queue;
52
52
 
53
53
  static grpc_rb_event_queue event_queue;
54
+ static VALUE g_event_thread = Qnil;
55
+ static bool g_one_time_init_done = false;
54
56
 
55
57
  void grpc_rb_event_queue_enqueue(void (*callback)(void*), void* argument) {
56
58
  grpc_rb_event* event = gpr_malloc(sizeof(grpc_rb_event));
@@ -117,7 +119,6 @@ static void grpc_rb_event_unblocking_func(void* arg) {
117
119
  static VALUE grpc_rb_event_thread(VALUE arg) {
118
120
  grpc_rb_event* event;
119
121
  (void)arg;
120
- grpc_ruby_init();
121
122
  while (true) {
122
123
  event = (grpc_rb_event*)rb_thread_call_without_gvl(
123
124
  grpc_rb_wait_for_event_no_gil, NULL, grpc_rb_event_unblocking_func,
@@ -131,15 +132,30 @@ static VALUE grpc_rb_event_thread(VALUE arg) {
131
132
  }
132
133
  }
133
134
  grpc_rb_event_queue_destroy();
134
- grpc_ruby_shutdown();
135
135
  return Qnil;
136
136
  }
137
137
 
138
138
  void grpc_rb_event_queue_thread_start() {
139
- event_queue.head = event_queue.tail = NULL;
139
+ if (!g_one_time_init_done) {
140
+ g_one_time_init_done = true;
141
+ gpr_mu_init(&event_queue.mu);
142
+ gpr_cv_init(&event_queue.cv);
143
+ rb_global_variable(&g_event_thread);
144
+ event_queue.head = event_queue.tail = NULL;
145
+ }
140
146
  event_queue.abort = false;
141
- gpr_mu_init(&event_queue.mu);
142
- gpr_cv_init(&event_queue.cv);
147
+ GPR_ASSERT(!RTEST(g_event_thread));
148
+ g_event_thread = rb_thread_create(grpc_rb_event_thread, NULL);
149
+ }
143
150
 
144
- rb_thread_create(grpc_rb_event_thread, NULL);
151
+ void grpc_rb_event_queue_thread_stop() {
152
+ GPR_ASSERT(g_one_time_init_done);
153
+ if (!RTEST(g_event_thread)) {
154
+ gpr_log(GPR_ERROR,
155
+ "GRPC_RUBY: call credentials thread stop: thread not running");
156
+ return;
157
+ }
158
+ rb_thread_call_without_gvl(grpc_rb_event_unblocking_func, NULL, NULL, NULL);
159
+ rb_funcall(g_event_thread, rb_intern("join"), 0);
160
+ g_event_thread = Qnil;
145
161
  }
@@ -17,5 +17,6 @@
17
17
  */
18
18
 
19
19
  void grpc_rb_event_queue_thread_start();
20
+ void grpc_rb_event_queue_thread_stop();
20
21
 
21
22
  void grpc_rb_event_queue_enqueue(void (*callback)(void*), void* argument);
@@ -44,6 +44,11 @@
44
44
  #include <grpc/support/log.h>
45
45
  #include <grpc/support/time.h>
46
46
 
47
+ #ifdef GPR_LINUX
48
+ #include <sys/syscall.h>
49
+ #include <unistd.h>
50
+ #endif
51
+
47
52
  static VALUE grpc_rb_cTimeVal = Qnil;
48
53
 
49
54
  static rb_data_type_t grpc_rb_timespec_data_type = {
@@ -224,20 +229,51 @@ static void Init_grpc_time_consts() {
224
229
  id_tv_nsec = rb_intern("tv_nsec");
225
230
  }
226
231
 
232
+ static bool g_enable_fork_support;
233
+
234
+ #ifdef GPR_LINUX
235
+ static long sys_gettid() { return syscall(__NR_gettid); }
236
+ static bool can_enable_fork_support() { return true; }
237
+ #else
238
+ static long sys_gettid() { return 0; }
239
+ static bool can_enable_fork_support() { return false; }
240
+ #endif
241
+
227
242
  #if GPR_WINDOWS
228
- static void grpc_ruby_set_init_pid(void) {}
229
- static bool grpc_ruby_forked_after_init(void) { return false; }
243
+ static void grpc_ruby_basic_init(void) {}
244
+ static bool grpc_ruby_initial_pid(void) { return true; }
245
+ static bool grpc_ruby_initial_thread(void) { return true; }
246
+ static void grpc_ruby_reset_init_state(void) {}
230
247
  #else
231
- static pid_t grpc_init_pid;
248
+ static pid_t g_init_pid;
249
+ static long g_init_tid;
232
250
 
233
- static void grpc_ruby_set_init_pid(void) {
234
- GPR_ASSERT(grpc_init_pid == 0);
235
- grpc_init_pid = getpid();
251
+ static bool grpc_ruby_initial_pid(void) {
252
+ GPR_ASSERT(g_init_pid != 0);
253
+ return g_init_pid == getpid();
236
254
  }
237
255
 
238
- static bool grpc_ruby_forked_after_init(void) {
239
- GPR_ASSERT(grpc_init_pid != 0);
240
- return grpc_init_pid != getpid();
256
+ static bool grpc_ruby_initial_thread(void) {
257
+ GPR_ASSERT(g_init_tid != 0);
258
+ return sys_gettid() == g_init_tid;
259
+ }
260
+
261
+ static void grpc_ruby_reset_init_state(void) {
262
+ g_init_pid = getpid();
263
+ g_init_tid = sys_gettid();
264
+ }
265
+
266
+ static void grpc_ruby_basic_init(void) {
267
+ GPR_ASSERT(g_init_pid == 0);
268
+ GPR_ASSERT(g_init_tid == 0);
269
+ grpc_ruby_reset_init_state();
270
+ // TODO(apolcyn): ideally, we should share logic with C-core
271
+ // for determining whether or not fork support is enabled, rather
272
+ // than parsing the environment variable ourselves.
273
+ const char* res = getenv("GRPC_ENABLE_FORK_SUPPORT");
274
+ if (res != NULL && strcmp(res, "1") == 0) {
275
+ g_enable_fork_support = can_enable_fork_support();
276
+ }
241
277
  }
242
278
  #endif
243
279
 
@@ -258,59 +294,175 @@ VALUE sym_details = Qundef;
258
294
  VALUE sym_metadata = Qundef;
259
295
 
260
296
  static gpr_once g_once_init = GPR_ONCE_INIT;
297
+ static int64_t g_grpc_rb_prefork_pending; // synchronized by the GIL
298
+ static int64_t g_grpc_rb_num_fork_unsafe_threads; // synchronized by the GIL
261
299
 
262
300
  void grpc_ruby_fork_guard() {
263
- if (grpc_ruby_forked_after_init()) {
264
- rb_raise(rb_eRuntimeError, "grpc cannot be used before and after forking");
301
+ // Check if we're using gRPC between prefork and postfork
302
+ gpr_once_init(&g_once_init, grpc_ruby_basic_init);
303
+ if (g_grpc_rb_prefork_pending) {
304
+ rb_raise(rb_eRuntimeError,
305
+ "grpc cannot be used between calls to GRPC.prefork and "
306
+ "GRPC.postfork_child or GRPC.postfork_parent");
307
+ }
308
+ if (!grpc_ruby_initial_pid()) {
309
+ if (g_enable_fork_support) {
310
+ // Only way we can get here is by enabling for support and forking but not
311
+ // calling prefork
312
+ rb_raise(rb_eRuntimeError,
313
+ "grpc is in a broken state: GRPC.prefork must be called before "
314
+ "calling fork from a process using grpc");
315
+ } else {
316
+ rb_raise(rb_eRuntimeError,
317
+ "grpc cannot be used before and after forking unless the "
318
+ "GRPC_ENABLE_FORK_SUPPORT env var is set to \"1\" and the "
319
+ "platform supports it (linux only)");
320
+ }
265
321
  }
266
322
  }
267
323
 
268
- static VALUE bg_thread_init_rb_mu = Qundef;
269
- static int bg_thread_init_done = 0;
324
+ static VALUE g_bg_thread_init_rb_mu = Qundef;
325
+ static bool g_bg_thread_init_done;
270
326
 
271
327
  static void grpc_ruby_init_threads() {
272
328
  // Avoid calling into ruby library (when creating threads here)
273
329
  // in gpr_once_init. In general, it appears to be unsafe to call
274
330
  // into the ruby library while holding a non-ruby mutex, because a gil yield
275
331
  // could end up trying to lock onto that same mutex and deadlocking.
276
- rb_mutex_lock(bg_thread_init_rb_mu);
277
- if (!bg_thread_init_done) {
332
+ gpr_log(GPR_INFO,
333
+ "GRPC_RUBY: grpc_ruby_init_threads g_bg_thread_init_done=%d",
334
+ g_bg_thread_init_done);
335
+ rb_mutex_lock(g_bg_thread_init_rb_mu);
336
+ if (!g_bg_thread_init_done) {
278
337
  grpc_rb_event_queue_thread_start();
279
338
  grpc_rb_channel_polling_thread_start();
280
- bg_thread_init_done = 1;
339
+ g_bg_thread_init_done = true;
281
340
  }
282
- rb_mutex_unlock(bg_thread_init_rb_mu);
341
+ rb_mutex_unlock(g_bg_thread_init_rb_mu);
283
342
  }
284
343
 
285
344
  static int64_t g_grpc_ruby_init_count;
286
345
 
287
346
  void grpc_ruby_init() {
288
- gpr_once_init(&g_once_init, grpc_ruby_set_init_pid);
347
+ gpr_once_init(&g_once_init, grpc_ruby_basic_init);
348
+ grpc_ruby_fork_guard();
289
349
  grpc_init();
290
350
  grpc_ruby_init_threads();
291
351
  // (only gpr_log after logging has been initialized)
292
352
  gpr_log(GPR_DEBUG,
293
- "GRPC_RUBY: grpc_ruby_init - prev g_grpc_ruby_init_count:%" PRId64,
294
- g_grpc_ruby_init_count++);
353
+ "GRPC_RUBY: grpc_ruby_init - g_enable_fork_support=%d prev "
354
+ "g_grpc_ruby_init_count:%" PRId64,
355
+ g_enable_fork_support, g_grpc_ruby_init_count++);
356
+ }
357
+
358
+ // fork APIs, useable on linux with env var: GRPC_ENABLE_FORK_SUPPORT=1
359
+ //
360
+ // Must be called once and only once before forking. Must be called on the
361
+ // same threads that gRPC was (lazy-)initialized on. One must not call
362
+ // into the gRPC library during or after prefork has been called, until
363
+ // the corresponding postfork_{parent,child} APIs have been called.
364
+ static VALUE grpc_rb_prefork(VALUE self) {
365
+ // This might be the first time we've called into the grpc library, so make
366
+ // sure basic one-time initialization is taken care of. Note that if this is
367
+ // the case, then grpc_init() will start up c-core threads; that's OK since
368
+ // they will be shut down in C-core's pthread_atfork handler.
369
+ gpr_once_init(&g_once_init, grpc_ruby_basic_init);
370
+ grpc_init();
371
+ if (!g_enable_fork_support) {
372
+ rb_raise(rb_eRuntimeError,
373
+ "forking with gRPC/Ruby is only supported on linux with env var: "
374
+ "GRPC_ENABLE_FORK_SUPPORT=1");
375
+ }
376
+ if (g_grpc_rb_prefork_pending) {
377
+ rb_raise(rb_eRuntimeError,
378
+ "GRPC.prefork already called without a matching "
379
+ "GRPC.postfork_{parent,child}");
380
+ }
381
+ if (!grpc_ruby_initial_thread()) {
382
+ rb_raise(rb_eRuntimeError,
383
+ "GRPC.prefork and fork need to be called from the same thread "
384
+ "that GRPC was initialized on (GRPC lazy-initializes when when "
385
+ "the first GRPC object is created");
386
+ }
387
+ if (g_grpc_rb_num_fork_unsafe_threads > 0) {
388
+ rb_raise(
389
+ rb_eRuntimeError,
390
+ "Detected at least %ld threads actively using grpc, so it is not safe "
391
+ "call GRPC.prefork or fork. Note that grpc-ruby servers and "
392
+ "bidirectional "
393
+ "streams manage background threads and are not fork safe.",
394
+ g_grpc_rb_num_fork_unsafe_threads);
395
+ }
396
+ g_grpc_rb_prefork_pending = true;
397
+ rb_mutex_lock(g_bg_thread_init_rb_mu);
398
+ if (g_bg_thread_init_done) {
399
+ grpc_rb_channel_polling_thread_stop();
400
+ grpc_rb_event_queue_thread_stop();
401
+ // all ruby-level background threads joined at this point
402
+ g_bg_thread_init_done = false;
403
+ }
404
+ rb_mutex_unlock(g_bg_thread_init_rb_mu);
405
+ return Qnil;
295
406
  }
296
407
 
297
- void grpc_ruby_shutdown() {
298
- GPR_ASSERT(g_grpc_ruby_init_count > 0);
299
- if (!grpc_ruby_forked_after_init()) grpc_shutdown();
300
- gpr_log(
301
- GPR_DEBUG,
302
- "GRPC_RUBY: grpc_ruby_shutdown - prev g_grpc_ruby_init_count:%" PRId64,
303
- g_grpc_ruby_init_count--);
408
+ static VALUE grpc_rb_postfork_child(VALUE self) {
409
+ if (!g_grpc_rb_prefork_pending) {
410
+ rb_raise(rb_eRuntimeError,
411
+ "GRPC::postfork_child can only be called once following a "
412
+ "GRPC::prefork");
413
+ }
414
+ if (grpc_ruby_initial_pid()) {
415
+ rb_raise(rb_eRuntimeError,
416
+ "GRPC.postfork_child must be called only from the child process "
417
+ "after a fork");
418
+ }
419
+ grpc_ruby_reset_init_state();
420
+ grpc_ruby_init_threads();
421
+ g_grpc_rb_prefork_pending = false;
422
+ return Qnil;
304
423
  }
305
424
 
425
+ static VALUE grpc_rb_postfork_parent(VALUE self) {
426
+ // TODO(apolcyn): check calling thread vs. thread that gRPC was initialized on
427
+ if (!g_grpc_rb_prefork_pending) {
428
+ rb_raise(rb_eRuntimeError,
429
+ "GRPC::postfork_parent can only be called once following a "
430
+ "GRPC::prefork");
431
+ }
432
+ if (!grpc_ruby_initial_pid()) {
433
+ rb_raise(rb_eRuntimeError,
434
+ "GRPC.postfork_parent must be called only from the parent process "
435
+ "after a fork");
436
+ }
437
+ if (!grpc_ruby_initial_thread()) {
438
+ rb_raise(rb_eRuntimeError,
439
+ "GRPC.postfork_parent needs to be called from the same thread "
440
+ "that GRPC.prefork (and fork) was called from");
441
+ }
442
+ grpc_ruby_init_threads();
443
+ g_grpc_rb_prefork_pending = false;
444
+ return Qnil;
445
+ }
446
+
447
+ // APIs to mark fork-unsafe sections from C-extension code
448
+ void grpc_rb_fork_unsafe_begin() { g_grpc_rb_num_fork_unsafe_threads++; }
449
+
450
+ void grpc_rb_fork_unsafe_end() { g_grpc_rb_num_fork_unsafe_threads--; }
451
+
452
+ // APIs to mark fork-unsafe sections from ruby code
453
+ static VALUE grpc_rb_fork_unsafe_begin_api() { grpc_rb_fork_unsafe_begin(); }
454
+
455
+ static VALUE grpc_rb_fork_unsafe_end_api() { grpc_rb_fork_unsafe_end(); }
456
+
457
+ // One-time initialization
306
458
  void Init_grpc_c() {
307
459
  if (!grpc_rb_load_core()) {
308
460
  rb_raise(rb_eLoadError, "Couldn't find or load gRPC's dynamic C core");
309
461
  return;
310
462
  }
311
463
 
312
- rb_global_variable(&bg_thread_init_rb_mu);
313
- bg_thread_init_rb_mu = rb_mutex_new();
464
+ rb_global_variable(&g_bg_thread_init_rb_mu);
465
+ g_bg_thread_init_rb_mu = rb_mutex_new();
314
466
 
315
467
  grpc_rb_mGRPC = rb_define_module("GRPC");
316
468
  grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
@@ -320,7 +472,7 @@ void Init_grpc_c() {
320
472
  sym_code = ID2SYM(rb_intern("code"));
321
473
  sym_details = ID2SYM(rb_intern("details"));
322
474
  sym_metadata = ID2SYM(rb_intern("metadata"));
323
-
475
+ // init C-defined classes
324
476
  Init_grpc_channel();
325
477
  Init_grpc_call();
326
478
  Init_grpc_call_credentials();
@@ -331,4 +483,14 @@ void Init_grpc_c() {
331
483
  Init_grpc_xds_server_credentials();
332
484
  Init_grpc_time_consts();
333
485
  Init_grpc_compression_options();
486
+ // define fork APIs
487
+ rb_define_module_function(grpc_rb_mGRPC, "prefork", grpc_rb_prefork, 0);
488
+ rb_define_module_function(grpc_rb_mGRPC, "postfork_child",
489
+ grpc_rb_postfork_child, 0);
490
+ rb_define_module_function(grpc_rb_mGRPC, "postfork_parent",
491
+ grpc_rb_postfork_parent, 0);
492
+ rb_define_module_function(grpc_rb_mGrpcCore, "fork_unsafe_begin",
493
+ grpc_rb_fork_unsafe_begin_api, 0);
494
+ rb_define_module_function(grpc_rb_mGrpcCore, "fork_unsafe_end",
495
+ grpc_rb_fork_unsafe_end_api, 0);
334
496
  }
@@ -70,8 +70,14 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval);
70
70
 
71
71
  void grpc_ruby_fork_guard();
72
72
 
73
- void grpc_ruby_init();
73
+ /* To be called once and only once before entering code section that is
74
+ * definitely not fork-safe. Used in conjunction with GRPC.prefork
75
+ * to catch for-unsafe processes and raise errors. */
76
+ void grpc_rb_fork_unsafe_begin();
77
+
78
+ /* To be called once and only once after each grpc_rb_fork_unsafe_begin*/
79
+ void grpc_rb_fork_unsafe_end();
74
80
 
75
- void grpc_ruby_shutdown();
81
+ void grpc_ruby_init();
76
82
 
77
83
  #endif /* GRPC_RB_H_ */
@@ -106,10 +106,7 @@ static void grpc_rb_server_free_internal(void* p) {
106
106
  }
107
107
 
108
108
  /* Destroys server instances. */
109
- static void grpc_rb_server_free(void* p) {
110
- grpc_rb_server_free_internal(p);
111
- grpc_ruby_shutdown();
112
- }
109
+ static void grpc_rb_server_free(void* p) { grpc_rb_server_free_internal(p); }
113
110
 
114
111
  static const rb_data_type_t grpc_rb_server_data_type = {
115
112
  "grpc_server",
@@ -155,10 +152,7 @@ static VALUE grpc_rb_server_init(VALUE self, VALUE channel_args) {
155
152
  wrapper);
156
153
  grpc_rb_hash_convert_to_channel_args(channel_args, &args);
157
154
  srv = grpc_server_create(&args, NULL);
158
-
159
- if (args.args != NULL) {
160
- xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
161
- }
155
+ grpc_rb_channel_args_destroy(&args);
162
156
  if (srv == NULL) {
163
157
  rb_raise(rb_eRuntimeError, "could not create a gRPC server, not sure why");
164
158
  }
@@ -191,65 +185,88 @@ static void grpc_request_call_stack_cleanup(request_call_stack* st) {
191
185
  grpc_call_details_destroy(&st->details);
192
186
  }
193
187
 
194
- /* call-seq:
195
- server.request_call
188
+ struct server_request_call_args {
189
+ grpc_rb_server* server;
190
+ grpc_completion_queue* call_queue;
191
+ request_call_stack st;
192
+ };
193
+
194
+ static VALUE grpc_rb_server_request_call_try(VALUE value_args) {
195
+ grpc_rb_fork_unsafe_begin();
196
+ struct server_request_call_args* args =
197
+ (struct server_request_call_args*)value_args;
196
198
 
197
- Requests notification of a new call on a server. */
198
- static VALUE grpc_rb_server_request_call(VALUE self) {
199
- grpc_rb_server* s = NULL;
200
199
  grpc_call* call = NULL;
201
- grpc_event ev;
202
- grpc_call_error err;
203
- request_call_stack st;
204
- VALUE result;
205
- void* tag = (void*)&st;
206
- grpc_completion_queue* call_queue =
207
- grpc_completion_queue_create_for_pluck(NULL);
208
- gpr_timespec deadline;
200
+ void* tag = (void*)&args->st;
201
+
202
+ args->call_queue = grpc_completion_queue_create_for_pluck(NULL);
203
+ grpc_request_call_stack_init(&args->st);
209
204
 
210
- TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
211
- if (s->wrapped == NULL) {
212
- rb_raise(rb_eRuntimeError, "destroyed!");
213
- return Qnil;
214
- }
215
- grpc_request_call_stack_init(&st);
216
205
  /* call grpc_server_request_call, then wait for it to complete using
217
206
  * pluck_event */
218
- err = grpc_server_request_call(s->wrapped, &call, &st.details, &st.md_ary,
219
- call_queue, s->queue, tag);
207
+ grpc_call_error err = grpc_server_request_call(
208
+ args->server->wrapped, &call, &args->st.details, &args->st.md_ary,
209
+ args->call_queue, args->server->queue, tag);
220
210
  if (err != GRPC_CALL_OK) {
221
- grpc_request_call_stack_cleanup(&st);
222
211
  rb_raise(grpc_rb_eCallError,
223
212
  "grpc_server_request_call failed: %s (code=%d)",
224
213
  grpc_call_error_detail_of(err), err);
225
- return Qnil;
226
214
  }
227
215
 
228
- ev = rb_completion_queue_pluck(s->queue, tag,
229
- gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
216
+ grpc_event ev = rb_completion_queue_pluck(
217
+ args->server->queue, tag, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
230
218
  if (!ev.success) {
231
- grpc_request_call_stack_cleanup(&st);
232
219
  rb_raise(grpc_rb_eCallError, "request_call completion failed");
233
- return Qnil;
234
220
  }
235
221
 
236
222
  /* build the NewServerRpc struct result */
237
- deadline = gpr_convert_clock_type(st.details.deadline, GPR_CLOCK_REALTIME);
238
- result = rb_struct_new(
239
- grpc_rb_sNewServerRpc, grpc_rb_slice_to_ruby_string(st.details.method),
240
- grpc_rb_slice_to_ruby_string(st.details.host),
241
- rb_funcall(rb_cTime, id_at, 2, INT2NUM(deadline.tv_sec),
242
- INT2NUM(deadline.tv_nsec / 1000)),
243
- grpc_rb_md_ary_to_h(&st.md_ary), grpc_rb_wrap_call(call, call_queue),
244
- NULL);
245
- grpc_request_call_stack_cleanup(&st);
223
+ gpr_timespec deadline =
224
+ gpr_convert_clock_type(args->st.details.deadline, GPR_CLOCK_REALTIME);
225
+ VALUE result =
226
+ rb_struct_new(grpc_rb_sNewServerRpc,
227
+ grpc_rb_slice_to_ruby_string(args->st.details.method),
228
+ grpc_rb_slice_to_ruby_string(args->st.details.host),
229
+ rb_funcall(rb_cTime, id_at, 2, INT2NUM(deadline.tv_sec),
230
+ INT2NUM(deadline.tv_nsec / 1000)),
231
+ grpc_rb_md_ary_to_h(&args->st.md_ary),
232
+ grpc_rb_wrap_call(call, args->call_queue), NULL);
233
+ args->call_queue = NULL;
246
234
  return result;
247
235
  }
248
236
 
237
+ static VALUE grpc_rb_server_request_call_ensure(VALUE value_args) {
238
+ grpc_rb_fork_unsafe_end();
239
+ struct server_request_call_args* args =
240
+ (struct server_request_call_args*)value_args;
241
+
242
+ if (args->call_queue) {
243
+ grpc_rb_completion_queue_destroy(args->call_queue);
244
+ }
245
+
246
+ grpc_request_call_stack_cleanup(&args->st);
247
+
248
+ return Qnil;
249
+ }
250
+
251
+ /* call-seq:
252
+ server.request_call
253
+
254
+ Requests notification of a new call on a server. */
255
+ static VALUE grpc_rb_server_request_call(VALUE self) {
256
+ grpc_rb_server* s;
257
+ TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
258
+ grpc_ruby_fork_guard();
259
+ if (s->wrapped == NULL) {
260
+ rb_raise(rb_eRuntimeError, "destroyed!");
261
+ }
262
+ struct server_request_call_args args = {.server = s, .call_queue = NULL};
263
+ return rb_ensure(grpc_rb_server_request_call_try, (VALUE)&args,
264
+ grpc_rb_server_request_call_ensure, (VALUE)&args);
265
+ }
266
+
249
267
  static VALUE grpc_rb_server_start(VALUE self) {
250
268
  grpc_rb_server* s = NULL;
251
269
  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
252
-
253
270
  grpc_ruby_fork_guard();
254
271
  if (s->wrapped == NULL) {
255
272
  rb_raise(rb_eRuntimeError, "destroyed!");
@@ -62,7 +62,6 @@ static void grpc_rb_server_credentials_free_internal(void* p) {
62
62
  /* Destroys the server credentials instances. */
63
63
  static void grpc_rb_server_credentials_free(void* p) {
64
64
  grpc_rb_server_credentials_free_internal(p);
65
- grpc_ruby_shutdown();
66
65
  }
67
66
 
68
67
  /* Protects the mark object from GC */
@@ -62,7 +62,6 @@ static void grpc_rb_xds_channel_credentials_free_internal(void* p) {
62
62
  /* Destroys the credentials instances. */
63
63
  static void grpc_rb_xds_channel_credentials_free(void* p) {
64
64
  grpc_rb_xds_channel_credentials_free_internal(p);
65
- grpc_ruby_shutdown();
66
65
  }
67
66
 
68
67
  /* Protects the mark object from GC */
@@ -63,7 +63,6 @@ static void grpc_rb_xds_server_credentials_free_internal(void* p) {
63
63
  /* Destroys the server credentials instances. */
64
64
  static void grpc_rb_xds_server_credentials_free(void* p) {
65
65
  grpc_rb_xds_server_credentials_free_internal(p);
66
- grpc_ruby_shutdown();
67
66
  }
68
67
 
69
68
  /* Protects the mark object from GC */
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -141,6 +141,7 @@ module GRPC
141
141
  # set_output_stream_done is relevant on client-side
142
142
  # rubocop:disable Metrics/PerceivedComplexity
143
143
  def write_loop(requests, is_client: true, set_output_stream_done: nil)
144
+ GRPC::Core.fork_unsafe_begin
144
145
  GRPC.logger.debug('bidi-write-loop: starting')
145
146
  count = 0
146
147
  requests.each do |req|
@@ -181,6 +182,7 @@ module GRPC
181
182
  raise e
182
183
  end
183
184
  ensure
185
+ GRPC::Core.fork_unsafe_end
184
186
  set_output_stream_done.call if is_client
185
187
  end
186
188
  # rubocop:enable Metrics/PerceivedComplexity
@@ -14,5 +14,5 @@
14
14
 
15
15
  # GRPC contains the General RPC module.
16
16
  module GRPC
17
- VERSION = '1.56.2'
17
+ VERSION = '1.57.0.pre1'
18
18
  end