grpc 1.2.0 → 1.2.1.pre1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8060071c5cb9a2c789bb97636b3b41462306ee87
4
- data.tar.gz: 0ea743e36f192f8bda07065bb125c8dd0f0852e7
3
+ metadata.gz: 210d5851abb9539a5ef0b9ee48ec5c7873c99af1
4
+ data.tar.gz: fff44d43a3444d9c04ec3ccbe2e00b39b6b95694
5
5
  SHA512:
6
- metadata.gz: 9b65336e26b944cded2dea12343b4ad0fcd5a3fdd5c194af35d211ab43888a9f7f52f4f63c7bc644a31fb786fffe525f3e3aa75e40ad4947b6d2d31c439cc3ee
7
- data.tar.gz: 892314c98a7977d99d412a8667a20ec800d09eaec5cafc71605474747f11c5b5bda1e9612f08b27e6bef0f1c4ce86157741426cca3ff4c4460564aa50a9fd410
6
+ metadata.gz: 82446a5b38485f523de8733c8268d9f61d0972a0fef1b026f734a27a67863d2919a6193d42f2018d74f11213873487192e3f3143b105f6c9c4c97d3491ef1148
7
+ data.tar.gz: 2d8e175716214f4286a7c73c25dd7bf51c5214c68478d5d6d052a111fc3ea2eb850fbecf32cd537cd40bfbd2e308d73eda08cc69260ab3246677cd5f7490354e
data/Makefile CHANGED
@@ -412,8 +412,8 @@ Q = @
412
412
  endif
413
413
 
414
414
  CORE_VERSION = 3.0.0-dev
415
- CPP_VERSION = 1.2.0
416
- CSHARP_VERSION = 1.2.0
415
+ CPP_VERSION = 1.2.1-pre1
416
+ CSHARP_VERSION = 1.2.1-pre1
417
417
 
418
418
  CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
419
419
  CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)
@@ -32,21 +32,22 @@
32
32
  */
33
33
 
34
34
  #include <ruby/ruby.h>
35
+ #include <ruby/thread.h>
35
36
 
36
37
  #include "rb_grpc_imports.generated.h"
37
- #include "rb_channel.h"
38
38
  #include "rb_byte_buffer.h"
39
+ #include "rb_channel.h"
39
40
 
40
41
  #include <grpc/grpc.h>
41
42
  #include <grpc/grpc_security.h>
42
43
  #include <grpc/support/alloc.h>
43
44
  #include <grpc/support/log.h>
44
45
  #include <grpc/support/time.h>
45
- #include "rb_grpc.h"
46
46
  #include "rb_call.h"
47
47
  #include "rb_channel_args.h"
48
48
  #include "rb_channel_credentials.h"
49
49
  #include "rb_completion_queue.h"
50
+ #include "rb_grpc.h"
50
51
  #include "rb_server.h"
51
52
 
52
53
  /* id_channel is the name of the hidden ivar that preserves a reference to the
@@ -73,9 +74,26 @@ typedef struct grpc_rb_channel {
73
74
 
74
75
  /* The actual channel */
75
76
  grpc_channel *wrapped;
76
- grpc_completion_queue *queue;
77
+ int request_safe_destroy;
78
+ int safe_to_destroy;
79
+ grpc_connectivity_state current_connectivity_state;
80
+
81
+ int mu_init_done;
82
+ int abort_watch_connectivity_state;
83
+ gpr_mu channel_mu;
84
+ gpr_cv channel_cv;
77
85
  } grpc_rb_channel;
78
86
 
87
+ /* Forward declarations of functions involved in temporary fix to
88
+ * https://github.com/grpc/grpc/issues/9941 */
89
+ static void grpc_rb_channel_try_register_connection_polling(
90
+ grpc_rb_channel *wrapper);
91
+ static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper);
92
+
93
+ static grpc_completion_queue *channel_polling_cq;
94
+ static gpr_mu global_connection_polling_mu;
95
+ static int abort_channel_polling = 0;
96
+
79
97
  /* Destroys Channel instances. */
80
98
  static void grpc_rb_channel_free(void *p) {
81
99
  grpc_rb_channel *ch = NULL;
@@ -85,8 +103,13 @@ static void grpc_rb_channel_free(void *p) {
85
103
  ch = (grpc_rb_channel *)p;
86
104
 
87
105
  if (ch->wrapped != NULL) {
88
- grpc_channel_destroy(ch->wrapped);
89
- grpc_rb_completion_queue_destroy(ch->queue);
106
+ grpc_rb_channel_safe_destroy(ch);
107
+ ch->wrapped = NULL;
108
+ }
109
+
110
+ if (ch->mu_init_done) {
111
+ gpr_mu_destroy(&ch->channel_mu);
112
+ gpr_cv_destroy(&ch->channel_cv);
90
113
  }
91
114
 
92
115
  xfree(p);
@@ -104,13 +127,15 @@ static void grpc_rb_channel_mark(void *p) {
104
127
  }
105
128
  }
106
129
 
107
- static rb_data_type_t grpc_channel_data_type = {
108
- "grpc_channel",
109
- {grpc_rb_channel_mark, grpc_rb_channel_free, GRPC_RB_MEMSIZE_UNAVAILABLE,
110
- {NULL, NULL}},
111
- NULL, NULL,
130
+ static rb_data_type_t grpc_channel_data_type = {"grpc_channel",
131
+ {grpc_rb_channel_mark,
132
+ grpc_rb_channel_free,
133
+ GRPC_RB_MEMSIZE_UNAVAILABLE,
134
+ {NULL, NULL}},
135
+ NULL,
136
+ NULL,
112
137
  #ifdef RUBY_TYPED_FREE_IMMEDIATELY
113
- RUBY_TYPED_FREE_IMMEDIATELY
138
+ RUBY_TYPED_FREE_IMMEDIATELY
114
139
  #endif
115
140
  };
116
141
 
@@ -145,6 +170,7 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
145
170
  rb_scan_args(argc, argv, "3", &target, &channel_args, &credentials);
146
171
 
147
172
  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
173
+ wrapper->mu_init_done = 0;
148
174
  target_chars = StringValueCStr(target);
149
175
  grpc_rb_hash_convert_to_channel_args(channel_args, &args);
150
176
  if (TYPE(credentials) == T_SYMBOL) {
@@ -159,6 +185,27 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
159
185
  creds = grpc_rb_get_wrapped_channel_credentials(credentials);
160
186
  ch = grpc_secure_channel_create(creds, target_chars, &args, NULL);
161
187
  }
188
+
189
+ GPR_ASSERT(ch);
190
+
191
+ wrapper->wrapped = ch;
192
+
193
+ gpr_mu_init(&wrapper->channel_mu);
194
+ gpr_cv_init(&wrapper->channel_cv);
195
+ wrapper->mu_init_done = 1;
196
+
197
+ gpr_mu_lock(&wrapper->channel_mu);
198
+ wrapper->abort_watch_connectivity_state = 0;
199
+ wrapper->current_connectivity_state = grpc_channel_check_connectivity_state(wrapper->wrapped, 0);
200
+ wrapper->safe_to_destroy = 0;
201
+ wrapper->request_safe_destroy = 0;
202
+
203
+ gpr_cv_broadcast(&wrapper->channel_cv);
204
+ gpr_mu_unlock(&wrapper->channel_mu);
205
+
206
+
207
+ grpc_rb_channel_try_register_connection_polling(wrapper);
208
+
162
209
  if (args.args != NULL) {
163
210
  xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
164
211
  }
@@ -169,25 +216,28 @@ static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
169
216
  }
170
217
  rb_ivar_set(self, id_target, target);
171
218
  wrapper->wrapped = ch;
172
- wrapper->queue = grpc_completion_queue_create(NULL);
173
219
  return self;
174
220
  }
175
221
 
176
222
  /*
177
223
  call-seq:
178
- insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'})
179
- creds = ...
180
- secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds)
224
+ ch.connectivity_state -> state
225
+ ch.connectivity_state(true) -> state
181
226
 
182
- Creates channel instances. */
227
+ Indicates the current state of the channel, whose value is one of the
228
+ constants defined in GRPC::Core::ConnectivityStates.
229
+
230
+ It also tries to connect if the chennel is idle in the second form. */
183
231
  static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv,
184
232
  VALUE self) {
185
- VALUE try_to_connect = Qfalse;
233
+ VALUE try_to_connect_param = Qfalse;
234
+ int grpc_try_to_connect = 0;
186
235
  grpc_rb_channel *wrapper = NULL;
187
236
  grpc_channel *ch = NULL;
188
237
 
189
238
  /* "01" == 0 mandatory args, 1 (try_to_connect) is optional */
190
- rb_scan_args(argc, argv, "01", try_to_connect);
239
+ rb_scan_args(argc, argv, "01", &try_to_connect_param);
240
+ grpc_try_to_connect = RTEST(try_to_connect_param) ? 1 : 0;
191
241
 
192
242
  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
193
243
  ch = wrapper->wrapped;
@@ -195,57 +245,88 @@ static VALUE grpc_rb_channel_get_connectivity_state(int argc, VALUE *argv,
195
245
  rb_raise(rb_eRuntimeError, "closed!");
196
246
  return Qnil;
197
247
  }
198
- return NUM2LONG(
199
- grpc_channel_check_connectivity_state(ch, (int)try_to_connect));
248
+ return LONG2NUM(grpc_channel_check_connectivity_state(wrapper->wrapped, grpc_try_to_connect));
200
249
  }
201
250
 
202
- /* Watch for a change in connectivity state.
251
+ typedef struct watch_state_stack {
252
+ grpc_rb_channel *wrapper;
253
+ gpr_timespec deadline;
254
+ int last_state;
255
+ } watch_state_stack;
256
+
257
+ static void *watch_channel_state_without_gvl(void *arg) {
258
+ watch_state_stack *stack = (watch_state_stack*)arg;
259
+ gpr_timespec deadline = stack->deadline;
260
+ grpc_rb_channel *wrapper = stack->wrapper;
261
+ int last_state = stack->last_state;
262
+ void *return_value = (void*)0;
263
+
264
+ gpr_mu_lock(&wrapper->channel_mu);
265
+ while(wrapper->current_connectivity_state == last_state &&
266
+ !wrapper->request_safe_destroy &&
267
+ !wrapper->safe_to_destroy &&
268
+ !wrapper->abort_watch_connectivity_state &&
269
+ gpr_time_cmp(deadline, gpr_now(GPR_CLOCK_REALTIME)) > 0) {
270
+ gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu, deadline);
271
+ }
272
+ if (wrapper->current_connectivity_state != last_state) {
273
+ return_value = (void*)1;
274
+ }
275
+ gpr_mu_unlock(&wrapper->channel_mu);
276
+
277
+ return return_value;
278
+ }
203
279
 
204
- Once the channel connectivity state is different from the last observed
205
- state, tag will be enqueued on cq with success=1
280
+ static void watch_channel_state_unblocking_func(void *arg) {
281
+ grpc_rb_channel *wrapper = (grpc_rb_channel*)arg;
282
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: watch channel state unblocking func called");
283
+ gpr_mu_lock(&wrapper->channel_mu);
284
+ wrapper->abort_watch_connectivity_state = 1;
285
+ gpr_cv_broadcast(&wrapper->channel_cv);
286
+ gpr_mu_unlock(&wrapper->channel_mu);
287
+ }
206
288
 
207
- If deadline expires BEFORE the state is changed, tag will be enqueued on
208
- the completion queue with success=0 */
289
+ /* Wait until the channel's connectivity state becomes different from
290
+ * "last_state", or "deadline" expires.
291
+ * Returns true if the the channel's connectivity state becomes
292
+ * different from "last_state" within "deadline".
293
+ * Returns false if "deadline" expires before the channel's connectivity
294
+ * state changes from "last_state".
295
+ * */
209
296
  static VALUE grpc_rb_channel_watch_connectivity_state(VALUE self,
210
297
  VALUE last_state,
211
298
  VALUE deadline) {
212
299
  grpc_rb_channel *wrapper = NULL;
213
- grpc_channel *ch = NULL;
214
- grpc_completion_queue *cq = NULL;
215
-
216
- void *tag = wrapper;
217
-
218
- grpc_event event;
300
+ watch_state_stack stack;
301
+ void* out;
219
302
 
220
303
  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
221
- ch = wrapper->wrapped;
222
- cq = wrapper->queue;
223
- if (ch == NULL) {
304
+
305
+ if (wrapper->wrapped == NULL) {
224
306
  rb_raise(rb_eRuntimeError, "closed!");
225
307
  return Qnil;
226
308
  }
227
- grpc_channel_watch_connectivity_state(
228
- ch,
229
- (grpc_connectivity_state)NUM2LONG(last_state),
230
- grpc_rb_time_timeval(deadline, /* absolute time */ 0),
231
- cq,
232
- tag);
233
309
 
234
- event = rb_completion_queue_pluck(cq, tag,
235
- gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
310
+ if (!FIXNUM_P(last_state)) {
311
+ rb_raise(rb_eTypeError, "bad type for last_state. want a GRPC::Core::ChannelState constant");
312
+ return Qnil;
313
+ }
236
314
 
237
- if (event.success) {
315
+ stack.wrapper = wrapper;
316
+ stack.deadline = grpc_rb_time_timeval(deadline, 0);
317
+ stack.last_state = NUM2LONG(last_state);
318
+ out = rb_thread_call_without_gvl(watch_channel_state_without_gvl, &stack, watch_channel_state_unblocking_func, wrapper);
319
+ if (out) {
238
320
  return Qtrue;
239
- } else {
240
- return Qfalse;
241
321
  }
322
+ return Qfalse;
242
323
  }
243
324
 
244
325
  /* Create a call given a grpc_channel, in order to call method. The request
245
326
  is not sent until grpc_call_invoke is called. */
246
- static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent,
247
- VALUE mask, VALUE method,
248
- VALUE host, VALUE deadline) {
327
+ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent, VALUE mask,
328
+ VALUE method, VALUE host,
329
+ VALUE deadline) {
249
330
  VALUE res = Qnil;
250
331
  grpc_rb_channel *wrapper = NULL;
251
332
  grpc_call *call = NULL;
@@ -256,10 +337,11 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent,
256
337
  grpc_slice method_slice;
257
338
  grpc_slice host_slice;
258
339
  grpc_slice *host_slice_ptr = NULL;
259
- char* tmp_str = NULL;
340
+ char *tmp_str = NULL;
260
341
 
261
342
  if (host != Qnil) {
262
- host_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(host), RSTRING_LEN(host));
343
+ host_slice =
344
+ grpc_slice_from_copied_buffer(RSTRING_PTR(host), RSTRING_LEN(host));
263
345
  host_slice_ptr = &host_slice;
264
346
  }
265
347
  if (mask != Qnil) {
@@ -277,17 +359,18 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent,
277
359
  return Qnil;
278
360
  }
279
361
 
280
- method_slice = grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method));
362
+ method_slice =
363
+ grpc_slice_from_copied_buffer(RSTRING_PTR(method), RSTRING_LEN(method));
281
364
 
282
365
  call = grpc_channel_create_call(ch, parent_call, flags, cq, method_slice,
283
- host_slice_ptr, grpc_rb_time_timeval(
284
- deadline,
285
- /* absolute time */ 0), NULL);
366
+ host_slice_ptr,
367
+ grpc_rb_time_timeval(deadline,
368
+ /* absolute time */ 0),
369
+ NULL);
286
370
 
287
371
  if (call == NULL) {
288
372
  tmp_str = grpc_slice_to_c_string(method_slice);
289
- rb_raise(rb_eRuntimeError, "cannot create call with method %s",
290
- tmp_str);
373
+ rb_raise(rb_eRuntimeError, "cannot create call with method %s", tmp_str);
291
374
  return Qnil;
292
375
  }
293
376
 
@@ -304,7 +387,6 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE parent,
304
387
  return res;
305
388
  }
306
389
 
307
-
308
390
  /* Closes the channel, calling it's destroy method */
309
391
  static VALUE grpc_rb_channel_destroy(VALUE self) {
310
392
  grpc_rb_channel *wrapper = NULL;
@@ -313,19 +395,18 @@ static VALUE grpc_rb_channel_destroy(VALUE self) {
313
395
  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
314
396
  ch = wrapper->wrapped;
315
397
  if (ch != NULL) {
316
- grpc_channel_destroy(ch);
398
+ grpc_rb_channel_safe_destroy(wrapper);
317
399
  wrapper->wrapped = NULL;
318
400
  }
319
401
 
320
402
  return Qnil;
321
403
  }
322
404
 
323
-
324
405
  /* Called to obtain the target that this channel accesses. */
325
406
  static VALUE grpc_rb_channel_get_target(VALUE self) {
326
407
  grpc_rb_channel *wrapper = NULL;
327
408
  VALUE res = Qnil;
328
- char* target = NULL;
409
+ char *target = NULL;
329
410
 
330
411
  TypedData_Get_Struct(self, grpc_rb_channel, &grpc_channel_data_type, wrapper);
331
412
  target = grpc_channel_get_target(wrapper->wrapped);
@@ -335,10 +416,122 @@ static VALUE grpc_rb_channel_get_target(VALUE self) {
335
416
  return res;
336
417
  }
337
418
 
419
+ // Either start polling channel connection state or signal that it's free to
420
+ // destroy.
421
+ // Not safe to call while a channel's connection state is polled.
422
+ static void grpc_rb_channel_try_register_connection_polling(
423
+ grpc_rb_channel *wrapper) {
424
+ grpc_connectivity_state conn_state;
425
+ gpr_timespec sleep_time = gpr_time_add(
426
+ gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(20, GPR_TIMESPAN));
427
+
428
+ GPR_ASSERT(wrapper);
429
+ GPR_ASSERT(wrapper->wrapped);
430
+ gpr_mu_lock(&wrapper->channel_mu);
431
+ if (wrapper->request_safe_destroy) {
432
+ wrapper->safe_to_destroy = 1;
433
+ gpr_cv_broadcast(&wrapper->channel_cv);
434
+ gpr_mu_unlock(&wrapper->channel_mu);
435
+ return;
436
+ }
437
+ gpr_mu_lock(&global_connection_polling_mu);
438
+
439
+ conn_state = grpc_channel_check_connectivity_state(wrapper->wrapped, 0);
440
+ if (conn_state != wrapper->current_connectivity_state) {
441
+ wrapper->current_connectivity_state = conn_state;
442
+ gpr_cv_broadcast(&wrapper->channel_cv);
443
+ }
444
+ // avoid posting work to the channel polling cq if it's been shutdown
445
+ if (!abort_channel_polling && conn_state != GRPC_CHANNEL_SHUTDOWN) {
446
+ grpc_channel_watch_connectivity_state(
447
+ wrapper->wrapped, conn_state, sleep_time, channel_polling_cq, wrapper);
448
+ } else {
449
+ wrapper->safe_to_destroy = 1;
450
+ gpr_cv_broadcast(&wrapper->channel_cv);
451
+ }
452
+ gpr_mu_unlock(&global_connection_polling_mu);
453
+ gpr_mu_unlock(&wrapper->channel_mu);
454
+ }
455
+
456
+ // Note requires wrapper->wrapped, wrapper->channel_mu/cv initialized
457
+ static void grpc_rb_channel_safe_destroy(grpc_rb_channel *wrapper) {
458
+ gpr_mu_lock(&wrapper->channel_mu);
459
+ wrapper->request_safe_destroy = 1;
460
+
461
+ while (!wrapper->safe_to_destroy) {
462
+ gpr_cv_wait(&wrapper->channel_cv, &wrapper->channel_mu,
463
+ gpr_inf_future(GPR_CLOCK_REALTIME));
464
+ }
465
+ GPR_ASSERT(wrapper->safe_to_destroy);
466
+ gpr_mu_unlock(&wrapper->channel_mu);
467
+
468
+ grpc_channel_destroy(wrapper->wrapped);
469
+ }
470
+
471
+ // Note this loop breaks out with a single call of
472
+ // "grpc_rb_event_unblocking_func".
473
+ // This assumes that a ruby call the unblocking func
474
+ // indicates process shutdown.
475
+ // In the worst case, this stops polling channel connectivity
476
+ // early and falls back to current behavior.
477
+ static void *run_poll_channels_loop_no_gil(void *arg) {
478
+ grpc_event event;
479
+ (void)arg;
480
+ for (;;) {
481
+ event = grpc_completion_queue_next(
482
+ channel_polling_cq, gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
483
+ if (event.type == GRPC_QUEUE_SHUTDOWN) {
484
+ break;
485
+ }
486
+ if (event.type == GRPC_OP_COMPLETE) {
487
+ grpc_rb_channel_try_register_connection_polling((grpc_rb_channel *)event.tag);
488
+ }
489
+ }
490
+ grpc_completion_queue_destroy(channel_polling_cq);
491
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop_no_gil - exit connection polling loop");
492
+ return NULL;
493
+ }
494
+
495
+ // Notify the channel polling loop to cleanup and shutdown.
496
+ static void grpc_rb_event_unblocking_func(void *arg) {
497
+ (void)arg;
498
+ gpr_mu_lock(&global_connection_polling_mu);
499
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: grpc_rb_event_unblocking_func - begin aborting connection polling");
500
+ abort_channel_polling = 1;
501
+ grpc_completion_queue_shutdown(channel_polling_cq);
502
+ gpr_mu_unlock(&global_connection_polling_mu);
503
+ }
504
+
505
+ // Poll channel connectivity states in background thread without the GIL.
506
+ static VALUE run_poll_channels_loop(VALUE arg) {
507
+ (void)arg;
508
+ gpr_log(GPR_DEBUG, "GRPC_RUBY: run_poll_channels_loop - create connection polling thread");
509
+ rb_thread_call_without_gvl(run_poll_channels_loop_no_gil, NULL,
510
+ grpc_rb_event_unblocking_func, NULL);
511
+ return Qnil;
512
+ }
513
+
514
+ /* Temporary fix for
515
+ * https://github.com/GoogleCloudPlatform/google-cloud-ruby/issues/899.
516
+ * Transports in idle channels can get destroyed. Normally c-core re-connects,
517
+ * but in grpc-ruby core never gets a thread until an RPC is made, because ruby
518
+ * only calls c-core's "completion_queu_pluck" API.
519
+ * This uses a global background thread that calls
520
+ * "completion_queue_next" on registered "watch_channel_connectivity_state"
521
+ * calls - so that c-core can reconnect if needed, when there aren't any RPC's.
522
+ * TODO(apolcyn) remove this when core handles new RPCs on dead connections.
523
+ */
524
+ static void start_poll_channels_loop() {
525
+ channel_polling_cq = grpc_completion_queue_create(NULL);
526
+ gpr_mu_init(&global_connection_polling_mu);
527
+ abort_channel_polling = 0;
528
+ rb_thread_create(run_poll_channels_loop, NULL);
529
+ }
530
+
338
531
  static void Init_grpc_propagate_masks() {
339
532
  /* Constants representing call propagation masks in grpc.h */
340
- VALUE grpc_rb_mPropagateMasks = rb_define_module_under(
341
- grpc_rb_mGrpcCore, "PropagateMasks");
533
+ VALUE grpc_rb_mPropagateMasks =
534
+ rb_define_module_under(grpc_rb_mGrpcCore, "PropagateMasks");
342
535
  rb_define_const(grpc_rb_mPropagateMasks, "DEADLINE",
343
536
  UINT2NUM(GRPC_PROPAGATE_DEADLINE));
344
537
  rb_define_const(grpc_rb_mPropagateMasks, "CENSUS_STATS_CONTEXT",
@@ -353,8 +546,8 @@ static void Init_grpc_propagate_masks() {
353
546
 
354
547
  static void Init_grpc_connectivity_states() {
355
548
  /* Constants representing call propagation masks in grpc.h */
356
- VALUE grpc_rb_mConnectivityStates = rb_define_module_under(
357
- grpc_rb_mGrpcCore, "ConnectivityStates");
549
+ VALUE grpc_rb_mConnectivityStates =
550
+ rb_define_module_under(grpc_rb_mGrpcCore, "ConnectivityStates");
358
551
  rb_define_const(grpc_rb_mConnectivityStates, "IDLE",
359
552
  LONG2NUM(GRPC_CHANNEL_IDLE));
360
553
  rb_define_const(grpc_rb_mConnectivityStates, "CONNECTING",
@@ -382,12 +575,11 @@ void Init_grpc_channel() {
382
575
 
383
576
  /* Add ruby analogues of the Channel methods. */
384
577
  rb_define_method(grpc_rb_cChannel, "connectivity_state",
385
- grpc_rb_channel_get_connectivity_state,
386
- -1);
578
+ grpc_rb_channel_get_connectivity_state, -1);
387
579
  rb_define_method(grpc_rb_cChannel, "watch_connectivity_state",
388
- grpc_rb_channel_watch_connectivity_state, 4);
389
- rb_define_method(grpc_rb_cChannel, "create_call",
390
- grpc_rb_channel_create_call, 5);
580
+ grpc_rb_channel_watch_connectivity_state, 2);
581
+ rb_define_method(grpc_rb_cChannel, "create_call", grpc_rb_channel_create_call,
582
+ 5);
391
583
  rb_define_method(grpc_rb_cChannel, "target", grpc_rb_channel_get_target, 0);
392
584
  rb_define_method(grpc_rb_cChannel, "destroy", grpc_rb_channel_destroy, 0);
393
585
  rb_define_alias(grpc_rb_cChannel, "close", "destroy");
@@ -405,6 +597,7 @@ void Init_grpc_channel() {
405
597
  id_insecure_channel = rb_intern("this_channel_is_insecure");
406
598
  Init_grpc_propagate_masks();
407
599
  Init_grpc_connectivity_states();
600
+ start_poll_channels_loop();
408
601
  }
409
602
 
410
603
  /* Gets the wrapped channel from the ruby wrapper */
@@ -29,5 +29,5 @@
29
29
 
30
30
  # GRPC contains the General RPC module.
31
31
  module GRPC
32
- VERSION = '1.2.0'
32
+ VERSION = '1.2.1.pre1'
33
33
  end
@@ -0,0 +1,141 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'grpc'
31
+
32
+ # A test message
33
+ class EchoMsg
34
+ def self.marshal(_o)
35
+ ''
36
+ end
37
+
38
+ def self.unmarshal(_o)
39
+ EchoMsg.new
40
+ end
41
+ end
42
+
43
+ # A test service with an echo implementation.
44
+ class EchoService
45
+ include GRPC::GenericService
46
+ rpc :an_rpc, EchoMsg, EchoMsg
47
+ attr_reader :received_md
48
+
49
+ def initialize(**kw)
50
+ @trailing_metadata = kw
51
+ @received_md = []
52
+ end
53
+
54
+ def an_rpc(req, call)
55
+ GRPC.logger.info('echo service received a request')
56
+ call.output_metadata.update(@trailing_metadata)
57
+ @received_md << call.metadata unless call.metadata.nil?
58
+ req
59
+ end
60
+ end
61
+
62
+ EchoStub = EchoService.rpc_stub_class
63
+
64
+ def start_server(port = 0)
65
+ @srv = GRPC::RpcServer.new
66
+ server_port = @srv.add_http2_port("localhost:#{port}", :this_port_is_insecure)
67
+ @srv.handle(EchoService)
68
+ @server_thd = Thread.new { @srv.run }
69
+ @srv.wait_till_running
70
+ server_port
71
+ end
72
+
73
+ def stop_server
74
+ expect(@srv.stopped?).to be(false)
75
+ @srv.stop
76
+ @server_thd.join
77
+ expect(@srv.stopped?).to be(true)
78
+ end
79
+
80
+ describe 'channel connection behavior' do
81
+ it 'the client channel handles temporary loss of a transport' do
82
+ port = start_server
83
+ stub = EchoStub.new("localhost:#{port}", :this_channel_is_insecure)
84
+ req = EchoMsg.new
85
+ expect(stub.an_rpc(req)).to be_a(EchoMsg)
86
+ stop_server
87
+ sleep 1
88
+ # TODO(apolcyn) grabbing the same port might fail, is this stable enough?
89
+ start_server(port)
90
+ expect(stub.an_rpc(req)).to be_a(EchoMsg)
91
+ stop_server
92
+ end
93
+
94
+ it 'observably connects and reconnects to transient server' \
95
+ ' when using the channel state API' do
96
+ port = start_server
97
+ ch = GRPC::Core::Channel.new("localhost:#{port}", {},
98
+ :this_channel_is_insecure)
99
+
100
+ expect(ch.connectivity_state).to be(GRPC::Core::ConnectivityStates::IDLE)
101
+
102
+ state = ch.connectivity_state(true)
103
+
104
+ count = 0
105
+ while count < 20 && state != GRPC::Core::ConnectivityStates::READY
106
+ ch.watch_connectivity_state(state, Time.now + 60)
107
+ state = ch.connectivity_state(true)
108
+ count += 1
109
+ end
110
+
111
+ expect(state).to be(GRPC::Core::ConnectivityStates::READY)
112
+
113
+ stop_server
114
+
115
+ state = ch.connectivity_state
116
+
117
+ count = 0
118
+ while count < 20 && state == GRPC::Core::ConnectivityStates::READY
119
+ ch.watch_connectivity_state(state, Time.now + 60)
120
+ state = ch.connectivity_state
121
+ count += 1
122
+ end
123
+
124
+ expect(state).to_not be(GRPC::Core::ConnectivityStates::READY)
125
+
126
+ start_server(port)
127
+
128
+ state = ch.connectivity_state(true)
129
+
130
+ count = 0
131
+ while count < 20 && state != GRPC::Core::ConnectivityStates::READY
132
+ ch.watch_connectivity_state(state, Time.now + 60)
133
+ state = ch.connectivity_state(true)
134
+ count += 1
135
+ end
136
+
137
+ expect(state).to be(GRPC::Core::ConnectivityStates::READY)
138
+
139
+ stop_server
140
+ end
141
+ end
@@ -153,6 +153,35 @@ describe GRPC::Core::Channel do
153
153
  end
154
154
  end
155
155
 
156
+ describe '#connectivity_state' do
157
+ it 'returns an enum' do
158
+ ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
159
+ valid_states = [
160
+ GRPC::Core::ConnectivityStates::IDLE,
161
+ GRPC::Core::ConnectivityStates::CONNECTING,
162
+ GRPC::Core::ConnectivityStates::READY,
163
+ GRPC::Core::ConnectivityStates::TRANSIENT_FAILURE,
164
+ GRPC::Core::ConnectivityStates::FATAL_FAILURE
165
+ ]
166
+
167
+ expect(valid_states).to include(ch.connectivity_state)
168
+ end
169
+
170
+ it 'returns an enum when trying to connect' do
171
+ ch = GRPC::Core::Channel.new(fake_host, nil, :this_channel_is_insecure)
172
+ ch.connectivity_state(true)
173
+ valid_states = [
174
+ GRPC::Core::ConnectivityStates::IDLE,
175
+ GRPC::Core::ConnectivityStates::CONNECTING,
176
+ GRPC::Core::ConnectivityStates::READY,
177
+ GRPC::Core::ConnectivityStates::TRANSIENT_FAILURE,
178
+ GRPC::Core::ConnectivityStates::FATAL_FAILURE
179
+ ]
180
+
181
+ expect(valid_states).to include(ch.connectivity_state)
182
+ end
183
+ end
184
+
156
185
  describe '::SSL_TARGET' do
157
186
  it 'is a symbol' do
158
187
  expect(GRPC::Core::Channel::SSL_TARGET).to be_a(Symbol)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - gRPC Authors
8
8
  autorequire:
9
9
  bindir: src/ruby/bin
10
10
  cert_chain: []
11
- date: 2017-03-20 00:00:00.000000000 Z
11
+ date: 2017-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf
@@ -787,6 +787,7 @@ files:
787
787
  - src/ruby/pb/test/server.rb
788
788
  - src/ruby/spec/call_credentials_spec.rb
789
789
  - src/ruby/spec/call_spec.rb
790
+ - src/ruby/spec/channel_connection_spec.rb
790
791
  - src/ruby/spec/channel_credentials_spec.rb
791
792
  - src/ruby/spec/channel_spec.rb
792
793
  - src/ruby/spec/client_server_spec.rb
@@ -1265,9 +1266,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
1265
1266
  version: 2.0.0
1266
1267
  required_rubygems_version: !ruby/object:Gem::Requirement
1267
1268
  requirements:
1268
- - - ">="
1269
+ - - ">"
1269
1270
  - !ruby/object:Gem::Version
1270
- version: '0'
1271
+ version: 1.3.1
1271
1272
  requirements: []
1272
1273
  rubyforge_project:
1273
1274
  rubygems_version: 2.6.11
@@ -1295,6 +1296,7 @@ test_files:
1295
1296
  - src/ruby/spec/time_consts_spec.rb
1296
1297
  - src/ruby/spec/channel_credentials_spec.rb
1297
1298
  - src/ruby/spec/call_credentials_spec.rb
1299
+ - src/ruby/spec/channel_connection_spec.rb
1298
1300
  - src/ruby/spec/server_spec.rb
1299
1301
  - src/ruby/spec/error_sanity_spec.rb
1300
1302
  - src/ruby/spec/compression_options_spec.rb