Merge pull request #1731 from ctiller/you-complete-me

New core shutdown API
pull/2106/head
Nicolas Noble 10 years ago
commit a93954f844
  1. 2
      Makefile
  2. 4
      build.json
  3. 1
      include/grpc++/server.h
  4. 18
      include/grpc/grpc.h
  5. 31
      src/core/iomgr/pollset_posix.c
  6. 4
      src/core/support/sync.c
  7. 38
      src/core/surface/completion_queue.c
  8. 198
      src/core/surface/server.c
  9. 13
      src/cpp/server/server.cc
  10. 30
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  11. 2
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  12. 3
      src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
  13. 19
      src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
  14. 19
      src/csharp/Grpc.Core/Server.cs
  15. 11
      src/csharp/ext/grpc_csharp_ext.c
  16. 41
      src/node/ext/server.cc
  17. 3
      src/node/ext/server.h
  18. 3
      src/php/ext/grpc/server.c
  19. 12
      src/python/src/grpc/_adapter/_c/types/server.c
  20. 3
      src/python/src/grpc/_adapter/_c/utility.c
  21. 4
      src/python/src/grpc/_adapter/_intermediary_low.py
  22. 11
      src/python/src/grpc/_adapter/_intermediary_low_test.py
  23. 7
      src/python/src/grpc/_adapter/_low.py
  24. 2
      src/python/src/grpc/_adapter/_low_test.py
  25. 12
      src/ruby/ext/grpc/rb_completion_queue.c
  26. 64
      src/ruby/ext/grpc/rb_server.c
  27. 13
      src/ruby/lib/grpc/generic/rpc_server.rb
  28. 9
      src/ruby/spec/client_server_spec.rb
  29. 2
      src/ruby/spec/generic/active_call_spec.rb
  30. 3
      src/ruby/spec/generic/client_stub_spec.rb
  31. 12
      src/ruby/spec/generic/rpc_server_spec.rb
  32. 22
      src/ruby/spec/server_spec.rb
  33. 4
      test/core/bad_client/bad_client.c
  34. 3
      test/core/end2end/dualstack_socket_test.c
  35. 3
      test/core/end2end/tests/bad_hostname.c
  36. 3
      test/core/end2end/tests/cancel_after_accept.c
  37. 3
      test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
  38. 3
      test/core/end2end/tests/cancel_after_invoke.c
  39. 3
      test/core/end2end/tests/cancel_before_invoke.c
  40. 5
      test/core/end2end/tests/cancel_in_a_vacuum.c
  41. 7
      test/core/end2end/tests/census_simple_request.c
  42. 4
      test/core/end2end/tests/disappearing_server.c
  43. 14
      test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
  44. 13
      test/core/end2end/tests/early_server_shutdown_finishes_tags.c
  45. 3
      test/core/end2end/tests/empty_batch.c
  46. 5
      test/core/end2end/tests/graceful_server_shutdown.c
  47. 3
      test/core/end2end/tests/invoke_large_request.c
  48. 3
      test/core/end2end/tests/max_concurrent_streams.c
  49. 3
      test/core/end2end/tests/max_message_length.c
  50. 5
      test/core/end2end/tests/no_op.c
  51. 3
      test/core/end2end/tests/ping_pong_streaming.c
  52. 3
      test/core/end2end/tests/registered_call.c
  53. 3
      test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c
  54. 3
      test/core/end2end/tests/request_response_with_metadata_and_payload.c
  55. 3
      test/core/end2end/tests/request_response_with_payload.c
  56. 3
      test/core/end2end/tests/request_response_with_payload_and_call_creds.c
  57. 3
      test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c
  58. 3
      test/core/end2end/tests/request_with_flags.c
  59. 3
      test/core/end2end/tests/request_with_large_metadata.c
  60. 3
      test/core/end2end/tests/request_with_payload.c
  61. 3
      test/core/end2end/tests/server_finishes_request.c
  62. 3
      test/core/end2end/tests/simple_delayed_request.c
  63. 3
      test/core/end2end/tests/simple_request.c
  64. 3
      test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c
  65. 3
      test/core/fling/server.c
  66. 2
      tools/doxygen/Doxyfile.c++
  67. 2
      tools/doxygen/Doxyfile.c++.internal
  68. 2
      tools/doxygen/Doxyfile.core
  69. 2
      tools/doxygen/Doxyfile.core.internal

@ -310,7 +310,7 @@ E = @echo
Q = @ Q = @
endif endif
VERSION = 0.9.1.0 VERSION = 0.10.0.0
CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES)) CPPFLAGS_NO_ARCH += $(addprefix -I, $(INCLUDES)) $(addprefix -D, $(DEFINES))
CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS) CPPFLAGS += $(CPPFLAGS_NO_ARCH) $(ARCH_FLAGS)

@ -6,8 +6,8 @@
"#": "The public version number of the library.", "#": "The public version number of the library.",
"version": { "version": {
"major": 0, "major": 0,
"minor": 9, "minor": 10,
"micro": 1, "micro": 0,
"build": 0 "build": 0
} }
}, },

@ -77,6 +77,7 @@ class Server GRPC_FINAL : public GrpcLibrary,
class SyncRequest; class SyncRequest;
class AsyncRequest; class AsyncRequest;
class ShutdownRequest;
// ServerBuilder use only // ServerBuilder use only
Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned, Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned,

@ -488,18 +488,20 @@ void grpc_server_start(grpc_server *server);
/* Begin shutting down a server. /* Begin shutting down a server.
After completion, no new calls or connections will be admitted. After completion, no new calls or connections will be admitted.
Existing calls will be allowed to complete. Existing calls will be allowed to complete.
Shutdown is idempotent. */ Send a GRPC_OP_COMPLETE event when there are no more calls being serviced.
void grpc_server_shutdown(grpc_server *server);
/* As per grpc_server_shutdown, but send a GRPC_OP_COMPLETE event when
there are no more calls being serviced.
Shutdown is idempotent, and all tags will be notified at once if multiple Shutdown is idempotent, and all tags will be notified at once if multiple
grpc_server_shutdown_and_notify calls are made. */ grpc_server_shutdown_and_notify calls are made. */
void grpc_server_shutdown_and_notify(grpc_server *server, void *tag); void grpc_server_shutdown_and_notify(grpc_server *server,
grpc_completion_queue *cq, void *tag);
/* Cancel all in-progress calls.
Only usable after shutdown. */
void grpc_server_cancel_all_calls(grpc_server *server);
/* Destroy a server. /* Destroy a server.
Forcefully cancels all existing calls. Shutdown must have completed beforehand (i.e. all tags generated by
Implies grpc_server_shutdown() if one was not previously performed. */ grpc_server_shutdown_and_notify must have been received, and at least
one call to grpc_server_shutdown_and_notify must have been made). */
void grpc_server_destroy(grpc_server *server); void grpc_server_destroy(grpc_server *server);
/** Enable or disable a tracer. /** Enable or disable a tracer.

@ -174,8 +174,6 @@ void grpc_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {
int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
/* pollset->mu already held */ /* pollset->mu already held */
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now();
/* FIXME(ctiller): see below */
gpr_timespec maximum_deadline = gpr_time_add(now, gpr_time_from_seconds(1));
int r; int r;
if (gpr_time_cmp(now, deadline) > 0) { if (gpr_time_cmp(now, deadline) > 0) {
return 0; return 0;
@ -186,14 +184,25 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
if (grpc_alarm_check(&pollset->mu, now, &deadline)) { if (grpc_alarm_check(&pollset->mu, now, &deadline)) {
return 1; return 1;
} }
/* FIXME(ctiller): we should not clamp deadline, however we have some if (pollset->shutting_down) {
stuck at shutdown bugs that this resolves */ return 1;
if (gpr_time_cmp(deadline, maximum_deadline) > 0) {
deadline = maximum_deadline;
} }
gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset); gpr_tls_set(&g_current_thread_poller, (gpr_intptr)pollset);
r = pollset->vtable->maybe_work(pollset, deadline, now, 1); r = pollset->vtable->maybe_work(pollset, deadline, now, 1);
gpr_tls_set(&g_current_thread_poller, 0); gpr_tls_set(&g_current_thread_poller, 0);
if (pollset->shutting_down) {
if (pollset->counter > 0) {
grpc_pollset_kick(pollset);
} else if (pollset->in_flight_cbs == 0) {
gpr_mu_unlock(&pollset->mu);
pollset->shutdown_done_cb(pollset->shutdown_done_arg);
/* Continuing to access pollset here is safe -- it is the caller's
* responsibility to not destroy when it has outstanding calls to
* grpc_pollset_work.
* TODO(dklempner): Can we refactor the shutdown logic to avoid this? */
gpr_mu_lock(&pollset->mu);
}
}
return r; return r;
} }
@ -201,13 +210,19 @@ void grpc_pollset_shutdown(grpc_pollset *pollset,
void (*shutdown_done)(void *arg), void (*shutdown_done)(void *arg),
void *shutdown_done_arg) { void *shutdown_done_arg) {
int in_flight_cbs; int in_flight_cbs;
int counter;
gpr_mu_lock(&pollset->mu); gpr_mu_lock(&pollset->mu);
pollset->shutting_down = 1; pollset->shutting_down = 1;
in_flight_cbs = pollset->in_flight_cbs; in_flight_cbs = pollset->in_flight_cbs;
counter = pollset->counter;
pollset->shutdown_done_cb = shutdown_done; pollset->shutdown_done_cb = shutdown_done;
pollset->shutdown_done_arg = shutdown_done_arg; pollset->shutdown_done_arg = shutdown_done_arg;
if (counter > 0) {
grpc_pollset_kick(pollset);
}
gpr_mu_unlock(&pollset->mu); gpr_mu_unlock(&pollset->mu);
if (in_flight_cbs == 0) {
if (in_flight_cbs == 0 && counter == 0) {
shutdown_done(shutdown_done_arg); shutdown_done(shutdown_done_arg);
} }
} }
@ -294,7 +309,7 @@ static void unary_poll_do_promote(void *args, int success) {
pollset->in_flight_cbs--; pollset->in_flight_cbs--;
if (pollset->shutting_down) { if (pollset->shutting_down) {
/* We don't care about this pollset anymore. */ /* We don't care about this pollset anymore. */
if (pollset->in_flight_cbs == 0) { if (pollset->in_flight_cbs == 0 && pollset->counter == 0) {
do_shutdown_cb = 1; do_shutdown_cb = 1;
} }
} else if (grpc_fd_is_orphaned(fd)) { } else if (grpc_fd_is_orphaned(fd)) {

@ -118,7 +118,9 @@ void gpr_refn(gpr_refcount *r, int n) {
} }
int gpr_unref(gpr_refcount *r) { int gpr_unref(gpr_refcount *r) {
return gpr_atm_full_fetch_add(&r->count, -1) == 1; gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1);
GPR_ASSERT(prior > 0);
return prior == 1;
} }
void gpr_stats_init(gpr_stats_counter *c, gpr_intptr n) { void gpr_stats_init(gpr_stats_counter *c, gpr_intptr n) {

@ -83,7 +83,8 @@ grpc_completion_queue *grpc_completion_queue_create(void) {
memset(cc, 0, sizeof(*cc)); memset(cc, 0, sizeof(*cc));
/* Initial ref is dropped by grpc_completion_queue_shutdown */ /* Initial ref is dropped by grpc_completion_queue_shutdown */
gpr_ref_init(&cc->refs, 1); gpr_ref_init(&cc->refs, 1);
gpr_ref_init(&cc->owning_refs, 1); /* One for destroy(), one for pollset_shutdown */
gpr_ref_init(&cc->owning_refs, 2);
grpc_pollset_init(&cc->pollset); grpc_pollset_init(&cc->pollset);
cc->allow_polling = 1; cc->allow_polling = 1;
return cc; return cc;
@ -95,14 +96,14 @@ void grpc_cq_internal_ref(grpc_completion_queue *cc) {
static void on_pollset_destroy_done(void *arg) { static void on_pollset_destroy_done(void *arg) {
grpc_completion_queue *cc = arg; grpc_completion_queue *cc = arg;
grpc_pollset_destroy(&cc->pollset); grpc_cq_internal_unref(cc);
gpr_free(cc);
} }
void grpc_cq_internal_unref(grpc_completion_queue *cc) { void grpc_cq_internal_unref(grpc_completion_queue *cc) {
if (gpr_unref(&cc->owning_refs)) { if (gpr_unref(&cc->owning_refs)) {
GPR_ASSERT(cc->queue == NULL); GPR_ASSERT(cc->queue == NULL);
grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); grpc_pollset_destroy(&cc->pollset);
gpr_free(cc);
} }
} }
@ -145,25 +146,25 @@ void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call) {
/* Signal the end of an operation - if this is the last waiting-to-be-queued /* Signal the end of an operation - if this is the last waiting-to-be-queued
event, then enter shutdown mode */ event, then enter shutdown mode */
static void end_op_locked(grpc_completion_queue *cc,
grpc_completion_type type) {
if (gpr_unref(&cc->refs)) {
GPR_ASSERT(!cc->shutdown);
GPR_ASSERT(cc->shutdown_called);
cc->shutdown = 1;
gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
}
}
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call, void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call,
int success) { int success) {
event *ev; event *ev;
int shutdown = 0;
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call); ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call);
ev->base.success = success; ev->base.success = success;
end_op_locked(cc, GRPC_OP_COMPLETE); if (gpr_unref(&cc->refs)) {
GPR_ASSERT(!cc->shutdown);
GPR_ASSERT(cc->shutdown_called);
cc->shutdown = 1;
gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
shutdown = 1;
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0); if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0);
if (shutdown) {
grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
}
} }
/* Create a GRPC_QUEUE_SHUTDOWN event without queuing it anywhere */ /* Create a GRPC_QUEUE_SHUTDOWN event without queuing it anywhere */
@ -179,6 +180,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
event *ev = NULL; event *ev = NULL;
grpc_event ret; grpc_event ret;
grpc_cq_internal_ref(cc);
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) { for (;;) {
if (cc->queue != NULL) { if (cc->queue != NULL) {
@ -214,6 +216,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT; ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
grpc_cq_internal_unref(cc);
return ret; return ret;
} }
} }
@ -221,6 +224,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
ret = ev->base; ret = ev->base;
gpr_free(ev); gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
grpc_cq_internal_unref(cc);
return ret; return ret;
} }
@ -258,6 +262,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
event *ev = NULL; event *ev = NULL;
grpc_event ret; grpc_event ret;
grpc_cq_internal_ref(cc);
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) { for (;;) {
if ((ev = pluck_event(cc, tag))) { if ((ev = pluck_event(cc, tag))) {
@ -276,6 +281,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT; ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
grpc_cq_internal_unref(cc);
return ret; return ret;
} }
} }
@ -283,6 +289,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
ret = ev->base; ret = ev->base;
gpr_free(ev); gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
grpc_cq_internal_unref(cc);
return ret; return ret;
} }
@ -299,6 +306,7 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) {
cc->shutdown = 1; cc->shutdown = 1;
gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset)); gpr_cv_broadcast(GRPC_POLLSET_CV(&cc->pollset));
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
} }
} }

@ -127,6 +127,11 @@ struct channel_data {
grpc_iomgr_closure finish_destroy_channel_closure; grpc_iomgr_closure finish_destroy_channel_closure;
}; };
typedef struct shutdown_tag {
void *tag;
grpc_completion_queue *cq;
} shutdown_tag;
struct grpc_server { struct grpc_server {
size_t channel_filter_count; size_t channel_filter_count;
const grpc_channel_filter **channel_filters; const grpc_channel_filter **channel_filters;
@ -137,14 +142,14 @@ struct grpc_server {
size_t cq_count; size_t cq_count;
gpr_mu mu; gpr_mu mu;
gpr_cv cv;
registered_method *registered_methods; registered_method *registered_methods;
requested_call_array requested_calls; requested_call_array requested_calls;
gpr_uint8 shutdown; gpr_uint8 shutdown;
gpr_uint8 shutdown_published;
size_t num_shutdown_tags; size_t num_shutdown_tags;
void **shutdown_tags; shutdown_tag *shutdown_tags;
call_data *lists[CALL_LIST_COUNT]; call_data *lists[CALL_LIST_COUNT];
channel_data root_channel_data; channel_data root_channel_data;
@ -261,29 +266,32 @@ static void server_ref(grpc_server *server) {
gpr_ref(&server->internal_refcount); gpr_ref(&server->internal_refcount);
} }
static void server_unref(grpc_server *server) { static void server_delete(grpc_server *server) {
registered_method *rm; registered_method *rm;
size_t i; size_t i;
grpc_channel_args_destroy(server->channel_args);
gpr_mu_destroy(&server->mu);
gpr_free(server->channel_filters);
requested_call_array_destroy(&server->requested_calls);
while ((rm = server->registered_methods) != NULL) {
server->registered_methods = rm->next;
gpr_free(rm->method);
gpr_free(rm->host);
requested_call_array_destroy(&rm->requested);
gpr_free(rm);
}
for (i = 0; i < server->cq_count; i++) {
grpc_cq_internal_unref(server->cqs[i]);
}
gpr_free(server->cqs);
gpr_free(server->pollsets);
gpr_free(server->shutdown_tags);
gpr_free(server);
}
static void server_unref(grpc_server *server) {
if (gpr_unref(&server->internal_refcount)) { if (gpr_unref(&server->internal_refcount)) {
grpc_channel_args_destroy(server->channel_args); server_delete(server);
gpr_mu_destroy(&server->mu);
gpr_cv_destroy(&server->cv);
gpr_free(server->channel_filters);
requested_call_array_destroy(&server->requested_calls);
while ((rm = server->registered_methods) != NULL) {
server->registered_methods = rm->next;
gpr_free(rm->method);
gpr_free(rm->host);
requested_call_array_destroy(&rm->requested);
gpr_free(rm);
}
for (i = 0; i < server->cq_count; i++) {
grpc_cq_internal_unref(server->cqs[i]);
}
gpr_free(server->cqs);
gpr_free(server->pollsets);
gpr_free(server->shutdown_tags);
gpr_free(server);
} }
} }
@ -378,6 +386,26 @@ static void kill_zombie(void *elem, int success) {
grpc_call_destroy(grpc_call_from_top_element(elem)); grpc_call_destroy(grpc_call_from_top_element(elem));
} }
static int num_listeners(grpc_server *server) {
listener *l;
int n = 0;
for (l = server->listeners; l; l = l->next) {
n++;
}
return n;
}
static void maybe_finish_shutdown(grpc_server *server) {
size_t i;
if (server->shutdown && !server->shutdown_published && server->lists[ALL_CALLS] == NULL && server->listeners_destroyed == num_listeners(server)) {
server->shutdown_published = 1;
for (i = 0; i < server->num_shutdown_tags; i++) {
grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag,
NULL, 1);
}
}
}
static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
grpc_call_element *elem = user_data; grpc_call_element *elem = user_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
@ -441,6 +469,9 @@ static void server_on_recv(void *ptr, int success) {
grpc_iomgr_add_callback(&calld->kill_zombie_closure); grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} }
if (call_list_remove(calld, ALL_CALLS)) {
maybe_finish_shutdown(chand->server);
}
gpr_mu_unlock(&chand->server->mu); gpr_mu_unlock(&chand->server->mu);
break; break;
} }
@ -539,19 +570,15 @@ static void init_call_elem(grpc_call_element *elem,
static void destroy_call_elem(grpc_call_element *elem) { static void destroy_call_elem(grpc_call_element *elem) {
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
size_t i, j; int removed[CALL_LIST_COUNT];
size_t i;
gpr_mu_lock(&chand->server->mu); gpr_mu_lock(&chand->server->mu);
for (i = 0; i < CALL_LIST_COUNT; i++) { for (i = 0; i < CALL_LIST_COUNT; i++) {
call_list_remove(elem->call_data, i); removed[i] = call_list_remove(elem->call_data, i);
} }
if (chand->server->shutdown && chand->server->lists[ALL_CALLS] == NULL) { if (removed[ALL_CALLS]) {
for (i = 0; i < chand->server->num_shutdown_tags; i++) { maybe_finish_shutdown(chand->server);
for (j = 0; j < chand->server->cq_count; j++) {
grpc_cq_end_op(chand->server->cqs[j], chand->server->shutdown_tags[i],
NULL, 1);
}
}
} }
gpr_mu_unlock(&chand->server->mu); gpr_mu_unlock(&chand->server->mu);
@ -646,7 +673,6 @@ grpc_server *grpc_server_create_from_filters(grpc_channel_filter **filters,
memset(server, 0, sizeof(grpc_server)); memset(server, 0, sizeof(grpc_server));
gpr_mu_init(&server->mu); gpr_mu_init(&server->mu);
gpr_cv_init(&server->cv);
/* decremented by grpc_server_destroy */ /* decremented by grpc_server_destroy */
gpr_ref_init(&server->internal_refcount, 1); gpr_ref_init(&server->internal_refcount, 1);
@ -806,38 +832,28 @@ grpc_transport_setup_result grpc_server_setup_transport(
return result; return result;
} }
static int num_listeners(grpc_server *server) { void grpc_server_shutdown_and_notify(grpc_server *server,
listener *l; grpc_completion_queue *cq, void *tag) {
int n = 0;
for (l = server->listeners; l; l = l->next) {
n++;
}
return n;
}
static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
void *shutdown_tag) {
listener *l; listener *l;
requested_call_array requested_calls; requested_call_array requested_calls;
channel_data **channels; channel_data **channels;
channel_data *c; channel_data *c;
size_t nchannels; size_t nchannels;
size_t i, j; size_t i;
grpc_channel_op op; grpc_channel_op op;
grpc_channel_element *elem; grpc_channel_element *elem;
registered_method *rm; registered_method *rm;
shutdown_tag *sdt;
/* lock, and gather up some stuff to do */ /* lock, and gather up some stuff to do */
gpr_mu_lock(&server->mu); gpr_mu_lock(&server->mu);
if (have_shutdown_tag) { grpc_cq_begin_op(cq, NULL);
for (i = 0; i < server->cq_count; i++) { server->shutdown_tags =
grpc_cq_begin_op(server->cqs[i], NULL); gpr_realloc(server->shutdown_tags,
} sizeof(shutdown_tag) * (server->num_shutdown_tags + 1));
server->shutdown_tags = sdt = &server->shutdown_tags[server->num_shutdown_tags++];
gpr_realloc(server->shutdown_tags, sdt->tag = tag;
sizeof(void *) * (server->num_shutdown_tags + 1)); sdt->cq = cq;
server->shutdown_tags[server->num_shutdown_tags++] = shutdown_tag;
}
if (server->shutdown) { if (server->shutdown) {
gpr_mu_unlock(&server->mu); gpr_mu_unlock(&server->mu);
return; return;
@ -878,13 +894,7 @@ static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
} }
server->shutdown = 1; server->shutdown = 1;
if (server->lists[ALL_CALLS] == NULL) { maybe_finish_shutdown(server);
for (i = 0; i < server->num_shutdown_tags; i++) {
for (j = 0; j < server->cq_count; j++) {
grpc_cq_end_op(server->cqs[j], server->shutdown_tags[i], NULL, 1);
}
}
}
gpr_mu_unlock(&server->mu); gpr_mu_unlock(&server->mu);
for (i = 0; i < nchannels; i++) { for (i = 0; i < nchannels; i++) {
@ -914,46 +924,64 @@ static void shutdown_internal(grpc_server *server, gpr_uint8 have_shutdown_tag,
} }
} }
void grpc_server_shutdown(grpc_server *server) {
shutdown_internal(server, 0, NULL);
}
void grpc_server_shutdown_and_notify(grpc_server *server, void *tag) {
shutdown_internal(server, 1, tag);
}
void grpc_server_listener_destroy_done(void *s) { void grpc_server_listener_destroy_done(void *s) {
grpc_server *server = s; grpc_server *server = s;
gpr_mu_lock(&server->mu); gpr_mu_lock(&server->mu);
server->listeners_destroyed++; server->listeners_destroyed++;
gpr_cv_signal(&server->cv); maybe_finish_shutdown(server);
gpr_mu_unlock(&server->mu); gpr_mu_unlock(&server->mu);
} }
void grpc_server_destroy(grpc_server *server) { void grpc_server_cancel_all_calls(grpc_server *server) {
channel_data *c;
listener *l;
size_t i;
call_data *calld; call_data *calld;
grpc_call **calls;
size_t call_count;
size_t call_capacity;
int is_first = 1;
size_t i;
gpr_mu_lock(&server->mu); gpr_mu_lock(&server->mu);
if (!server->shutdown) {
GPR_ASSERT(server->shutdown);
if (!server->lists[ALL_CALLS]) {
gpr_mu_unlock(&server->mu); gpr_mu_unlock(&server->mu);
grpc_server_shutdown(server); return;
gpr_mu_lock(&server->mu);
} }
while (server->listeners_destroyed != num_listeners(server)) { call_capacity = 8;
for (i = 0; i < server->cq_count; i++) { call_count = 0;
gpr_mu_unlock(&server->mu); calls = gpr_malloc(sizeof(grpc_call *) * call_capacity);
grpc_cq_hack_spin_pollset(server->cqs[i]);
gpr_mu_lock(&server->mu); for (calld = server->lists[ALL_CALLS]; calld != server->lists[ALL_CALLS] || is_first; calld = calld->links[ALL_CALLS].next) {
if (call_count == call_capacity) {
call_capacity *= 2;
calls = gpr_realloc(calls, sizeof(grpc_call *) * call_capacity);
} }
calls[call_count++] = calld->call;
GRPC_CALL_INTERNAL_REF(calld->call, "cancel_all");
is_first = 0;
}
gpr_mu_unlock(&server->mu);
gpr_cv_wait(&server->cv, &server->mu, for (i = 0; i < call_count; i++) {
gpr_time_add(gpr_now(), gpr_time_from_millis(100))); grpc_call_cancel_with_status(calls[i], GRPC_STATUS_UNAVAILABLE, "Unavailable");
GRPC_CALL_INTERNAL_UNREF(calls[i], "cancel_all", 1);
} }
gpr_free(calls);
}
void grpc_server_destroy(grpc_server *server) {
channel_data *c;
listener *l;
call_data *calld;
gpr_mu_lock(&server->mu);
GPR_ASSERT(server->shutdown || !server->listeners);
GPR_ASSERT(server->listeners_destroyed == num_listeners(server));
while (server->listeners) { while (server->listeners) {
l = server->listeners; l = server->listeners;
server->listeners = l->next; server->listeners = l->next;
@ -962,10 +990,6 @@ void grpc_server_destroy(grpc_server *server) {
while ((calld = call_list_remove_head(&server->lists[PENDING_START], while ((calld = call_list_remove_head(&server->lists[PENDING_START],
PENDING_START)) != NULL) { PENDING_START)) != NULL) {
/* TODO(dgq): If we knew the size of the call list (or an upper bound), we
* could allocate all the memory for the closures in advance in a single
* chunk */
gpr_log(GPR_DEBUG, "server destroys call %p", calld->call);
calld->state = ZOMBIED; calld->state = ZOMBIED;
grpc_iomgr_closure_init( grpc_iomgr_closure_init(
&calld->kill_zombie_closure, kill_zombie, &calld->kill_zombie_closure, kill_zombie,

@ -52,6 +52,14 @@
namespace grpc { namespace grpc {
class Server::ShutdownRequest GRPC_FINAL : public CompletionQueueTag {
public:
bool FinalizeResult(void** tag, bool* status) {
delete this;
return false;
}
};
class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag { class Server::SyncRequest GRPC_FINAL : public CompletionQueueTag {
public: public:
SyncRequest(RpcServiceMethod* method, void* tag) SyncRequest(RpcServiceMethod* method, void* tag)
@ -217,6 +225,9 @@ Server::~Server() {
Shutdown(); Shutdown();
} }
} }
void* got_tag;
bool ok;
GPR_ASSERT(!cq_.Next(&got_tag, &ok));
grpc_server_destroy(server_); grpc_server_destroy(server_);
if (thread_pool_owned_) { if (thread_pool_owned_) {
delete thread_pool_; delete thread_pool_;
@ -290,7 +301,7 @@ void Server::Shutdown() {
grpc::unique_lock<grpc::mutex> lock(mu_); grpc::unique_lock<grpc::mutex> lock(mu_);
if (started_ && !shutdown_) { if (started_ && !shutdown_) {
shutdown_ = true; shutdown_ = true;
grpc_server_shutdown(server_); grpc_server_shutdown_and_notify(server_, cq_.cq(), new ShutdownRequest());
cq_.Shutdown(); cq_.Shutdown();
// Wait for running callbacks to finish. // Wait for running callbacks to finish.

@ -205,20 +205,22 @@ namespace Grpc.Core.Tests
() => { Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); }); () => { Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); });
} }
[Test] // TODO(jtattermusch): temporarily commented out for #1731
public void UnknownMethodHandler() // to be uncommented along with PR #1577
{ // [Test]
var call = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty); // public void UnknownMethodHandler()
try // {
{ // var call = new Call<string, string>(ServiceName, NonexistentMethod, channel, Metadata.Empty);
Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken)); // try
Assert.Fail(); // {
} // Calls.BlockingUnaryCall(call, "ABC", default(CancellationToken));
catch (RpcException e) // Assert.Fail();
{ // }
Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode); // catch (RpcException e)
} // {
} // Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
// }
// }
private static async Task<string> EchoHandler(ServerCallContext context, string request) private static async Task<string> EchoHandler(ServerCallContext context, string request)
{ {

@ -192,7 +192,5 @@ namespace Grpc.Core.Internal
{ {
return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
} }
} }
} }

@ -32,14 +32,15 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Grpc.Core.Utils; using Grpc.Core.Utils;
namespace Grpc.Core.Internal namespace Grpc.Core.Internal
{ {
internal delegate void OpCompletionDelegate(bool success); internal delegate void OpCompletionDelegate(bool success);
internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx); internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx);
internal class CompletionRegistry internal class CompletionRegistry

@ -60,10 +60,10 @@ namespace Grpc.Core.Internal
static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_shutdown(ServerSafeHandle server); static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server);
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, BatchContextSafeHandle ctx); static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")] [DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_destroy(IntPtr server); static extern void grpcsharp_server_destroy(IntPtr server);
@ -92,16 +92,11 @@ namespace Grpc.Core.Internal
grpcsharp_server_start(this); grpcsharp_server_start(this);
} }
public void Shutdown() public void ShutdownAndNotify(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
{
grpcsharp_server_shutdown(this);
}
public void ShutdownAndNotify(BatchCompletionDelegate callback)
{ {
var ctx = BatchContextSafeHandle.Create(); var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback); GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_server_shutdown_and_notify_callback(this, ctx); grpcsharp_server_shutdown_and_notify_callback(this, cq, ctx);
} }
public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback) public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
@ -116,5 +111,11 @@ namespace Grpc.Core.Internal
grpcsharp_server_destroy(handle); grpcsharp_server_destroy(handle);
return true; return true;
} }
// Only to be called after ShutdownAndNotify.
public void CancelAllCalls()
{
grpcsharp_server_cancel_all_calls(this);
}
} }
} }

@ -143,7 +143,8 @@ namespace Grpc.Core
Preconditions.CheckState(!shutdownRequested); Preconditions.CheckState(!shutdownRequested);
shutdownRequested = true; shutdownRequested = true;
} }
handle.ShutdownAndNotify(HandleServerShutdown);
handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
await shutdownTcs.Task; await shutdownTcs.Task;
handle.Dispose(); handle.Dispose();
} }
@ -159,8 +160,22 @@ namespace Grpc.Core
} }
} }
public void Kill() /// <summary>
/// Requests server shutdown while cancelling all the in-progress calls.
/// The returned task finishes when shutdown procedure is complete.
/// </summary>
public async Task KillAsync()
{ {
lock (myLock)
{
Preconditions.CheckState(startRequested);
Preconditions.CheckState(!shutdownRequested);
shutdownRequested = true;
}
handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
handle.CancelAllCalls();
await shutdownTcs.Task;
handle.Dispose(); handle.Dispose();
} }

@ -648,14 +648,15 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) {
grpc_server_start(server); grpc_server_start(server);
} }
GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_shutdown(grpc_server *server) {
grpc_server_shutdown(server);
}
GPR_EXPORT void GPR_CALLTYPE GPR_EXPORT void GPR_CALLTYPE
grpcsharp_server_shutdown_and_notify_callback(grpc_server *server, grpcsharp_server_shutdown_and_notify_callback(grpc_server *server,
grpc_completion_queue *cq,
grpcsharp_batch_context *ctx) { grpcsharp_batch_context *ctx) {
grpc_server_shutdown_and_notify(server, ctx); grpc_server_shutdown_and_notify(server, cq, ctx);
}
GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_cancel_all_calls(grpc_server *server) {
grpc_server_cancel_all_calls(server);
} }
GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) { GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) {

@ -112,9 +112,17 @@ class NewCallOp : public Op {
} }
}; };
Server::Server(grpc_server *server) : wrapped_server(server) {} Server::Server(grpc_server *server) : wrapped_server(server) {
shutdown_queue = grpc_completion_queue_create();
grpc_server_register_completion_queue(server, shutdown_queue);
}
Server::~Server() { grpc_server_destroy(wrapped_server); } Server::~Server() {
this->ShutdownServer();
grpc_completion_queue_shutdown(this->shutdown_queue);
grpc_server_destroy(wrapped_server);
grpc_completion_queue_destroy(this->shutdown_queue);
}
void Server::Init(Handle<Object> exports) { void Server::Init(Handle<Object> exports) {
NanScope(); NanScope();
@ -148,6 +156,16 @@ bool Server::HasInstance(Handle<Value> val) {
return NanHasInstance(fun_tpl, val); return NanHasInstance(fun_tpl, val);
} }
void Server::ShutdownServer() {
if (this->wrapped_server != NULL) {
grpc_server_shutdown_and_notify(this->wrapped_server,
this->shutdown_queue,
NULL);
grpc_completion_queue_pluck(this->shutdown_queue, NULL, gpr_inf_future);
this->wrapped_server = NULL;
}
}
NAN_METHOD(Server::New) { NAN_METHOD(Server::New) {
NanScope(); NanScope();
@ -207,6 +225,9 @@ NAN_METHOD(Server::RequestCall) {
return NanThrowTypeError("requestCall can only be called on a Server"); return NanThrowTypeError("requestCall can only be called on a Server");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
if (server->wrapped_server == NULL) {
return NanThrowError("requestCall cannot be called on a shut down Server");
}
NewCallOp *op = new NewCallOp(); NewCallOp *op = new NewCallOp();
unique_ptr<OpVec> ops(new OpVec()); unique_ptr<OpVec> ops(new OpVec());
ops->push_back(unique_ptr<Op>(op)); ops->push_back(unique_ptr<Op>(op));
@ -232,6 +253,9 @@ NAN_METHOD(Server::AddHttp2Port) {
return NanThrowTypeError("addHttp2Port's argument must be a String"); return NanThrowTypeError("addHttp2Port's argument must be a String");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
if (server->wrapped_server == NULL) {
return NanThrowError("addHttp2Port cannot be called on a shut down Server");
}
NanReturnValue(NanNew<Number>(grpc_server_add_http2_port( NanReturnValue(NanNew<Number>(grpc_server_add_http2_port(
server->wrapped_server, *NanUtf8String(args[0])))); server->wrapped_server, *NanUtf8String(args[0]))));
} }
@ -251,6 +275,10 @@ NAN_METHOD(Server::AddSecureHttp2Port) {
"addSecureHttp2Port's second argument must be ServerCredentials"); "addSecureHttp2Port's second argument must be ServerCredentials");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
if (server->wrapped_server == NULL) {
return NanThrowError(
"addSecureHttp2Port cannot be called on a shut down Server");
}
ServerCredentials *creds = ObjectWrap::Unwrap<ServerCredentials>( ServerCredentials *creds = ObjectWrap::Unwrap<ServerCredentials>(
args[1]->ToObject()); args[1]->ToObject());
NanReturnValue(NanNew<Number>(grpc_server_add_secure_http2_port( NanReturnValue(NanNew<Number>(grpc_server_add_secure_http2_port(
@ -264,17 +292,24 @@ NAN_METHOD(Server::Start) {
return NanThrowTypeError("start can only be called on a Server"); return NanThrowTypeError("start can only be called on a Server");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
if (server->wrapped_server == NULL) {
return NanThrowError("start cannot be called on a shut down Server");
}
grpc_server_start(server->wrapped_server); grpc_server_start(server->wrapped_server);
NanReturnUndefined(); NanReturnUndefined();
} }
NAN_METHOD(ShutdownCallback) {
NanReturnUndefined();
}
NAN_METHOD(Server::Shutdown) { NAN_METHOD(Server::Shutdown) {
NanScope(); NanScope();
if (!HasInstance(args.This())) { if (!HasInstance(args.This())) {
return NanThrowTypeError("shutdown can only be called on a Server"); return NanThrowTypeError("shutdown can only be called on a Server");
} }
Server *server = ObjectWrap::Unwrap<Server>(args.This()); Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_server_shutdown(server->wrapped_server); server->ShutdownServer();
NanReturnUndefined(); NanReturnUndefined();
} }

@ -61,6 +61,8 @@ class Server : public ::node::ObjectWrap {
Server(const Server &); Server(const Server &);
Server &operator=(const Server &); Server &operator=(const Server &);
void ShutdownServer();
static NAN_METHOD(New); static NAN_METHOD(New);
static NAN_METHOD(RequestCall); static NAN_METHOD(RequestCall);
static NAN_METHOD(AddHttp2Port); static NAN_METHOD(AddHttp2Port);
@ -71,6 +73,7 @@ class Server : public ::node::ObjectWrap {
static v8::Persistent<v8::FunctionTemplate> fun_tpl; static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_server *wrapped_server; grpc_server *wrapped_server;
grpc_completion_queue *shutdown_queue;
}; };
} // namespace node } // namespace node

@ -63,7 +63,8 @@ zend_class_entry *grpc_ce_server;
void free_wrapped_grpc_server(void *object TSRMLS_DC) { void free_wrapped_grpc_server(void *object TSRMLS_DC) {
wrapped_grpc_server *server = (wrapped_grpc_server *)object; wrapped_grpc_server *server = (wrapped_grpc_server *)object;
if (server->wrapped != NULL) { if (server->wrapped != NULL) {
grpc_server_shutdown(server->wrapped); grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL);
grpc_completion_queue_pluck(completion_queue, NULL, gpr_inf_future);
grpc_server_destroy(server->wrapped); grpc_server_destroy(server->wrapped);
} }
efree(server); efree(server);

@ -167,17 +167,13 @@ PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) {
PyObject *pygrpc_Server_shutdown( PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs) { Server *self, PyObject *args, PyObject *kwargs) {
PyObject *user_tag = NULL; PyObject *user_tag;
pygrpc_tag *tag; pygrpc_tag *tag;
static char *keywords[] = {"tag", NULL}; static char *keywords[] = {"tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", keywords, &user_tag)) { if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) {
return NULL; return NULL;
} }
if (user_tag) { tag = pygrpc_produce_server_shutdown_tag(user_tag);
tag = pygrpc_produce_server_shutdown_tag(user_tag); grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag);
grpc_server_shutdown_and_notify(self->c_serv, tag);
} else {
grpc_server_shutdown(self->c_serv);
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }

@ -123,7 +123,8 @@ PyObject *pygrpc_consume_event(grpc_event event) {
event.success ? Py_True : Py_False); event.success ? Py_True : Py_False);
} else { } else {
result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag, result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag,
tag->call, Py_None, pygrpc_consume_ops(tag->ops, tag->nops), tag->call ? (PyObject*)tag->call : Py_None, Py_None,
pygrpc_consume_ops(tag->ops, tag->nops),
event.success ? Py_True : Py_False); event.success ? Py_True : Py_False);
} }
break; break;

@ -207,7 +207,7 @@ class CompletionQueue(object):
complete_accepted = ev.success if kind == Event.Kind.COMPLETE_ACCEPTED else None complete_accepted = ev.success if kind == Event.Kind.COMPLETE_ACCEPTED else None
service_acceptance = ServiceAcceptance(Call._from_internal(ev.call), ev.call_details.method, ev.call_details.host, ev.call_details.deadline) if kind == Event.Kind.SERVICE_ACCEPTED else None service_acceptance = ServiceAcceptance(Call._from_internal(ev.call), ev.call_details.method, ev.call_details.host, ev.call_details.deadline) if kind == Event.Kind.SERVICE_ACCEPTED else None
message_bytes = ev.results[0].message if kind == Event.Kind.READ_ACCEPTED else None message_bytes = ev.results[0].message if kind == Event.Kind.READ_ACCEPTED else None
status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if ev.results[0].cancelled is not None else None status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if len(ev.results) > 0 and ev.results[0].cancelled is not None else None
metadata = ev.results[0].initial_metadata if (kind in [Event.Kind.SERVICE_ACCEPTED, Event.Kind.METADATA_ACCEPTED]) else (ev.results[0].trailing_metadata if kind == Event.Kind.FINISH else None) metadata = ev.results[0].initial_metadata if (kind in [Event.Kind.SERVICE_ACCEPTED, Event.Kind.METADATA_ACCEPTED]) else (ev.results[0].trailing_metadata if kind == Event.Kind.FINISH else None)
else: else:
raise RuntimeError('unknown event') raise RuntimeError('unknown event')
@ -241,7 +241,7 @@ class Server(object):
return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED)) return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED))
def stop(self): def stop(self):
return self._internal.shutdown() return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP))
class ClientCredentials(object): class ClientCredentials(object):

@ -94,14 +94,6 @@ class EchoTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.server.stop() self.server.stop()
# NOTE(nathaniel): Yep, this is weird; it's a consequence of
# grpc_server_destroy's being what has the effect of telling the server's
# completion queue to pump out all pending events/tags immediately rather
# than gracefully completing all outstanding RPCs while accepting no new
# ones.
# TODO(nathaniel): Deallocation of a Python object shouldn't have this kind
# of observable side effect let alone such an important one.
del self.server
self.server_completion_queue.stop() self.server_completion_queue.stop()
self.client_completion_queue.stop() self.client_completion_queue.stop()
while True: while True:
@ -114,6 +106,7 @@ class EchoTest(unittest.TestCase):
break break
self.server_completion_queue = None self.server_completion_queue = None
self.client_completion_queue = None self.client_completion_queue = None
del self.server
def _perform_echo_test(self, test_data): def _perform_echo_test(self, test_data):
method = 'test method' method = 'test method'
@ -316,7 +309,6 @@ class CancellationTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.server.stop() self.server.stop()
del self.server
self.server_completion_queue.stop() self.server_completion_queue.stop()
self.client_completion_queue.stop() self.client_completion_queue.stop()
while True: while True:
@ -327,6 +319,7 @@ class CancellationTest(unittest.TestCase):
event = self.client_completion_queue.get(0) event = self.client_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP: if event is not None and event.kind is _low.Event.Kind.STOP:
break break
del self.server
def testCancellation(self): def testCancellation(self):
method = 'test method' method = 'test method'

@ -101,11 +101,8 @@ class Server(_types.Server):
def start(self): def start(self):
return self.server.start() return self.server.start()
def shutdown(self, tag=_NO_TAG): def shutdown(self, tag=None):
if tag is _NO_TAG: return self.server.shutdown(tag)
return self.server.shutdown()
else:
return self.server.shutdown(tag)
def request_call(self, completion_queue, tag): def request_call(self, completion_queue, tag):
return self.server.request_call(completion_queue.completion_queue, tag) return self.server.request_call(completion_queue.completion_queue, tag)

@ -48,7 +48,6 @@ class InsecureServerInsecureClient(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.server.shutdown() self.server.shutdown()
del self.client_channel del self.client_channel
del self.server
self.client_completion_queue.shutdown() self.client_completion_queue.shutdown()
while self.client_completion_queue.next().type != _types.EventType.QUEUE_SHUTDOWN: while self.client_completion_queue.next().type != _types.EventType.QUEUE_SHUTDOWN:
@ -59,6 +58,7 @@ class InsecureServerInsecureClient(unittest.TestCase):
del self.client_completion_queue del self.client_completion_queue
del self.server_completion_queue del self.server_completion_queue
del self.server
def testEcho(self): def testEcho(self):
DEADLINE = time.time()+5 DEADLINE = time.time()+5

@ -142,8 +142,16 @@ grpc_event grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag,
MEMZERO(&next_call, next_call_stack, 1); MEMZERO(&next_call, next_call_stack, 1);
TypedData_Get_Struct(self, grpc_completion_queue, TypedData_Get_Struct(self, grpc_completion_queue,
&grpc_rb_completion_queue_data_type, next_call.cq); &grpc_rb_completion_queue_data_type, next_call.cq);
next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0); if (TYPE(timeout) == T_NIL) {
next_call.tag = ROBJECT(tag); next_call.timeout = gpr_inf_future;
} else {
next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
}
if (TYPE(tag) == T_NIL) {
next_call.tag = NULL;
} else {
next_call.tag = ROBJECT(tag);
}
next_call.event.type = GRPC_QUEUE_TIMEOUT; next_call.event.type = GRPC_QUEUE_TIMEOUT;
rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil, rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
(void *)&next_call, NULL, NULL); (void *)&next_call, NULL, NULL);

@ -210,7 +210,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
VALUE result; VALUE result;
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
if (s->wrapped == NULL) { if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "destroyed!");
return Qnil; return Qnil;
} else { } else {
grpc_request_call_stack_init(&st); grpc_request_call_stack_init(&st);
@ -259,21 +259,69 @@ static VALUE grpc_rb_server_start(VALUE self) {
grpc_rb_server *s = NULL; grpc_rb_server *s = NULL;
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
if (s->wrapped == NULL) { if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "destroyed!");
} else { } else {
grpc_server_start(s->wrapped); grpc_server_start(s->wrapped);
} }
return Qnil; return Qnil;
} }
static VALUE grpc_rb_server_destroy(VALUE self) { /*
call-seq:
cq = CompletionQueue.new
server = Server.new(cq, {'arg1': 'value1'})
... // do stuff with server
...
... // to shutdown the server
server.destroy(cq)
... // to shutdown the server with a timeout
server.destroy(cq, timeout)
Destroys server instances. */
static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
VALUE cqueue = Qnil;
VALUE timeout = Qnil;
grpc_completion_queue *cq = NULL;
grpc_event ev;
grpc_rb_server *s = NULL; grpc_rb_server *s = NULL;
/* "11" == 1 mandatory args, 1 (timeout) is optional */
rb_scan_args(argc, argv, "11", &cqueue, &timeout);
cq = grpc_rb_get_wrapped_completion_queue(cqueue);
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
if (s->wrapped != NULL) { if (s->wrapped != NULL) {
grpc_server_shutdown(s->wrapped); grpc_server_shutdown_and_notify(s->wrapped, cq, NULL);
ev = grpc_rb_completion_queue_pluck_event(cqueue, Qnil, timeout);
if (!ev.success) {
rb_warn("server shutdown failed, there will be a LEAKED object warning");
return Qnil;
/*
TODO: renable the rb_raise below.
At the moment if the timeout is INFINITE_FUTURE as recommended, the
pluck blocks forever, even though
the outstanding server_request_calls correctly fail on the other
thread that they are running on.
it's almost as if calls that fail on the other thread do not get
cleaned up by shutdown request, even though it caused htem to
terminate.
rb_raise(rb_eRuntimeError, "grpc server shutdown did not succeed");
return Qnil;
The workaround is just to use a timeout and return without really
shutting down the server, and rely on the grpc core garbage collection
it down as a 'LEAKED OBJECT'.
*/
}
grpc_server_destroy(s->wrapped); grpc_server_destroy(s->wrapped);
s->wrapped = NULL; s->wrapped = NULL;
s->mark = Qnil;
} }
return Qnil; return Qnil;
} }
@ -302,7 +350,7 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
if (s->wrapped == NULL) { if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!"); rb_raise(rb_eRuntimeError, "destroyed!");
return Qnil; return Qnil;
} else if (rb_creds == Qnil) { } else if (rb_creds == Qnil) {
recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port)); recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
@ -315,7 +363,7 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
creds = grpc_rb_get_wrapped_server_credentials(rb_creds); creds = grpc_rb_get_wrapped_server_credentials(rb_creds);
recvd_port = recvd_port =
grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port), grpc_server_add_secure_http2_port(s->wrapped, StringValueCStr(port),
creds); creds);
if (recvd_port == 0) { if (recvd_port == 0) {
rb_raise(rb_eRuntimeError, rb_raise(rb_eRuntimeError,
"could not add secure port %s to server, not sure why", "could not add secure port %s to server, not sure why",
@ -341,7 +389,7 @@ void Init_grpc_server() {
rb_define_method(grpc_rb_cServer, "request_call", rb_define_method(grpc_rb_cServer, "request_call",
grpc_rb_server_request_call, 3); grpc_rb_server_request_call, 3);
rb_define_method(grpc_rb_cServer, "start", grpc_rb_server_start, 0); rb_define_method(grpc_rb_cServer, "start", grpc_rb_server_start, 0);
rb_define_method(grpc_rb_cServer, "destroy", grpc_rb_server_destroy, 0); rb_define_method(grpc_rb_cServer, "destroy", grpc_rb_server_destroy, -1);
rb_define_alias(grpc_rb_cServer, "close", "destroy"); rb_define_alias(grpc_rb_cServer, "close", "destroy");
rb_define_method(grpc_rb_cServer, "add_http2_port", rb_define_method(grpc_rb_cServer, "add_http2_port",
grpc_rb_server_add_http2_port, grpc_rb_server_add_http2_port,

@ -278,7 +278,9 @@ module GRPC
@stopped = true @stopped = true
end end
@pool.stop @pool.stop
@server.close deadline = from_relative_time(@poll_period)
@server.close(@cq, deadline)
end end
# determines if the server has been stopped # determines if the server has been stopped
@ -410,17 +412,18 @@ module GRPC
# handles calls to the server # handles calls to the server
def loop_handle_server_calls def loop_handle_server_calls
fail 'not running' unless @running fail 'not running' unless @running
request_call_tag = Object.new loop_tag = Object.new
until stopped? until stopped?
deadline = from_relative_time(@poll_period) deadline = from_relative_time(@poll_period)
begin begin
an_rpc = @server.request_call(@cq, request_call_tag, deadline) an_rpc = @server.request_call(@cq, loop_tag, deadline)
c = new_active_server_call(an_rpc)
rescue Core::CallError, RuntimeError => e rescue Core::CallError, RuntimeError => e
# can happen during server shutdown # these might happen for various reasonse. The correct behaviour of
# the server is to log them and continue.
GRPC.logger.warn("server call failed: #{e}") GRPC.logger.warn("server call failed: #{e}")
next next
end end
c = new_active_server_call(an_rpc)
unless c.nil? unless c.nil?
mth = an_rpc.method.to_sym mth = an_rpc.method.to_sym
@pool.schedule(c) do |call| @pool.schedule(c) do |call|

@ -42,11 +42,8 @@ shared_context 'setup: tags' do
let(:sent_message) { 'sent message' } let(:sent_message) { 'sent message' }
let(:reply_text) { 'the reply' } let(:reply_text) { 'the reply' }
before(:example) do before(:example) do
@server_finished_tag = Object.new @client_tag = Object.new
@client_finished_tag = Object.new
@client_metadata_tag = Object.new
@server_tag = Object.new @server_tag = Object.new
@tag = Object.new
end end
def deadline def deadline
@ -351,7 +348,7 @@ describe 'the http client/server' do
after(:example) do after(:example) do
@ch.close @ch.close
@server.close @server.close(@server_queue, deadline)
end end
it_behaves_like 'basic GRPC message delivery is OK' do it_behaves_like 'basic GRPC message delivery is OK' do
@ -377,7 +374,7 @@ describe 'the secure http client/server' do
end end
after(:example) do after(:example) do
@server.close @server.close(@server_queue, deadline)
end end
it_behaves_like 'basic GRPC message delivery is OK' do it_behaves_like 'basic GRPC message delivery is OK' do

@ -51,7 +51,7 @@ describe GRPC::ActiveCall do
end end
after(:each) do after(:each) do
@server.close @server.close(@server_queue, deadline)
end end
describe 'restricted view methods' do describe 'restricted view methods' do

@ -54,6 +54,7 @@ describe 'ClientStub' do
before(:each) do before(:each) do
Thread.abort_on_exception = true Thread.abort_on_exception = true
@server = nil @server = nil
@server_queue = nil
@method = 'an_rpc_method' @method = 'an_rpc_method'
@pass = OK @pass = OK
@fail = INTERNAL @fail = INTERNAL
@ -61,7 +62,7 @@ describe 'ClientStub' do
end end
after(:each) do after(:each) do
@server.close unless @server.nil? @server.close(@server_queue) unless @server_queue.nil?
end end
describe '#new' do describe '#new' do

@ -136,10 +136,6 @@ describe GRPC::RpcServer do
@ch = GRPC::Core::Channel.new(@host, nil) @ch = GRPC::Core::Channel.new(@host, nil)
end end
after(:each) do
@server.close
end
describe '#new' do describe '#new' do
it 'can be created with just some args' do it 'can be created with just some args' do
opts = { a_channel_arg: 'an_arg' } opts = { a_channel_arg: 'an_arg' }
@ -344,10 +340,6 @@ describe GRPC::RpcServer do
@srv = RpcServer.new(**server_opts) @srv = RpcServer.new(**server_opts)
end end
after(:each) do
@srv.stop
end
it 'should return NOT_FOUND status on unknown methods', server: true do it 'should return NOT_FOUND status on unknown methods', server: true do
@srv.handle(EchoService) @srv.handle(EchoService)
t = Thread.new { @srv.run } t = Thread.new { @srv.run }
@ -527,10 +519,6 @@ describe GRPC::RpcServer do
@srv = RpcServer.new(**server_opts) @srv = RpcServer.new(**server_opts)
end end
after(:each) do
@srv.stop
end
it 'should send connect metadata to the client', server: true do it 'should send connect metadata to the client', server: true do
service = EchoService.new service = EchoService.new
@srv.handle(service) @srv.handle(service)

@ -54,7 +54,7 @@ describe Server do
it 'fails if the server is closed' do it 'fails if the server is closed' do
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.close s.close(@cq)
expect { s.start }.to raise_error(RuntimeError) expect { s.start }.to raise_error(RuntimeError)
end end
end end
@ -62,19 +62,19 @@ describe Server do
describe '#destroy' do describe '#destroy' do
it 'destroys a server ok' do it 'destroys a server ok' do
s = start_a_server s = start_a_server
blk = proc { s.destroy } blk = proc { s.destroy(@cq) }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
s = start_a_server s = start_a_server
begin begin
blk = proc { s.destroy } blk = proc { s.destroy(@cq) }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
blk.call blk.call
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
ensure ensure
s.close s.close(@cq)
end end
end end
end end
@ -83,16 +83,16 @@ describe Server do
it 'closes a server ok' do it 'closes a server ok' do
s = start_a_server s = start_a_server
begin begin
blk = proc { s.close } blk = proc { s.close(@cq) }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
ensure ensure
s.close s.close(@cq)
end end
end end
it 'can be called more than once without error' do it 'can be called more than once without error' do
s = start_a_server s = start_a_server
blk = proc { s.close } blk = proc { s.close(@cq) }
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
blk.call blk.call
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
@ -105,14 +105,14 @@ describe Server do
blk = proc do blk = proc do
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.add_http2_port('localhost:0') s.add_http2_port('localhost:0')
s.close s.close(@cq)
end end
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'fails if the server is closed' do it 'fails if the server is closed' do
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.close s.close(@cq)
expect { s.add_http2_port('localhost:0') }.to raise_error(RuntimeError) expect { s.add_http2_port('localhost:0') }.to raise_error(RuntimeError)
end end
end end
@ -123,14 +123,14 @@ describe Server do
blk = proc do blk = proc do
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.add_http2_port('localhost:0', cert) s.add_http2_port('localhost:0', cert)
s.close s.close(@cq)
end end
expect(&blk).to_not raise_error expect(&blk).to_not raise_error
end end
it 'fails if the server is closed' do it 'fails if the server is closed' do
s = Server.new(@cq, nil) s = Server.new(@cq, nil)
s.close s.close(@cq)
blk = proc { s.add_http2_port('localhost:0', cert) } blk = proc { s.add_http2_port('localhost:0', cert) }
expect(&blk).to raise_error(RuntimeError) expect(&blk).to raise_error(RuntimeError)
end end

@ -143,6 +143,10 @@ void grpc_run_bad_client_test(grpc_bad_client_server_side_validator validator,
if (sfd.client) { if (sfd.client) {
grpc_endpoint_destroy(sfd.client); grpc_endpoint_destroy(sfd.client);
} }
grpc_server_shutdown_and_notify(a.server, a.cq, NULL);
GPR_ASSERT(grpc_completion_queue_pluck(a.cq, NULL,
GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1))
.type == GRPC_OP_COMPLETE);
grpc_server_destroy(a.server); grpc_server_destroy(a.server);
grpc_completion_queue_destroy(a.cq); grpc_completion_queue_destroy(a.cq);

@ -213,7 +213,8 @@ void test_connect(const char *server_host, const char *client_host, int port,
grpc_completion_queue_destroy(client_cq); grpc_completion_queue_destroy(client_cq);
/* Destroy server. */ /* Destroy server. */
grpc_server_shutdown(server); grpc_server_shutdown_and_notify(server, server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(server); grpc_server_destroy(server);
grpc_completion_queue_shutdown(server_cq); grpc_completion_queue_shutdown(server_cq);
drain_cq(server_cq); drain_cq(server_cq);

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -46,6 +46,8 @@
enum { TIMEOUT = 200000 }; enum { TIMEOUT = 200000 };
static void *tag(gpr_intptr t) { return (void *)t; }
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
const char *test_name, const char *test_name,
grpc_channel_args *client_args, grpc_channel_args *client_args,
@ -73,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -61,9 +61,12 @@ static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
return f; return f;
} }
static void *tag(gpr_intptr t) { return (void *)t; }
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }
@ -93,8 +96,6 @@ static void end_test(grpc_end2end_test_fixture *f) {
grpc_completion_queue_destroy(f->client_cq); grpc_completion_queue_destroy(f->client_cq);
} }
static void *tag(gpr_intptr t) { return (void *)t; }
static void test_body(grpc_end2end_test_fixture f) { static void test_body(grpc_end2end_test_fixture f) {
grpc_call *c; grpc_call *c;
grpc_call *s; grpc_call *s;

@ -62,7 +62,6 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }
@ -141,7 +140,7 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f,
/* should be able to shut down the server early /* should be able to shut down the server early
- and still complete the request */ - and still complete the request */
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
op = ops; op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA; op->op = GRPC_OP_SEND_INITIAL_METADATA;
@ -161,6 +160,7 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f,
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102))); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
cq_expect_completion(v_server, tag(102), 1); cq_expect_completion(v_server, tag(102), 1);
cq_expect_completion(v_server, tag(1000), 1);
cq_verify(v_server); cq_verify(v_server);
cq_expect_completion(v_client, tag(1), 1); cq_expect_completion(v_client, tag(1), 1);

@ -72,13 +72,6 @@ static void drain_cq(grpc_completion_queue *cq) {
} while (ev.type != GRPC_QUEUE_SHUTDOWN); } while (ev.type != GRPC_QUEUE_SHUTDOWN);
} }
static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return;
grpc_server_shutdown(f->server);
grpc_server_destroy(f->server);
f->server = NULL;
}
static void shutdown_client(grpc_end2end_test_fixture *f) { static void shutdown_client(grpc_end2end_test_fixture *f) {
if (!f->client) return; if (!f->client) return;
grpc_channel_destroy(f->client); grpc_channel_destroy(f->client);
@ -86,7 +79,6 @@ static void shutdown_client(grpc_end2end_test_fixture *f) {
} }
static void end_test(grpc_end2end_test_fixture *f) { static void end_test(grpc_end2end_test_fixture *f) {
shutdown_server(f);
shutdown_client(f); shutdown_client(f);
grpc_completion_queue_shutdown(f->server_cq); grpc_completion_queue_shutdown(f->server_cq);
@ -162,11 +154,15 @@ static void test_early_server_shutdown_finishes_inflight_calls(
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102))); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
/* shutdown and destroy the server */ /* shutdown and destroy the server */
shutdown_server(&f); grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(1000));
grpc_server_cancel_all_calls(f.server);
cq_expect_completion(v_server, tag(102), 1); cq_expect_completion(v_server, tag(102), 1);
cq_expect_completion(v_server, tag(1000), 1);
cq_verify(v_server); cq_verify(v_server);
grpc_server_destroy(f.server);
cq_expect_completion(v_client, tag(1), 1); cq_expect_completion(v_client, tag(1), 1);
cq_verify(v_client); cq_verify(v_client);

@ -72,13 +72,6 @@ static void drain_cq(grpc_completion_queue *cq) {
} while (ev.type != GRPC_QUEUE_SHUTDOWN); } while (ev.type != GRPC_QUEUE_SHUTDOWN);
} }
static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return;
/* don't shutdown, just destroy, to tickle this code edge */
grpc_server_destroy(f->server);
f->server = NULL;
}
static void shutdown_client(grpc_end2end_test_fixture *f) { static void shutdown_client(grpc_end2end_test_fixture *f) {
if (!f->client) return; if (!f->client) return;
grpc_channel_destroy(f->client); grpc_channel_destroy(f->client);
@ -86,7 +79,6 @@ static void shutdown_client(grpc_end2end_test_fixture *f) {
} }
static void end_test(grpc_end2end_test_fixture *f) { static void end_test(grpc_end2end_test_fixture *f) {
shutdown_server(f);
shutdown_client(f); shutdown_client(f);
grpc_completion_queue_shutdown(f->server_cq); grpc_completion_queue_shutdown(f->server_cq);
@ -114,11 +106,14 @@ static void test_early_server_shutdown_finishes_tags(
grpc_server_request_call(f.server, &s, &call_details, grpc_server_request_call(f.server, &s, &call_details,
&request_metadata_recv, f.server_cq, &request_metadata_recv, f.server_cq,
f.server_cq, tag(101))); f.server_cq, tag(101)));
grpc_server_shutdown(f.server); grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(1000));
cq_expect_completion(v_server, tag(101), 0); cq_expect_completion(v_server, tag(101), 0);
cq_expect_completion(v_server, tag(1000), 1);
cq_verify(v_server); cq_verify(v_server);
GPR_ASSERT(s == NULL); GPR_ASSERT(s == NULL);
grpc_server_destroy(f.server);
end_test(&f); end_test(&f);
config.tear_down_data(&f); config.tear_down_data(&f);
cq_verifier_destroy(v_server); cq_verifier_destroy(v_server);

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -154,7 +154,7 @@ static void test_early_server_shutdown_finishes_inflight_calls(
cq_verify(v_server); cq_verify(v_server);
/* shutdown and destroy the server */ /* shutdown and destroy the server */
grpc_server_shutdown_and_notify(f.server, tag(0xdead)); grpc_server_shutdown_and_notify(f.server, f.server_cq, tag(0xdead));
cq_verify_empty(v_server); cq_verify_empty(v_server);
op = ops; op = ops;
@ -175,11 +175,10 @@ static void test_early_server_shutdown_finishes_inflight_calls(
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102))); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
cq_expect_completion(v_server, tag(102), 1); cq_expect_completion(v_server, tag(102), 1);
cq_expect_completion(v_server, tag(0xdead), 1);
cq_verify(v_server); cq_verify(v_server);
grpc_call_destroy(s); grpc_call_destroy(s);
cq_expect_completion(v_server, tag(0xdead), 1);
cq_verify(v_server);
cq_expect_completion(v_client, tag(1), 1); cq_expect_completion(v_client, tag(1), 1);
cq_verify(v_client); cq_verify(v_client);

@ -72,7 +72,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -45,6 +45,8 @@
enum { TIMEOUT = 200000 }; enum { TIMEOUT = 200000 };
static void *tag(gpr_intptr t) { return (void *)t; }
static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
const char *test_name, const char *test_name,
grpc_channel_args *client_args, grpc_channel_args *client_args,
@ -72,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -88,7 +88,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -75,7 +75,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -74,7 +74,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -62,7 +62,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -76,7 +76,8 @@ static void drain_cq(grpc_completion_queue *cq) {
static void shutdown_server(grpc_end2end_test_fixture *f) { static void shutdown_server(grpc_end2end_test_fixture *f) {
if (!f->server) return; if (!f->server) return;
grpc_server_shutdown(f->server); grpc_server_shutdown_and_notify(f->server, f->server_cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(f->server_cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_server_destroy(f->server); grpc_server_destroy(f->server);
f->server = NULL; f->server = NULL;
} }

@ -233,7 +233,8 @@ int main(int argc, char **argv) {
while (!shutdown_finished) { while (!shutdown_finished) {
if (got_sigint && !shutdown_started) { if (got_sigint && !shutdown_started) {
gpr_log(GPR_INFO, "Shutting down due to SIGINT"); gpr_log(GPR_INFO, "Shutting down due to SIGINT");
grpc_server_shutdown(server); grpc_server_shutdown_and_notify(server, cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5)).type == GRPC_OP_COMPLETE);
grpc_completion_queue_shutdown(cq); grpc_completion_queue_shutdown(cq);
shutdown_started = 1; shutdown_started = 1;
} }

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC C++"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.9.1.0 PROJECT_NUMBER = 0.10.0.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC C++"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.9.1.0 PROJECT_NUMBER = 0.10.0.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC Core"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.9.1.0 PROJECT_NUMBER = 0.10.0.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -40,7 +40,7 @@ PROJECT_NAME = "GRPC Core"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.9.1.0 PROJECT_NUMBER = 0.10.0.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

Loading…
Cancel
Save