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 +4 -4
- data/Makefile +2 -2
- data/src/ruby/ext/grpc/rb_channel.c +262 -69
- data/src/ruby/lib/grpc/version.rb +1 -1
- data/src/ruby/spec/channel_connection_spec.rb +141 -0
- data/src/ruby/spec/channel_spec.rb +29 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 210d5851abb9539a5ef0b9ee48ec5c7873c99af1
|
4
|
+
data.tar.gz: fff44d43a3444d9c04ec3ccbe2e00b39b6b95694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
416
|
-
CSHARP_VERSION = 1.2.
|
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
|
-
|
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
|
-
|
89
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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
|
-
|
179
|
-
|
180
|
-
secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds)
|
224
|
+
ch.connectivity_state -> state
|
225
|
+
ch.connectivity_state(true) -> state
|
181
226
|
|
182
|
-
|
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
|
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",
|
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
|
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
|
-
|
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
|
-
|
205
|
-
|
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
|
-
|
208
|
-
|
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
|
-
|
214
|
-
|
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
|
-
|
222
|
-
|
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
|
-
|
235
|
-
|
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
|
-
|
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
|
248
|
-
VALUE
|
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*
|
340
|
+
char *tmp_str = NULL;
|
260
341
|
|
261
342
|
if (host != Qnil) {
|
262
|
-
host_slice =
|
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 =
|
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,
|
284
|
-
|
285
|
-
|
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
|
-
|
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*
|
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 =
|
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 =
|
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,
|
389
|
-
rb_define_method(grpc_rb_cChannel, "create_call",
|
390
|
-
|
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 */
|
@@ -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.
|
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-
|
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:
|
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
|