diff options
Diffstat (limited to 'src')
72 files changed, 1855 insertions, 928 deletions
diff --git a/src/compiler/config.h b/src/compiler/config.h index ba44cd8a31..fd1400cd24 100644 --- a/src/compiler/config.h +++ b/src/compiler/config.h @@ -96,4 +96,11 @@ typedef GRPC_CUSTOM_STRINGOUTPUTSTREAM StringOutputStream; } // namespace protobuf } // namespace grpc +namespace grpc_cpp_generator { + +static const char* const kCppGeneratorMessageHeaderExt = ".pb.h"; +static const char* const kCppGeneratorServiceHeaderExt = ".grpc.pb.h"; + +} // namespace grpc_cpp_generator + #endif // SRC_COMPILER_CONFIG_H diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index a1a0258c68..7a2c44fd46 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -40,9 +40,6 @@ namespace grpc_cpp_generator { namespace { -grpc::string message_header_ext() { return ".pb.h"; } -grpc::string service_header_ext() { return ".grpc.pb.h"; } - template <class T> grpc::string as_string(T x) { std::ostringstream out; @@ -113,7 +110,7 @@ grpc::string GetHeaderPrologue(grpc_generator::File *file, vars["filename"] = file->filename(); vars["filename_identifier"] = FilenameIdentifier(file->filename()); vars["filename_base"] = file->filename_without_ext(); - vars["message_header_ext"] = message_header_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; printer->Print(vars, "// Generated by the gRPC C++ plugin.\n"); printer->Print(vars, @@ -128,6 +125,7 @@ grpc::string GetHeaderPrologue(grpc_generator::File *file, printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n"); printer->Print(vars, "\n"); printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); printer->Print(vars, "\n"); } return output; @@ -1039,8 +1037,8 @@ grpc::string GetSourcePrologue(grpc_generator::File *file, vars["filename"] = file->filename(); vars["filename_base"] = file->filename_without_ext(); - vars["message_header_ext"] = message_header_ext(); - vars["service_header_ext"] = service_header_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; + vars["service_header_ext"] = kCppGeneratorServiceHeaderExt; printer->Print(vars, "// Generated by the gRPC C++ plugin.\n"); printer->Print(vars, @@ -1049,7 +1047,6 @@ grpc::string GetSourcePrologue(grpc_generator::File *file, printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); - printer->Print(vars, file->additional_headers().c_str()); printer->Print(vars, "\n"); } return output; @@ -1425,8 +1422,8 @@ grpc::string GetMockPrologue(grpc_generator::File *file, vars["filename"] = file->filename(); vars["filename_base"] = file->filename_without_ext(); - vars["message_header_ext"] = message_header_ext(); - vars["service_header_ext"] = service_header_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; + vars["service_header_ext"] = kCppGeneratorServiceHeaderExt; printer->Print(vars, "// Generated by the gRPC C++ plugin.\n"); printer->Print(vars, diff --git a/src/core/ext/filters/client_channel/client_channel.c b/src/core/ext/filters/client_channel/client_channel.c index 24843d52e9..f2f27b9175 100644 --- a/src/core/ext/filters/client_channel/client_channel.c +++ b/src/core/ext/filters/client_channel/client_channel.c @@ -914,10 +914,13 @@ static void subchannel_ready_locked(grpc_exec_ctx *exec_ctx, void *arg, grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent, chand->interested_parties); if (calld->connected_subchannel == NULL) { - gpr_atm_no_barrier_store(&calld->subchannel_call, 1); + gpr_atm_no_barrier_store(&calld->subchannel_call, (gpr_atm)CANCELLED_CALL); fail_locked(exec_ctx, calld, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to create subchannel", &error, 1)); + error == GRPC_ERROR_NONE + ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Call dropped by load balancing policy") + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to create subchannel", &error, 1)); } else if (GET_CALL(calld) == CANCELLED_CALL) { /* already cancelled before subchannel became ready */ grpc_error *cancellation_error = @@ -1180,6 +1183,15 @@ static void start_transport_stream_op_batch_locked_inner( &calld->next_step)) { calld->pick_pending = false; GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel"); + if (calld->connected_subchannel == NULL) { + gpr_atm_no_barrier_store(&calld->subchannel_call, + (gpr_atm)CANCELLED_CALL); + grpc_error *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Call dropped by load balancing policy"); + fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error)); + grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error); + return; // Early out. + } } else { grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent, chand->interested_parties); diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h index 184b2ef720..fb4aa084a6 100644 --- a/src/core/ext/filters/client_channel/lb_policy.h +++ b/src/core/ext/filters/client_channel/lb_policy.h @@ -149,7 +149,9 @@ void grpc_lb_policy_init(grpc_lb_policy *policy, /** Finds an appropriate subchannel for a call, based on \a pick_args. - \a target will be set to the selected subchannel, or NULL on failure. + \a target will be set to the selected subchannel, or NULL on failure + or when the LB policy decides to drop the call. + Upon success, \a user_data will be set to whatever opaque information may need to be propagated from the LB policy, or NULL if not needed. \a context will be populated with context to pass to the subchannel diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c index b7c0e929b7..d2a2856a18 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c @@ -327,6 +327,11 @@ typedef struct glb_lb_policy { * response has arrived. */ grpc_grpclb_serverlist *serverlist; + /** Index into serverlist for next pick. + * If the server at this index is a drop, we return a drop. + * Otherwise, we delegate to the RR policy. */ + size_t serverlist_index; + /** list of picks that are waiting on RR's policy connectivity */ pending_pick *pending_picks; @@ -402,6 +407,9 @@ struct rr_connectivity_data { static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, bool log) { + if (server->drop_for_rate_limiting || server->drop_for_load_balancing) { + return false; + } const grpc_grpclb_ip_address *ip = &server->ip_address; if (server->port >> 16 != 0) { if (log) { @@ -411,7 +419,6 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, } return false; } - if (ip->size != 4 && ip->size != 16) { if (log) { gpr_log(GPR_ERROR, @@ -445,11 +452,12 @@ static const grpc_lb_user_data_vtable lb_token_vtable = { static void parse_server(const grpc_grpclb_server *server, grpc_resolved_address *addr) { + memset(addr, 0, sizeof(*addr)); + if (server->drop_for_rate_limiting || server->drop_for_load_balancing) return; const uint16_t netorder_port = htons((uint16_t)server->port); /* the addresses are given in binary format (a in(6)_addr struct) in * server->ip_address.bytes. */ const grpc_grpclb_ip_address *ip = &server->ip_address; - memset(addr, 0, sizeof(*addr)); if (ip->size == 4) { addr->len = sizeof(struct sockaddr_in); struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr; @@ -586,16 +594,51 @@ static bool update_lb_connectivity_status_locked( return true; } -/* perform a pick over \a rr_policy. Given that a pick can return immediately - * (ignoring its completion callback) we need to perform the cleanups this - * callback would be otherwise resposible for */ +/* Perform a pick over \a glb_policy->rr_policy. Given that a pick can return + * immediately (ignoring its completion callback), we need to perform the + * cleanups this callback would otherwise be resposible for. + * If \a force_async is true, then we will manually schedule the + * completion callback even if the pick is available immediately. */ static bool pick_from_internal_rr_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *rr_policy, - const grpc_lb_policy_pick_args *pick_args, + grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, + const grpc_lb_policy_pick_args *pick_args, bool force_async, grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) { - GPR_ASSERT(rr_policy != NULL); + // Look at the index into the serverlist to see if we should drop this call. + grpc_grpclb_server *server = + glb_policy->serverlist->servers[glb_policy->serverlist_index++]; + if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) { + glb_policy->serverlist_index = 0; // Wrap-around. + } + if (server->drop_for_rate_limiting || server->drop_for_load_balancing) { + // Not using the RR policy, so unref it. + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Unreffing RR for drop (0x%" PRIxPTR ")", + (intptr_t)wc_arg->rr_policy); + } + GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); + // Update client load reporting stats to indicate the number of + // dropped calls. Note that we have to do this here instead of in + // the client_load_reporting filter, because we do not create a + // subchannel call (and therefore no client_load_reporting filter) + // for dropped calls. + grpc_grpclb_client_stats_add_call_started(wc_arg->client_stats); + grpc_grpclb_client_stats_add_call_finished( + server->drop_for_rate_limiting, server->drop_for_load_balancing, + false /* failed_to_send */, false /* known_received */, + wc_arg->client_stats); + grpc_grpclb_client_stats_unref(wc_arg->client_stats); + if (force_async) { + GPR_ASSERT(wc_arg->wrapped_closure != NULL); + grpc_closure_sched(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); + gpr_free(wc_arg->free_when_done); + return false; + } + gpr_free(wc_arg->free_when_done); + return true; + } + // Pick via the RR policy. const bool pick_done = grpc_lb_policy_pick_locked( - exec_ctx, rr_policy, pick_args, target, wc_arg->context, + exec_ctx, wc_arg->rr_policy, pick_args, target, wc_arg->context, (void **)&wc_arg->lb_token, &wc_arg->wrapper_closure); if (pick_done) { /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */ @@ -604,17 +647,20 @@ static bool pick_from_internal_rr_locked( (intptr_t)wc_arg->rr_policy); } GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); - /* add the load reporting initial metadata */ initial_metadata_add_lb_token(exec_ctx, pick_args->initial_metadata, pick_args->lb_token_mdelem_storage, GRPC_MDELEM_REF(wc_arg->lb_token)); - // Pass on client stats via context. Passes ownership of the reference. GPR_ASSERT(wc_arg->client_stats != NULL); wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats; wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats; - + if (force_async) { + GPR_ASSERT(wc_arg->wrapped_closure != NULL); + grpc_closure_sched(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); + gpr_free(wc_arg->free_when_done); + return false; + } gpr_free(wc_arg->free_when_done); } /* else, the pending pick will be registered and taken care of by the @@ -744,8 +790,8 @@ static void rr_handover_locked(grpc_exec_ctx *exec_ctx, gpr_log(GPR_INFO, "Pending pick about to PICK from 0x%" PRIxPTR "", (intptr_t)glb_policy->rr_policy); } - pick_from_internal_rr_locked(exec_ctx, glb_policy->rr_policy, - &pp->pick_args, pp->target, + pick_from_internal_rr_locked(exec_ctx, glb_policy, &pp->pick_args, + true /* force_async */, pp->target, &pp->wrapped_on_complete_arg); } @@ -1115,8 +1161,9 @@ static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, wc_arg->lb_token_mdelem_storage = pick_args->lb_token_mdelem_storage; wc_arg->initial_metadata = pick_args->initial_metadata; wc_arg->free_when_done = wc_arg; - pick_done = pick_from_internal_rr_locked(exec_ctx, glb_policy->rr_policy, - pick_args, target, wc_arg); + pick_done = + pick_from_internal_rr_locked(exec_ctx, glb_policy, pick_args, + false /* force_async */, target, wc_arg); } else { if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { gpr_log(GPR_DEBUG, @@ -1517,7 +1564,7 @@ static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg, * serverlist instance will be destroyed either upon the next * update or in glb_destroy() */ glb_policy->serverlist = serverlist; - + glb_policy->serverlist_index = 0; rr_handover_locked(exec_ctx, glb_policy); } } else { diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c index 81b6932fae..90e7c2efe5 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c @@ -37,44 +37,39 @@ #include <grpc/support/alloc.h> +/* invoked once for every Server in ServerList */ +static bool count_serverlist(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + grpc_grpclb_serverlist *sl = *arg; + grpc_grpclb_server server; + if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + ++sl->num_servers; + return true; +} + typedef struct decode_serverlist_arg { - /* The first pass counts the number of servers in the server list. The second - * one allocates and decodes. */ - bool first_pass; /* The decoding callback is invoked once per server in serverlist. Remember * which index of the serverlist are we currently decoding */ size_t decoding_idx; - /* Populated after the first pass. Number of server in the input serverlist */ - size_t num_servers; /* The decoded serverlist */ - grpc_grpclb_server **servers; + grpc_grpclb_serverlist *serverlist; } decode_serverlist_arg; /* invoked once for every Server in ServerList */ static bool decode_serverlist(pb_istream_t *stream, const pb_field_t *field, void **arg) { decode_serverlist_arg *dec_arg = *arg; - if (dec_arg->first_pass) { /* count how many server do we have */ - grpc_grpclb_server server; - if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) { - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); - return false; - } - dec_arg->num_servers++; - } else { /* second pass. Actually decode. */ - grpc_grpclb_server *server = gpr_zalloc(sizeof(grpc_grpclb_server)); - GPR_ASSERT(dec_arg->num_servers > 0); - if (dec_arg->decoding_idx == 0) { /* first iteration of second pass */ - dec_arg->servers = - gpr_malloc(sizeof(grpc_grpclb_server *) * dec_arg->num_servers); - } - if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) { - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); - return false; - } - dec_arg->servers[dec_arg->decoding_idx++] = server; + GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx); + grpc_grpclb_server *server = gpr_zalloc(sizeof(grpc_grpclb_server)); + if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) { + gpr_free(server); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; } - + dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server; return true; } @@ -165,36 +160,38 @@ grpc_grpclb_initial_response *grpc_grpclb_initial_response_parse( grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist( grpc_slice encoded_grpc_grpclb_response) { - bool status; - decode_serverlist_arg arg; pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); pb_istream_t stream_at_start = stream; + grpc_grpclb_serverlist *sl = gpr_zalloc(sizeof(grpc_grpclb_serverlist)); grpc_grpclb_response res; memset(&res, 0, sizeof(grpc_grpclb_response)); - memset(&arg, 0, sizeof(decode_serverlist_arg)); - - res.server_list.servers.funcs.decode = decode_serverlist; - res.server_list.servers.arg = &arg; - arg.first_pass = true; - status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); + // First pass: count number of servers. + res.server_list.servers.funcs.decode = count_serverlist; + res.server_list.servers.arg = sl; + bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); if (!status) { + gpr_free(sl); gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); return NULL; } - - arg.first_pass = false; - status = - pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, &res); - if (!status) { - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); - return NULL; + // Second pass: populate servers. + if (sl->num_servers > 0) { + sl->servers = gpr_zalloc(sizeof(grpc_grpclb_server *) * sl->num_servers); + decode_serverlist_arg decode_arg; + memset(&decode_arg, 0, sizeof(decode_arg)); + decode_arg.serverlist = sl; + res.server_list.servers.funcs.decode = decode_serverlist; + res.server_list.servers.arg = &decode_arg; + status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, + &res); + if (!status) { + grpc_grpclb_destroy_serverlist(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return NULL; + } } - - grpc_grpclb_serverlist *sl = gpr_zalloc(sizeof(grpc_grpclb_serverlist)); - sl->num_servers = arg.num_servers; - sl->servers = arg.servers; if (res.server_list.has_expiration_interval) { sl->expiration_interval = res.server_list.expiration_interval; } @@ -228,7 +225,7 @@ grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy( bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs, const grpc_grpclb_serverlist *rhs) { - if ((lhs == NULL) || (rhs == NULL)) { + if (lhs == NULL || rhs == NULL) { return false; } if (lhs->num_servers != rhs->num_servers) { diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h index 06873821bd..7f596ce1f1 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h @@ -51,7 +51,7 @@ typedef grpc_lb_v1_LoadBalanceRequest grpc_grpclb_request; typedef grpc_lb_v1_InitialLoadBalanceResponse grpc_grpclb_initial_response; typedef grpc_lb_v1_Server grpc_grpclb_server; typedef grpc_lb_v1_Duration grpc_grpclb_duration; -typedef struct grpc_grpclb_serverlist { +typedef struct { grpc_grpclb_server **servers; size_t num_servers; grpc_grpclb_duration expiration_interval; diff --git a/src/core/ext/filters/client_channel/subchannel.c b/src/core/ext/filters/client_channel/subchannel.c index 1af3393a62..dd14bf1d02 100644 --- a/src/core/ext/filters/client_channel/subchannel.c +++ b/src/core/ext/filters/client_channel/subchannel.c @@ -283,6 +283,7 @@ static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { gpr_atm old_refs; + // add a weak ref and subtract a strong ref (atomically) old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS), 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { @@ -656,7 +657,6 @@ static bool publish_transport_locked(grpc_exec_ctx *exec_ctx, gpr_free(sw_subchannel); grpc_channel_stack_destroy(exec_ctx, stk); gpr_free(con); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); return false; } diff --git a/src/core/ext/filters/client_channel/subchannel_index.c b/src/core/ext/filters/client_channel/subchannel_index.c index f6ef4a845e..b25dbfcf51 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.c +++ b/src/core/ext/filters/client_channel/subchannel_index.c @@ -183,8 +183,11 @@ grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, enter_ctx(exec_ctx); grpc_subchannel *c = NULL; + bool need_to_unref_constructed; while (c == NULL) { + need_to_unref_constructed = false; + // Compare and swap loop: // - take a reference to the current index gpr_mu_lock(&g_mu); @@ -194,8 +197,11 @@ grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, // - Check to see if a subchannel already exists c = gpr_avl_get(index, key); if (c != NULL) { + c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "index_register"); + } + if (c != NULL) { // yes -> we're done - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, constructed, "index_register"); + need_to_unref_constructed = true; } else { // no -> update the avl and compare/swap gpr_avl updated = @@ -219,6 +225,10 @@ grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, leave_ctx(exec_ctx); + if (need_to_unref_constructed) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, constructed, "index_register"); + } + return c; } diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c new file mode 100644 index 0000000000..7fb75e3a4f --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c @@ -0,0 +1,223 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" + +#include <string.h> + +#include <grpc/support/alloc.h> + +#include "src/core/ext/filters/workarounds/workaround_utils.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/metadata.h" + +typedef struct call_data { + // Receive closures are chained: we inject this closure as the + // recv_initial_metadata_ready up-call on transport_stream_op, and remember to + // call our next_recv_initial_metadata_ready member after handling it. + grpc_closure recv_initial_metadata_ready; + // Used by recv_initial_metadata_ready. + grpc_metadata_batch* recv_initial_metadata; + // Original recv_initial_metadata_ready callback, invoked after our own. + grpc_closure* next_recv_initial_metadata_ready; + + // Marks whether the workaround is active + bool workaround_active; +} call_data; + +// Find the user agent metadata element in the batch +static bool get_user_agent_mdelem(const grpc_metadata_batch* batch, + grpc_mdelem* md) { + if (batch->idx.named.user_agent != NULL) { + *md = batch->idx.named.user_agent->md; + return true; + } + return false; +} + +// Callback invoked when we receive an initial metadata. +static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, + void* user_data, grpc_error* error) { + grpc_call_element* elem = user_data; + call_data* calld = elem->call_data; + + if (GRPC_ERROR_NONE == error) { + grpc_mdelem md; + if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) { + grpc_workaround_user_agent_md* user_agent_md = grpc_parse_user_agent(md); + if (user_agent_md + ->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) { + calld->workaround_active = true; + } + } + } + + // Invoke the next callback. + grpc_closure_run(exec_ctx, calld->next_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +// Start transport stream op. +static void start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + call_data* calld = elem->call_data; + + // Inject callback for receiving initial metadata + if (op->recv_initial_metadata) { + calld->next_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + } + + if (op->send_message) { + /* Send message happens after client's user-agent (initial metadata) is + * received, so workaround_active must be set already */ + if (calld->workaround_active) { + op->payload->send_message.send_message->flags |= GRPC_WRITE_NO_COMPRESS; + } + } + + // Chain to the next filter. + grpc_call_next_op(exec_ctx, elem, op); +} + +// Constructor for call_data. +static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + const grpc_call_element_args* args) { + call_data* calld = elem->call_data; + calld->next_recv_initial_metadata_ready = NULL; + calld->workaround_active = false; + grpc_closure_init(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. +static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) {} + +// Constructor for channel_data. +static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. +static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem) {} + +// Parse the user agent +static bool parse_user_agent(grpc_mdelem md) { + const char grpc_objc_specifier[] = "grpc-objc/"; + const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1; + const char cronet_specifier[] = "cronet_http"; + const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1; + + char* user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + bool grpc_objc_specifier_seen = false; + bool cronet_specifier_seen = false; + char *major_version_str = user_agent_str, *minor_version_str; + long major_version, minor_version; + + char* head = strtok(user_agent_str, " "); + while (head != NULL) { + if (!grpc_objc_specifier_seen && + 0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) { + major_version_str = head + grpc_objc_specifier_len; + grpc_objc_specifier_seen = true; + } else if (grpc_objc_specifier_seen && + 0 == strncmp(head, cronet_specifier, cronet_specifier_len)) { + cronet_specifier_seen = true; + break; + } + + head = strtok(NULL, " "); + } + if (grpc_objc_specifier_seen) { + major_version_str = strtok(major_version_str, "."); + minor_version_str = strtok(NULL, "."); + major_version = atol(major_version_str); + minor_version = atol(minor_version_str); + } + + gpr_free(user_agent_str); + return (grpc_objc_specifier_seen && cronet_specifier_seen && + (major_version < 1 || (major_version == 1 && minor_version <= 3))); +} + +const grpc_channel_filter grpc_workaround_cronet_compression_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, + init_channel_elem, + destroy_channel_elem, + grpc_call_next_get_peer, + grpc_channel_next_get_info, + "workaround_cronet_compression"}; + +static bool register_workaround_cronet_compression( + grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg* a = grpc_channel_args_find( + channel_args, GRPC_ARG_WORKAROUND_CRONET_COMPRESSION); + if (a == NULL) { + return true; + } + if (grpc_channel_arg_get_bool(a, false) == false) { + return true; + } + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_workaround_cronet_compression_filter, NULL, NULL); +} + +void grpc_workaround_cronet_compression_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_WORKAROUND_PRIORITY_HIGH, + register_workaround_cronet_compression, NULL); + grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION, + parse_user_agent); +} + +void grpc_workaround_cronet_compression_filter_shutdown(void) {} diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h new file mode 100644 index 0000000000..58c79a0c00 --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h @@ -0,0 +1,40 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H +#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_workaround_cronet_compression_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H \ + */ diff --git a/src/core/ext/filters/workarounds/workaround_utils.c b/src/core/ext/filters/workarounds/workaround_utils.c new file mode 100644 index 0000000000..1c565388e1 --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_utils.c @@ -0,0 +1,65 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "src/core/ext/filters/workarounds/workaround_utils.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +user_agent_parser ua_parser[GRPC_MAX_WORKAROUND_ID]; + +static void destroy_user_agent_md(void *user_agent_md) { + gpr_free(user_agent_md); +} + +grpc_workaround_user_agent_md *grpc_parse_user_agent(grpc_mdelem md) { + grpc_workaround_user_agent_md *user_agent_md = + (grpc_workaround_user_agent_md *)grpc_mdelem_get_user_data( + md, destroy_user_agent_md); + + if (NULL != user_agent_md) { + return user_agent_md; + } + user_agent_md = gpr_malloc(sizeof(grpc_workaround_user_agent_md)); + for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) { + if (ua_parser[i]) { + user_agent_md->workaround_active[i] = ua_parser[i](md); + } + } + grpc_mdelem_set_user_data(md, destroy_user_agent_md, (void *)user_agent_md); + + return user_agent_md; +} + +void grpc_register_workaround(uint32_t id, user_agent_parser parser) { + GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID); + ua_parser[id] = parser; +} diff --git a/src/core/ext/filters/workarounds/workaround_utils.h b/src/core/ext/filters/workarounds/workaround_utils.h new file mode 100644 index 0000000000..7cd70c12d8 --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_utils.h @@ -0,0 +1,52 @@ +// +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H +#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H + +#include <grpc/support/workaround_list.h> + +#include "src/core/lib/transport/metadata.h" + +#define GRPC_WORKAROUND_PRIORITY_HIGH 10001 +#define GRPC_WORKAROUND_PROIRITY_LOW 9999 + +typedef struct grpc_workaround_user_agent_md { + bool workaround_active[GRPC_MAX_WORKAROUND_ID]; +} grpc_workaround_user_agent_md; + +grpc_workaround_user_agent_md *grpc_parse_user_agent(grpc_mdelem md); + +typedef bool (*user_agent_parser)(grpc_mdelem); + +void grpc_register_workaround(uint32_t id, user_agent_parser parser); + +#endif diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c index fb8ceaecb0..f3268bcfca 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c @@ -2139,15 +2139,8 @@ static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, static void update_bdp(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, double bdp_dbl) { - int32_t bdp; - const int32_t kMinBDP = 128; - if (bdp_dbl <= kMinBDP) { - bdp = kMinBDP; - } else if (bdp_dbl > INT32_MAX) { - bdp = INT32_MAX; - } else { - bdp = (int32_t)(bdp_dbl); - } + // initial window size bounded [1,2^31-1], but we set the min to 128. + int32_t bdp = GPR_CLAMP((int32_t)bdp_dbl, 128, INT32_MAX); int64_t delta = (int64_t)bdp - (int64_t)t->settings[GRPC_LOCAL_SETTINGS] @@ -2161,7 +2154,26 @@ static void update_bdp(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, } push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, (uint32_t)bdp); - push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, (uint32_t)bdp); +} + +static void update_frame(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + double bw_dbl, double bdp_dbl) { + int32_t bdp = GPR_CLAMP((int32_t)bdp_dbl, 128, INT32_MAX); + int32_t target = GPR_MAX((int32_t)bw_dbl / 1000, bdp); + // frame size is bounded [2^14,2^24-1] + int32_t frame_size = GPR_CLAMP(target, 16384, 16777215); + int64_t delta = (int64_t)frame_size - + (int64_t)t->settings[GRPC_LOCAL_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]; + if (delta == 0 || (delta > -frame_size / 10 && delta < frame_size / 10)) { + return; + } + if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "%s: update max_frame size to %d", t->peer_string, + (int)frame_size); + } + push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, + (uint32_t)frame_size); } static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx, @@ -2280,6 +2292,7 @@ static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp, } int64_t estimate = -1; + double bdp_guess = -1; if (grpc_bdp_estimator_get_estimate(&t->bdp_estimator, &estimate)) { double target = 1 + log2((double)estimate); double memory_pressure = grpc_resource_quota_get_memory_pressure( @@ -2297,9 +2310,15 @@ static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp, } double log2_bdp_guess = grpc_pid_controller_update(&t->pid_controller, bdp_error, dt); - update_bdp(exec_ctx, t, pow(2, log2_bdp_guess)); + bdp_guess = pow(2, log2_bdp_guess); + update_bdp(exec_ctx, t, bdp_guess); t->last_pid_update = now; } + + double bw = -1; + if (grpc_bdp_estimator_get_bw(&t->bdp_estimator, &bw)) { + update_frame(exec_ctx, t, bw, bdp_guess); + } } GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); } else { diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c index 238d176dfa..247b134938 100644 --- a/src/core/lib/channel/channel_args.c +++ b/src/core/lib/channel/channel_args.c @@ -31,6 +31,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <limits.h> #include <string.h> diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c index 5f2c989aad..685581b5cb 100644 --- a/src/core/lib/iomgr/error.c +++ b/src/core/lib/iomgr/error.c @@ -769,7 +769,7 @@ grpc_error *grpc_os_error(const char *file, int line, int err, GRPC_ERROR_INT_ERRNO, err), GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_static_string(strerror(err))), - GRPC_ERROR_STR_SYSCALL, grpc_slice_from_static_string(call_name)); + GRPC_ERROR_STR_SYSCALL, grpc_slice_from_copied_string(call_name)); } #ifdef GPR_WINDOWS diff --git a/src/core/lib/iomgr/ev_epollsig_linux.c b/src/core/lib/iomgr/ev_epollsig_linux.c index 52362a62f4..92c555b7ea 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.c +++ b/src/core/lib/iomgr/ev_epollsig_linux.c @@ -65,9 +65,9 @@ #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) -#define GRPC_POLLING_TRACE(fmt, ...) \ +#define GRPC_POLLING_TRACE(...) \ if (GRPC_TRACER_ON(grpc_polling_trace)) { \ - gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \ + gpr_log(GPR_INFO, __VA_ARGS__); \ } /* Uncomment the following to enable extra checks on poll_object operations */ @@ -732,7 +732,7 @@ static void workqueue_maybe_wakeup(polling_island *pi) { it right now. Note that since we do an anticipatory mpscq_pop every poll loop, it's ok if we miss the wakeup here, as we'll get the work item when the next poller enters anyway. */ - if (current_pollers > min_current_pollers_for_wakeup) { + if (current_pollers >= min_current_pollers_for_wakeup) { GRPC_LOG_IF_ERROR("workqueue_wakeup_fd", grpc_wakeup_fd_wakeup(&pi->workqueue_wakeup_fd)); } @@ -1332,7 +1332,13 @@ static bool maybe_do_workqueue_work(grpc_exec_ctx *exec_ctx, gpr_mpscq_node *n = gpr_mpscq_pop(&pi->workqueue_items); gpr_mu_unlock(&pi->workqueue_read_mu); if (n != NULL) { - if (gpr_atm_full_fetch_add(&pi->workqueue_item_count, -1) > 1) { + gpr_atm remaining = + gpr_atm_full_fetch_add(&pi->workqueue_item_count, -1) - 1; + GRPC_POLLING_TRACE( + "maybe_do_workqueue_work: pi: %p: got closure %p, remaining = " + "%" PRIdPTR, + pi, n, remaining); + if (remaining > 0) { workqueue_maybe_wakeup(pi); } grpc_closure *c = (grpc_closure *)n; @@ -1347,8 +1353,13 @@ static bool maybe_do_workqueue_work(grpc_exec_ctx *exec_ctx, /* n == NULL might mean there's work but it's not available to be popped * yet - try to ensure another workqueue wakes up to check shortly if so */ + GRPC_POLLING_TRACE( + "maybe_do_workqueue_work: pi: %p: more to do, but not yet", pi); workqueue_maybe_wakeup(pi); } + } else { + GRPC_POLLING_TRACE("maybe_do_workqueue_work: pi: %p: read already locked", + pi); } return false; } @@ -1411,7 +1422,10 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, /* If we get some workqueue work to do, it might end up completing an item on the completion queue, so there's no need to poll... so we skip that and redo the complete loop to verify */ + GRPC_POLLING_TRACE("pollset_work: pollset: %p, worker %p, pi %p", pollset, + worker, pi); if (!maybe_do_workqueue_work(exec_ctx, pi)) { + GRPC_POLLING_TRACE("pollset_work: begins"); gpr_atm_no_barrier_fetch_add(&pi->poller_count, 1); g_current_thread_polling_island = pi; @@ -1472,6 +1486,7 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, g_current_thread_polling_island = NULL; gpr_atm_no_barrier_fetch_add(&pi->poller_count, -1); + GRPC_POLLING_TRACE("pollset_work: ends"); } GPR_ASSERT(pi != NULL); diff --git a/src/core/lib/iomgr/ev_windows.c b/src/core/lib/iomgr/ev_windows.c new file mode 100644 index 0000000000..7bf7327823 --- /dev/null +++ b/src/core/lib/iomgr/ev_windows.c @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/debug/trace.h" + +grpc_tracer_flag grpc_polling_trace = + GRPC_TRACER_INITIALIZER(false); /* Disabled by default */ + +#endif // GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/tcp_server_uv.c b/src/core/lib/iomgr/tcp_server_uv.c index e9246948f5..d446e5312a 100644 --- a/src/core/lib/iomgr/tcp_server_uv.c +++ b/src/core/lib/iomgr/tcp_server_uv.c @@ -56,6 +56,8 @@ struct grpc_tcp_listener { int port; /* linked list */ struct grpc_tcp_listener *next; + + bool closed; }; struct grpc_tcp_server { @@ -77,6 +79,8 @@ struct grpc_tcp_server { /* shutdown callback */ grpc_closure *shutdown_complete; + bool shutdown; + grpc_resource_quota *resource_quota; }; @@ -109,6 +113,7 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, s->shutdown_starting.head = NULL; s->shutdown_starting.tail = NULL; s->shutdown_complete = shutdown_complete; + s->shutdown = false; *server = s; return GRPC_ERROR_NONE; } @@ -125,6 +130,7 @@ void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, } static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + GPR_ASSERT(s->shutdown); if (s->shutdown_complete != NULL) { grpc_closure_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); } @@ -144,21 +150,31 @@ static void handle_close_callback(uv_handle_t *handle) { grpc_tcp_listener *sp = (grpc_tcp_listener *)handle->data; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; sp->server->open_ports--; - if (sp->server->open_ports == 0) { + if (sp->server->open_ports == 0 && sp->server->shutdown) { finish_shutdown(&exec_ctx, sp->server); } grpc_exec_ctx_finish(&exec_ctx); } +static void close_listener(grpc_tcp_listener *sp) { + if (!sp->closed) { + sp->closed = true; + uv_close((uv_handle_t *)sp->handle, handle_close_callback); + } +} + static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { int immediately_done = 0; grpc_tcp_listener *sp; + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + if (s->open_ports == 0) { immediately_done = 1; } for (sp = s->head; sp; sp = sp->next) { - uv_close((uv_handle_t *)sp->handle, handle_close_callback); + close_listener(sp); } if (immediately_done) { @@ -196,9 +212,14 @@ static void on_connect(uv_stream_t *server, int status) { int err; if (status < 0) { - gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", - uv_strerror(status)); - return; + switch (status) { + case UV_EINTR: + case UV_EAGAIN: + return; + default: + close_listener(sp); + return; + } } client = gpr_malloc(sizeof(uv_tcp_t)); @@ -287,6 +308,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, uv_tcp_t *handle, sp->handle = handle; sp->port = port; sp->port_index = port_index; + sp->closed = false; handle->data = sp; s->open_ports++; GPR_ASSERT(sp->handle); diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c index e7157537f6..dc23e4f521 100644 --- a/src/core/lib/iomgr/tcp_uv.c +++ b/src/core/lib/iomgr/tcp_uv.c @@ -88,12 +88,12 @@ static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { #ifdef GRPC_TCP_REFCOUNT_DEBUG #define TCP_UNREF(exec_ctx, tcp, reason) \ tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) \ - tcp_ref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, const char *reason, const char *file, int line) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, - reason, tcp->refcount.count, tcp->refcount.count - 1); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIiPTR " -> %" PRIiPTR, tcp, reason, + tcp->refcount.count, tcp->refcount.count - 1); if (gpr_unref(&tcp->refcount)) { tcp_free(exec_ctx, tcp); } @@ -101,8 +101,9 @@ static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, int line) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, - reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIiPTR " -> %" PRIiPTR, tcp, reason, + tcp->refcount.count, tcp->refcount.count + 1); gpr_ref(&tcp->refcount); } #else @@ -311,6 +312,7 @@ static void uv_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, tcp->shutting_down = true; uv_shutdown_t *req = &tcp->shutdown_req; uv_shutdown(req, (uv_stream_t *)tcp->handle, shutdown_callback); + grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); } GRPC_ERROR_UNREF(why); } diff --git a/src/core/lib/support/cmdline.c b/src/core/lib/support/cmdline.c index 88a65a8e2e..e5c9f3b84b 100644 --- a/src/core/lib/support/cmdline.c +++ b/src/core/lib/support/cmdline.c @@ -71,7 +71,7 @@ struct gpr_cmdline { static int normal_state(gpr_cmdline *cl, char *arg); gpr_cmdline *gpr_cmdline_create(const char *description) { - gpr_cmdline *cl = gpr_zalloc(sizeof(gpr_cmdline)); + gpr_cmdline *cl = (gpr_cmdline *)gpr_zalloc(sizeof(gpr_cmdline)); cl->description = description; cl->state = normal_state; @@ -100,7 +100,7 @@ static void add_arg(gpr_cmdline *cl, const char *name, const char *help, GPR_ASSERT(0 != strcmp(a->name, name)); } - a = gpr_zalloc(sizeof(arg)); + a = (arg *)gpr_zalloc(sizeof(arg)); a->name = name; a->help = help; a->type = type; @@ -302,7 +302,7 @@ static int normal_state(gpr_cmdline *cl, char *str) { eq = strchr(str, '='); if (eq != NULL) { /* copy the string into a temp buffer and extract the name */ - tmp = arg_name = gpr_malloc((size_t)(eq - str + 1)); + tmp = arg_name = (char *)gpr_malloc((size_t)(eq - str + 1)); memcpy(arg_name, str, (size_t)(eq - str)); arg_name[eq - str] = 0; } else { diff --git a/src/core/lib/support/histogram.c b/src/core/lib/support/histogram.c index ba8176bb05..c88695409d 100644 --- a/src/core/lib/support/histogram.c +++ b/src/core/lib/support/histogram.c @@ -88,7 +88,7 @@ static double bucket_start(gpr_histogram *h, double x) { gpr_histogram *gpr_histogram_create(double resolution, double max_bucket_start) { - gpr_histogram *h = gpr_malloc(sizeof(gpr_histogram)); + gpr_histogram *h = (gpr_histogram *)gpr_malloc(sizeof(gpr_histogram)); GPR_ASSERT(resolution > 0.0); GPR_ASSERT(max_bucket_start > resolution); h->sum = 0.0; @@ -102,7 +102,7 @@ gpr_histogram *gpr_histogram_create(double resolution, h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1; GPR_ASSERT(h->num_buckets > 1); GPR_ASSERT(h->num_buckets < 100000000); - h->buckets = gpr_zalloc(sizeof(uint32_t) * h->num_buckets); + h->buckets = (uint32_t *)gpr_zalloc(sizeof(uint32_t) * h->num_buckets); return h; } diff --git a/src/core/lib/support/host_port.c b/src/core/lib/support/host_port.c index f19bdbc835..bbd42c26e0 100644 --- a/src/core/lib/support/host_port.c +++ b/src/core/lib/support/host_port.c @@ -98,7 +98,7 @@ int gpr_split_host_port(const char *name, char **host, char **port) { } /* Allocate return values. */ - *host = gpr_malloc(host_len + 1); + *host = (char *)gpr_malloc(host_len + 1); memcpy(*host, host_start, host_len); (*host)[host_len] = '\0'; diff --git a/src/core/lib/support/mpscq.c b/src/core/lib/support/mpscq.c index 1015cc6776..822abd075d 100644 --- a/src/core/lib/support/mpscq.c +++ b/src/core/lib/support/mpscq.c @@ -46,11 +46,12 @@ void gpr_mpscq_destroy(gpr_mpscq *q) { GPR_ASSERT(q->tail == &q->stub); } -void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n) { +bool gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n) { gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL); gpr_mpscq_node *prev = (gpr_mpscq_node *)gpr_atm_full_xchg(&q->head, (gpr_atm)n); gpr_atm_rel_store(&prev->next, (gpr_atm)n); + return prev == &q->stub; } gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q) { @@ -92,3 +93,25 @@ gpr_mpscq_node *gpr_mpscq_pop_and_check_end(gpr_mpscq *q, bool *empty) { *empty = false; return NULL; } + +void gpr_locked_mpscq_init(gpr_locked_mpscq *q) { + gpr_mpscq_init(&q->queue); + q->read_lock = GPR_SPINLOCK_INITIALIZER; +} + +void gpr_locked_mpscq_destroy(gpr_locked_mpscq *q) { + gpr_mpscq_destroy(&q->queue); +} + +bool gpr_locked_mpscq_push(gpr_locked_mpscq *q, gpr_mpscq_node *n) { + return gpr_mpscq_push(&q->queue, n); +} + +gpr_mpscq_node *gpr_locked_mpscq_pop(gpr_locked_mpscq *q) { + if (gpr_spinlock_trylock(&q->read_lock)) { + gpr_mpscq_node *n = gpr_mpscq_pop(&q->queue); + gpr_spinlock_unlock(&q->read_lock); + return n; + } + return NULL; +} diff --git a/src/core/lib/support/mpscq.h b/src/core/lib/support/mpscq.h index 24c89f90c9..b3a171678a 100644 --- a/src/core/lib/support/mpscq.h +++ b/src/core/lib/support/mpscq.h @@ -37,6 +37,7 @@ #include <grpc/support/atm.h> #include <stdbool.h> #include <stddef.h> +#include "src/core/lib/support/spinlock.h" // Multiple-producer single-consumer lock free queue, based upon the // implementation from Dmitry Vyukov here: @@ -58,12 +59,34 @@ typedef struct gpr_mpscq { void gpr_mpscq_init(gpr_mpscq *q); void gpr_mpscq_destroy(gpr_mpscq *q); // Push a node -void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n); +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n); // Pop a node (returns NULL if no node is ready - which doesn't indicate that // the queue is empty!!) +// Thread compatible - can only be called from one thread at a time gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q); - // Pop a node; sets *empty to true if the queue is empty, or false if it is not gpr_mpscq_node *gpr_mpscq_pop_and_check_end(gpr_mpscq *q, bool *empty); +// An mpscq with a spinlock: it's safe to pop from multiple threads, but doing +// only one thread will succeed concurrently +typedef struct gpr_locked_mpscq { + gpr_mpscq queue; + gpr_spinlock read_lock; +} gpr_locked_mpscq; + +void gpr_locked_mpscq_init(gpr_locked_mpscq *q); +void gpr_locked_mpscq_destroy(gpr_locked_mpscq *q); +// Push a node +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_locked_mpscq_push(gpr_locked_mpscq *q, gpr_mpscq_node *n); +// Pop a node (returns NULL if no node is ready - which doesn't indicate that +// the queue is empty!!) +// Thread safe - can be called from multiple threads concurrently +gpr_mpscq_node *gpr_locked_mpscq_pop(gpr_locked_mpscq *q); + #endif /* GRPC_CORE_LIB_SUPPORT_MPSCQ_H */ diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c index d20b86f7cf..11297c9ddb 100644 --- a/src/core/lib/support/string.c +++ b/src/core/lib/support/string.c @@ -53,7 +53,7 @@ char *gpr_strdup(const char *src) { } len = strlen(src) + 1; - dst = gpr_malloc(len); + dst = (char *)gpr_malloc(len); memcpy(dst, src, len); @@ -74,13 +74,13 @@ static dump_out dump_out_create(void) { static void dump_out_append(dump_out *out, char c) { if (out->length == out->capacity) { out->capacity = GPR_MAX(8, 2 * out->capacity); - out->data = gpr_realloc(out->data, out->capacity); + out->data = (char *)gpr_realloc(out->data, out->capacity); } out->data[out->length++] = c; } static void hexdump(dump_out *out, const char *buf, size_t len) { - static const char hex[16] = "0123456789abcdef"; + static const char *hex = "0123456789abcdef"; const uint8_t *const beg = (const uint8_t *)buf; const uint8_t *const end = beg + len; @@ -124,16 +124,16 @@ char *gpr_dump(const char *buf, size_t len, uint32_t flags) { int gpr_parse_bytes_to_uint32(const char *buf, size_t len, uint32_t *result) { uint32_t out = 0; - uint32_t new; + uint32_t new_val; size_t i; if (len == 0) return 0; /* must have some bytes */ for (i = 0; i < len; i++) { if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */ - new = 10 * out + (uint32_t)(buf[i] - '0'); - if (new < out) return 0; /* overflow */ - out = new; + new_val = 10 * out + (uint32_t)(buf[i] - '0'); + if (new_val < out) return 0; /* overflow */ + out = new_val; } *result = out; @@ -201,7 +201,7 @@ int gpr_parse_nonnegative_int(const char *value) { char *gpr_leftpad(const char *str, char flag, size_t length) { const size_t str_length = strlen(str); const size_t out_length = str_length > length ? str_length : length; - char *out = gpr_malloc(out_length + 1); + char *out = (char *)gpr_malloc(out_length + 1); memset(out, flag, out_length - str_length); memcpy(out + out_length - str_length, str, str_length); out[out_length] = 0; @@ -225,7 +225,7 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, if (nstrs > 0) { out_length += sep_len * (nstrs - 1); /* separators */ } - out = gpr_malloc(out_length); + out = (char *)gpr_malloc(out_length); out_length = 0; for (i = 0; i < nstrs; i++) { const size_t slen = strlen(strs[i]); @@ -256,7 +256,7 @@ void gpr_strvec_destroy(gpr_strvec *sv) { void gpr_strvec_add(gpr_strvec *sv, char *str) { if (sv->count == sv->capacity) { sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); - sv->strs = gpr_realloc(sv->strs, sizeof(char *) * sv->capacity); + sv->strs = (char **)gpr_realloc(sv->strs, sizeof(char *) * sv->capacity); } sv->strs[sv->count++] = str; } @@ -278,12 +278,12 @@ int gpr_stricmp(const char *a, const char *b) { static void add_string_to_split(const char *beg, const char *end, char ***strs, size_t *nstrs, size_t *capstrs) { - char *out = gpr_malloc((size_t)(end - beg) + 1); + char *out = (char *)gpr_malloc((size_t)(end - beg) + 1); memcpy(out, beg, (size_t)(end - beg)); out[end - beg] = 0; if (*nstrs == *capstrs) { *capstrs = GPR_MAX(8, 2 * *capstrs); - *strs = gpr_realloc(*strs, sizeof(*strs) * *capstrs); + *strs = (char **)gpr_realloc(*strs, sizeof(*strs) * *capstrs); } (*strs)[*nstrs] = out; ++*nstrs; diff --git a/src/core/lib/support/string_posix.c b/src/core/lib/support/string_posix.c index c804ed5ded..2438b18d21 100644 --- a/src/core/lib/support/string_posix.c +++ b/src/core/lib/support/string_posix.c @@ -58,7 +58,7 @@ int gpr_asprintf(char **strp, const char *format, ...) { /* Allocate a new buffer, with space for the NUL terminator. */ strp_buflen = (size_t)ret + 1; - if ((*strp = gpr_malloc(strp_buflen)) == NULL) { + if ((*strp = (char *)gpr_malloc(strp_buflen)) == NULL) { /* This shouldn't happen, because gpr_malloc() calls abort(). */ return -1; } diff --git a/src/core/lib/support/subprocess_posix.c b/src/core/lib/support/subprocess_posix.c index ed653b9c2e..b9d0796b01 100644 --- a/src/core/lib/support/subprocess_posix.c +++ b/src/core/lib/support/subprocess_posix.c @@ -67,7 +67,7 @@ gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { if (pid == -1) { return NULL; } else if (pid == 0) { - exec_args = gpr_malloc(((size_t)argc + 1) * sizeof(char *)); + exec_args = (char **)gpr_malloc(((size_t)argc + 1) * sizeof(char *)); memcpy(exec_args, argv, (size_t)argc * sizeof(char *)); exec_args[argc] = NULL; execv(exec_args[0], exec_args); @@ -76,7 +76,7 @@ gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { _exit(1); return NULL; } else { - r = gpr_zalloc(sizeof(gpr_subprocess)); + r = (gpr_subprocess *)gpr_zalloc(sizeof(gpr_subprocess)); r->pid = pid; return r; } diff --git a/src/core/lib/support/thd_posix.c b/src/core/lib/support/thd_posix.c index 2fc23bffaf..16e645ad91 100644 --- a/src/core/lib/support/thd_posix.c +++ b/src/core/lib/support/thd_posix.c @@ -65,7 +65,7 @@ int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, pthread_t p; /* don't use gpr_malloc as we may cause an infinite recursion with * the profiling code */ - struct thd_arg *a = malloc(sizeof(*a)); + struct thd_arg *a = (struct thd_arg *)malloc(sizeof(*a)); GPR_ASSERT(a != NULL); a->body = thd_body; a->arg = arg; diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c index df5b70205c..de905941c1 100644 --- a/src/core/lib/surface/completion_queue.c +++ b/src/core/lib/surface/completion_queue.c @@ -30,7 +30,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ - #include "src/core/lib/surface/completion_queue.h" #include <stdio.h> @@ -45,6 +44,7 @@ #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/spinlock.h" #include "src/core/lib/support/string.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/call.h" @@ -201,33 +201,68 @@ static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { .destroy = non_polling_poller_destroy}, }; -/* Completion queue structure */ -struct grpc_completion_queue { - /** owned by pollset */ +typedef struct cq_vtable { + grpc_cq_completion_type cq_completion_type; + size_t (*size)(); + void (*begin_op)(grpc_completion_queue *cc, void *tag); + void (*end_op)(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + grpc_event (*next)(grpc_completion_queue *cc, gpr_timespec deadline, + void *reserved); + grpc_event (*pluck)(grpc_completion_queue *cc, void *tag, + gpr_timespec deadline, void *reserved); +} cq_vtable; + +/* Queue that holds the cq_completion_events. Internally uses gpr_mpscq queue + * (a lockfree multiproducer single consumer queue). It uses a queue_lock + * to support multiple consumers. + * Only used in completion queues whose completion_type is GRPC_CQ_NEXT */ +typedef struct grpc_cq_event_queue { + /* Spinlock to serialize consumers i.e pop() operations */ + gpr_spinlock queue_lock; + + gpr_mpscq queue; + + /* A lazy counter of number of items in the queue. This is NOT atomically + incremented/decremented along with push/pop operations and hence is only + eventually consistent */ + gpr_atm num_queue_items; +} grpc_cq_event_queue; + +/* TODO: sreek Refactor this based on the completion_type. Put completion-type + * specific data in a different structure (and co-allocate memory for it along + * with completion queue + pollset )*/ +typedef struct cq_data { gpr_mu *mu; - grpc_cq_completion_type completion_type; - - const cq_poller_vtable *poller_vtable; - - /** completed events */ + /** Completed events for completion-queues of type GRPC_CQ_PLUCK */ grpc_cq_completion completed_head; grpc_cq_completion *completed_tail; + + /** Completed events for completion-queues of type GRPC_CQ_NEXT */ + grpc_cq_event_queue queue; + /** Number of pending events (+1 if we're not shutdown) */ gpr_refcount pending_events; + /** Once owning_refs drops to zero, we will destroy the cq */ gpr_refcount owning_refs; - /** counter of how many things have ever been queued on this completion queue + + /** Counter of how many things have ever been queued on this completion queue useful for avoiding locks to check the queue */ gpr_atm things_queued_ever; + /** 0 initially, 1 once we've begun shutting down */ - int shutdown; + gpr_atm shutdown; int shutdown_called; + int is_server_cq; - /** Can the server cq accept incoming channels */ - /* TODO: sreek - This will no longer be needed. Use polling_type set */ - int is_non_listening_server_cq; + int num_pluckers; + int num_polls; plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; grpc_closure pollset_shutdown_done; @@ -236,8 +271,61 @@ struct grpc_completion_queue { size_t outstanding_tag_count; size_t outstanding_tag_capacity; #endif +} cq_data; + +/* Completion queue structure */ +struct grpc_completion_queue { + cq_data data; + const cq_vtable *vtable; + const cq_poller_vtable *poller_vtable; +}; - grpc_completion_queue *next_free; +/* Forward declarations */ +static void cq_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc); + +static size_t cq_size(grpc_completion_queue *cc); + +static void cq_begin_op(grpc_completion_queue *cc, void *tag); + +static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + +static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + +static grpc_event cq_next(grpc_completion_queue *cc, gpr_timespec deadline, + void *reserved); + +static grpc_event cq_pluck(grpc_completion_queue *cc, void *tag, + gpr_timespec deadline, void *reserved); + +/* Completion queue vtables based on the completion-type */ +static const cq_vtable g_cq_vtable[] = { + /* GRPC_CQ_NEXT */ + {.cq_completion_type = GRPC_CQ_NEXT, + .size = cq_size, + .begin_op = cq_begin_op, + .end_op = cq_end_op_for_next, + .next = cq_next, + .pluck = NULL}, + /* GRPC_CQ_PLUCK */ + {.cq_completion_type = GRPC_CQ_PLUCK, + .size = cq_size, + .begin_op = cq_begin_op, + .end_op = cq_end_op_for_pluck, + .next = NULL, + .pluck = cq_pluck}, }; #define POLLSET_FROM_CQ(cq) ((grpc_pollset *)(cq + 1)) @@ -258,6 +346,47 @@ grpc_tracer_flag grpc_cq_event_timeout_trace = GRPC_TRACER_INITIALIZER(true); static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cc, grpc_error *error); +static void cq_event_queue_init(grpc_cq_event_queue *q) { + gpr_mpscq_init(&q->queue); + q->queue_lock = GPR_SPINLOCK_INITIALIZER; + gpr_atm_no_barrier_store(&q->num_queue_items, 0); +} + +static void cq_event_queue_destroy(grpc_cq_event_queue *q) { + gpr_mpscq_destroy(&q->queue); +} + +static void cq_event_queue_push(grpc_cq_event_queue *q, grpc_cq_completion *c) { + gpr_mpscq_push(&q->queue, (gpr_mpscq_node *)c); + gpr_atm_no_barrier_fetch_add(&q->num_queue_items, 1); +} + +static grpc_cq_completion *cq_event_queue_pop(grpc_cq_event_queue *q) { + grpc_cq_completion *c = NULL; + if (gpr_spinlock_trylock(&q->queue_lock)) { + c = (grpc_cq_completion *)gpr_mpscq_pop(&q->queue); + gpr_spinlock_unlock(&q->queue_lock); + } + + if (c) { + gpr_atm_no_barrier_fetch_add(&q->num_queue_items, -1); + } + + return c; +} + +/* Note: The counter is not incremented/decremented atomically with push/pop. + * The count is only eventually consistent */ +static long cq_event_queue_num_items(grpc_cq_event_queue *q) { + return (long)gpr_atm_no_barrier_load(&q->num_queue_items); +} + +static size_t cq_size(grpc_completion_queue *cc) { + /* Size of the completion queue and the size of the pollset whose memory is + allocated right after that of completion queue */ + return sizeof(grpc_completion_queue) + cc->poller_vtable->size(); +} + grpc_completion_queue *grpc_completion_queue_create_internal( grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type) { @@ -270,35 +399,40 @@ grpc_completion_queue *grpc_completion_queue_create_internal( "polling_type=%d)", 2, (completion_type, polling_type)); + const cq_vtable *vtable = &g_cq_vtable[completion_type]; const cq_poller_vtable *poller_vtable = &g_poller_vtable_by_poller_type[polling_type]; cc = gpr_zalloc(sizeof(grpc_completion_queue) + poller_vtable->size()); - poller_vtable->init(POLLSET_FROM_CQ(cc), &cc->mu); -#ifndef NDEBUG - cc->outstanding_tags = NULL; - cc->outstanding_tag_capacity = 0; -#endif + cq_data *cqd = &cc->data; - cc->completion_type = completion_type; + cc->vtable = vtable; cc->poller_vtable = poller_vtable; + poller_vtable->init(POLLSET_FROM_CQ(cc), &cc->data.mu); + +#ifndef NDEBUG + cqd->outstanding_tags = NULL; + cqd->outstanding_tag_capacity = 0; +#endif + /* Initial ref is dropped by grpc_completion_queue_shutdown */ - gpr_ref_init(&cc->pending_events, 1); + gpr_ref_init(&cqd->pending_events, 1); /* One for destroy(), one for pollset_shutdown */ - gpr_ref_init(&cc->owning_refs, 2); - cc->completed_tail = &cc->completed_head; - cc->completed_head.next = (uintptr_t)cc->completed_tail; - cc->shutdown = 0; - cc->shutdown_called = 0; - cc->is_server_cq = 0; - cc->is_non_listening_server_cq = 0; - cc->num_pluckers = 0; - gpr_atm_no_barrier_store(&cc->things_queued_ever, 0); + gpr_ref_init(&cqd->owning_refs, 2); + cqd->completed_tail = &cqd->completed_head; + cqd->completed_head.next = (uintptr_t)cqd->completed_tail; + gpr_atm_no_barrier_store(&cqd->shutdown, 0); + cqd->shutdown_called = 0; + cqd->is_server_cq = 0; + cqd->num_pluckers = 0; + cqd->num_polls = 0; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); #ifndef NDEBUG - cc->outstanding_tag_count = 0; + cqd->outstanding_tag_count = 0; #endif - grpc_closure_init(&cc->pollset_shutdown_done, on_pollset_shutdown_done, cc, + cq_event_queue_init(&cqd->queue); + grpc_closure_init(&cqd->pollset_shutdown_done, on_pollset_shutdown_done, cc, grpc_schedule_on_exec_ctx); GPR_TIMER_END("grpc_completion_queue_create_internal", 0); @@ -307,18 +441,28 @@ grpc_completion_queue *grpc_completion_queue_create_internal( } grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue *cc) { - return cc->completion_type; + return cc->vtable->cq_completion_type; +} + +int grpc_get_cq_poll_num(grpc_completion_queue *cc) { + int cur_num_polls; + gpr_mu_lock(cc->data.mu); + cur_num_polls = cc->data.num_polls; + gpr_mu_unlock(cc->data.mu); + return cur_num_polls; } #ifdef GRPC_CQ_REF_COUNT_DEBUG void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, const char *file, int line) { + cq_data *cqd = &cc->data; gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", cc, - (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, reason); + (int)cqd->owning_refs.count, (int)cqd->owning_refs.count + 1, reason); #else void grpc_cq_internal_ref(grpc_completion_queue *cc) { + cq_data *cqd = &cc->data; #endif - gpr_ref(&cc->owning_refs); + gpr_ref(&cqd->owning_refs); } static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg, @@ -328,63 +472,95 @@ static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg, } #ifdef GRPC_CQ_REF_COUNT_DEBUG -void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, - const char *reason, const char *file, int line) { +void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason, + const char *file, int line) { + cq_data *cqd = &cc->data; gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", cc, - (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, reason); + (int)cqd->owning_refs.count, (int)cqd->owning_refs.count - 1, reason); #else void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc) { + cq_data *cqd = &cc->data; #endif - if (gpr_unref(&cc->owning_refs)) { - GPR_ASSERT(cc->completed_head.next == (uintptr_t)&cc->completed_head); + if (gpr_unref(&cqd->owning_refs)) { + GPR_ASSERT(cqd->completed_head.next == (uintptr_t)&cqd->completed_head); cc->poller_vtable->destroy(exec_ctx, POLLSET_FROM_CQ(cc)); + cq_event_queue_destroy(&cqd->queue); #ifndef NDEBUG - gpr_free(cc->outstanding_tags); + gpr_free(cqd->outstanding_tags); #endif gpr_free(cc); } } -void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag) { +static void cq_begin_op(grpc_completion_queue *cc, void *tag) { + cq_data *cqd = &cc->data; #ifndef NDEBUG - gpr_mu_lock(cc->mu); - GPR_ASSERT(!cc->shutdown_called); - if (cc->outstanding_tag_count == cc->outstanding_tag_capacity) { - cc->outstanding_tag_capacity = GPR_MAX(4, 2 * cc->outstanding_tag_capacity); - cc->outstanding_tags = - gpr_realloc(cc->outstanding_tags, sizeof(*cc->outstanding_tags) * - cc->outstanding_tag_capacity); + gpr_mu_lock(cqd->mu); + GPR_ASSERT(!cqd->shutdown_called); + if (cqd->outstanding_tag_count == cqd->outstanding_tag_capacity) { + cqd->outstanding_tag_capacity = + GPR_MAX(4, 2 * cqd->outstanding_tag_capacity); + cqd->outstanding_tags = + gpr_realloc(cqd->outstanding_tags, sizeof(*cqd->outstanding_tags) * + cqd->outstanding_tag_capacity); } - cc->outstanding_tags[cc->outstanding_tag_count++] = tag; - gpr_mu_unlock(cc->mu); + cqd->outstanding_tags[cqd->outstanding_tag_count++] = tag; + gpr_mu_unlock(cqd->mu); #endif - gpr_ref(&cc->pending_events); + gpr_ref(&cqd->pending_events); +} + +void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag) { + cc->vtable->begin_op(cc, tag); } -/* Signal the end of an operation - if this is the last waiting-to-be-queued - event, then enter shutdown mode */ -/* Queue a GRPC_OP_COMPLETED operation */ -void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, - void *tag, grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage) { - int shutdown; - int i; - grpc_pollset_worker *pluck_worker; #ifndef NDEBUG +static void cq_check_tag(grpc_completion_queue *cc, void *tag, bool lock_cq) { + cq_data *cqd = &cc->data; int found = 0; + if (lock_cq) { + gpr_mu_lock(cqd->mu); + } + + for (int i = 0; i < (int)cqd->outstanding_tag_count; i++) { + if (cqd->outstanding_tags[i] == tag) { + cqd->outstanding_tag_count--; + GPR_SWAP(void *, cqd->outstanding_tags[i], + cqd->outstanding_tags[cqd->outstanding_tag_count]); + found = 1; + break; + } + } + + if (lock_cq) { + gpr_mu_unlock(cqd->mu); + } + + GPR_ASSERT(found); +} +#else +static void cq_check_tag(grpc_completion_queue *cc, void *tag, bool lock_cq) {} #endif - GPR_TIMER_BEGIN("grpc_cq_end_op", 0); +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a completion + * type of GRPC_CQ_NEXT) */ +static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + GPR_TIMER_BEGIN("cq_end_op_for_next", 0); + if (GRPC_TRACER_ON(grpc_api_trace) || (GRPC_TRACER_ON(grpc_trace_operation_failures) && error != GRPC_ERROR_NONE)) { const char *errmsg = grpc_error_string(error); GRPC_API_TRACE( - "grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, error=%s, done=%p, " - "done_arg=%p, storage=%p)", + "cq_end_op_for_next(exec_ctx=%p, cc=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", 7, (exec_ctx, cc, tag, errmsg, done, done_arg, storage)); if (GRPC_TRACER_ON(grpc_trace_operation_failures) && error != GRPC_ERROR_NONE) { @@ -392,41 +568,100 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, } } + cq_data *cqd = &cc->data; + int is_success = (error == GRPC_ERROR_NONE); + storage->tag = tag; storage->done = done; storage->done_arg = done_arg; - storage->next = ((uintptr_t)&cc->completed_head) | - ((uintptr_t)(error == GRPC_ERROR_NONE)); + storage->next = (uintptr_t)(is_success); - gpr_mu_lock(cc->mu); -#ifndef NDEBUG - for (i = 0; i < (int)cc->outstanding_tag_count; i++) { - if (cc->outstanding_tags[i] == tag) { - cc->outstanding_tag_count--; - GPR_SWAP(void *, cc->outstanding_tags[i], - cc->outstanding_tags[cc->outstanding_tag_count]); - found = 1; - break; + cq_check_tag(cc, tag, true); /* Used in debug builds only */ + + /* Add the completion to the queue */ + cq_event_queue_push(&cqd->queue, storage); + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + + gpr_mu_lock(cqd->mu); + + int shutdown = gpr_unref(&cqd->pending_events); + if (!shutdown) { + grpc_error *kick_error = cc->poller_vtable->kick(POLLSET_FROM_CQ(cc), NULL); + gpr_mu_unlock(cqd->mu); + + if (kick_error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(kick_error); + gpr_log(GPR_ERROR, "Kick failed: %s", msg); + + GRPC_ERROR_UNREF(kick_error); } + } else { + cq_finish_shutdown(exec_ctx, cc); + gpr_mu_unlock(cqd->mu); } - GPR_ASSERT(found); -#endif - shutdown = gpr_unref(&cc->pending_events); - gpr_atm_no_barrier_fetch_add(&cc->things_queued_ever, 1); + + GPR_TIMER_END("cq_end_op_for_next", 0); + + GRPC_ERROR_UNREF(error); +} + +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a completion + * type of GRPC_CQ_PLUCK) */ +static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + cq_data *cqd = &cc->data; + int is_success = (error == GRPC_ERROR_NONE); + + GPR_TIMER_BEGIN("cq_end_op_for_pluck", 0); + + if (GRPC_TRACER_ON(grpc_api_trace) || + (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE)) { + const char *errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_pluck(exec_ctx=%p, cc=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 7, (exec_ctx, cc, tag, errmsg, done, done_arg, storage)); + if (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = ((uintptr_t)&cqd->completed_head) | ((uintptr_t)(is_success)); + + gpr_mu_lock(cqd->mu); + cq_check_tag(cc, tag, false); /* Used in debug builds only */ + + /* Add to the list of completions */ + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + cqd->completed_tail->next = + ((uintptr_t)storage) | (1u & (uintptr_t)cqd->completed_tail->next); + cqd->completed_tail = storage; + + int shutdown = gpr_unref(&cqd->pending_events); if (!shutdown) { - cc->completed_tail->next = - ((uintptr_t)storage) | (1u & (uintptr_t)cc->completed_tail->next); - cc->completed_tail = storage; - pluck_worker = NULL; - for (i = 0; i < cc->num_pluckers; i++) { - if (cc->pluckers[i].tag == tag) { - pluck_worker = *cc->pluckers[i].worker; + grpc_pollset_worker *pluck_worker = NULL; + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag) { + pluck_worker = *cqd->pluckers[i].worker; break; } } + grpc_error *kick_error = cc->poller_vtable->kick(POLLSET_FROM_CQ(cc), pluck_worker); - gpr_mu_unlock(cc->mu); + + gpr_mu_unlock(cqd->mu); + if (kick_error != GRPC_ERROR_NONE) { const char *msg = grpc_error_string(kick_error); gpr_log(GPR_ERROR, "Kick failed: %s", msg); @@ -434,22 +669,23 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, GRPC_ERROR_UNREF(kick_error); } } else { - cc->completed_tail->next = - ((uintptr_t)storage) | (1u & (uintptr_t)cc->completed_tail->next); - cc->completed_tail = storage; - GPR_ASSERT(!cc->shutdown); - GPR_ASSERT(cc->shutdown_called); - cc->shutdown = 1; - cc->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cc), - &cc->pollset_shutdown_done); - gpr_mu_unlock(cc->mu); + cq_finish_shutdown(exec_ctx, cc); + gpr_mu_unlock(cqd->mu); } - GPR_TIMER_END("grpc_cq_end_op", 0); + GPR_TIMER_END("cq_end_op_for_pluck", 0); GRPC_ERROR_UNREF(error); } +void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, + void *tag, grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + cc->vtable->end_op(exec_ctx, cc, tag, error, done, done_arg, storage); +} + typedef struct { gpr_atm last_seen_things_queued_ever; grpc_completion_queue *cq; @@ -462,23 +698,24 @@ typedef struct { static bool cq_is_next_finished(grpc_exec_ctx *exec_ctx, void *arg) { cq_is_finished_arg *a = arg; grpc_completion_queue *cq = a->cq; + cq_data *cqd = &cq->data; GPR_ASSERT(a->stolen_completion == NULL); + gpr_atm current_last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cq->things_queued_ever); + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { - gpr_mu_lock(cq->mu); a->last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cq->things_queued_ever); - if (cq->completed_tail != &cq->completed_head) { - a->stolen_completion = (grpc_cq_completion *)cq->completed_head.next; - cq->completed_head.next = a->stolen_completion->next & ~(uintptr_t)1; - if (a->stolen_completion == cq->completed_tail) { - cq->completed_tail = &cq->completed_head; - } - gpr_mu_unlock(cq->mu); + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + + /* Pop a cq_completion from the queue. Returns NULL if the queue is empty + * might return NULL in some cases even if the queue is not empty; but that + * is ok and doesn't affect correctness. Might effect the tail latencies a + * bit) */ + a->stolen_completion = cq_event_queue_pop(&cqd->queue); + if (a->stolen_completion != NULL) { return true; } - gpr_mu_unlock(cq->mu); } return !a->first_loop && gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; @@ -488,16 +725,18 @@ static bool cq_is_next_finished(grpc_exec_ctx *exec_ctx, void *arg) { static void dump_pending_tags(grpc_completion_queue *cc) { if (!GRPC_TRACER_ON(grpc_trace_pending_tags)) return; + cq_data *cqd = &cc->data; + gpr_strvec v; gpr_strvec_init(&v); gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:")); - gpr_mu_lock(cc->mu); - for (size_t i = 0; i < cc->outstanding_tag_count; i++) { + gpr_mu_lock(cqd->mu); + for (size_t i = 0; i < cqd->outstanding_tag_count; i++) { char *s; - gpr_asprintf(&s, " %p", cc->outstanding_tags[i]); + gpr_asprintf(&s, " %p", cqd->outstanding_tags[i]); gpr_strvec_add(&v, s); } - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); char *out = gpr_strvec_flatten(&v, NULL); gpr_strvec_destroy(&v); gpr_log(GPR_DEBUG, "%s", out); @@ -507,17 +746,11 @@ static void dump_pending_tags(grpc_completion_queue *cc) { static void dump_pending_tags(grpc_completion_queue *cc) {} #endif -grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, - gpr_timespec deadline, void *reserved) { +static grpc_event cq_next(grpc_completion_queue *cc, gpr_timespec deadline, + void *reserved) { grpc_event ret; gpr_timespec now; - - if (cc->completion_type != GRPC_CQ_NEXT) { - gpr_log(GPR_ERROR, - "grpc_completion_queue_next() cannot be called on this completion " - "queue since its completion type is not GRPC_CQ_NEXT"); - abort(); - } + cq_data *cqd = &cc->data; GPR_TIMER_BEGIN("grpc_completion_queue_next", 0); @@ -536,10 +769,10 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); GRPC_CQ_INTERNAL_REF(cc, "next"); - gpr_mu_lock(cc->mu); + cq_is_finished_arg is_finished_arg = { .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cc->things_queued_ever), + gpr_atm_no_barrier_load(&cqd->things_queued_ever), .cq = cc, .deadline = deadline, .stolen_completion = NULL, @@ -547,9 +780,11 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, .first_loop = true}; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg); + for (;;) { + gpr_timespec iteration_deadline = deadline; + if (is_finished_arg.stolen_completion != NULL) { - gpr_mu_unlock(cc->mu); grpc_cq_completion *c = is_finished_arg.stolen_completion; is_finished_arg.stolen_completion = NULL; ret.type = GRPC_OP_COMPLETE; @@ -558,37 +793,59 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, c->done(&exec_ctx, c->done_arg, c); break; } - if (cc->completed_tail != &cc->completed_head) { - grpc_cq_completion *c = (grpc_cq_completion *)cc->completed_head.next; - cc->completed_head.next = c->next & ~(uintptr_t)1; - if (c == cc->completed_tail) { - cc->completed_tail = &cc->completed_head; - } - gpr_mu_unlock(cc->mu); + + grpc_cq_completion *c = cq_event_queue_pop(&cqd->queue); + + if (c != NULL) { ret.type = GRPC_OP_COMPLETE; ret.success = c->next & 1u; ret.tag = c->tag; c->done(&exec_ctx, c->done_arg, c); break; + } else { + /* If c == NULL it means either the queue is empty OR in an transient + inconsistent state. If it is the latter, we shold do a 0-timeout poll + so that the thread comes back quickly from poll to make a second + attempt at popping. Not doing this can potentially deadlock this thread + forever (if the deadline is infinity) */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + iteration_deadline = gpr_time_0(GPR_CLOCK_MONOTONIC); + } } - if (cc->shutdown) { - gpr_mu_unlock(cc->mu); + + if (gpr_atm_no_barrier_load(&cqd->shutdown)) { + /* Before returning, check if the queue has any items left over (since + gpr_mpscq_pop() can sometimes return NULL even if the queue is not + empty. If so, keep retrying but do not return GRPC_QUEUE_SHUTDOWN */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + /* Go to the beginning of the loop. No point doing a poll because + (cc->shutdown == true) is only possible when there is no pending work + (i.e cc->pending_events == 0) and any outstanding grpc_cq_completion + events are already queued on this cq */ + continue; + } + memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_SHUTDOWN; break; } + now = gpr_now(GPR_CLOCK_MONOTONIC); if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { - gpr_mu_unlock(cc->mu); memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_TIMEOUT; dump_pending_tags(cc); break; } + + /* The main polling work happens in grpc_pollset_work */ + gpr_mu_lock(cqd->mu); + cqd->num_polls++; grpc_error *err = cc->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cc), - NULL, now, deadline); + NULL, now, iteration_deadline); + gpr_mu_unlock(cqd->mu); + if (err != GRPC_ERROR_NONE) { - gpr_mu_unlock(cc->mu); const char *msg = grpc_error_string(err); gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg); @@ -600,6 +857,7 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, } is_finished_arg.first_loop = false; } + GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cc, "next"); grpc_exec_ctx_finish(&exec_ctx); @@ -610,24 +868,30 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, return ret; } +grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, + gpr_timespec deadline, void *reserved) { + return cc->vtable->next(cc, deadline, reserved); +} + static int add_plucker(grpc_completion_queue *cc, void *tag, grpc_pollset_worker **worker) { - if (cc->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { + cq_data *cqd = &cc->data; + if (cqd->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { return 0; } - cc->pluckers[cc->num_pluckers].tag = tag; - cc->pluckers[cc->num_pluckers].worker = worker; - cc->num_pluckers++; + cqd->pluckers[cqd->num_pluckers].tag = tag; + cqd->pluckers[cqd->num_pluckers].worker = worker; + cqd->num_pluckers++; return 1; } static void del_plucker(grpc_completion_queue *cc, void *tag, grpc_pollset_worker **worker) { - int i; - for (i = 0; i < cc->num_pluckers; i++) { - if (cc->pluckers[i].tag == tag && cc->pluckers[i].worker == worker) { - cc->num_pluckers--; - GPR_SWAP(plucker, cc->pluckers[i], cc->pluckers[cc->num_pluckers]); + cq_data *cqd = &cc->data; + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag && cqd->pluckers[i].worker == worker) { + cqd->num_pluckers--; + GPR_SWAP(plucker, cqd->pluckers[i], cqd->pluckers[cqd->num_pluckers]); return; } } @@ -637,51 +901,47 @@ static void del_plucker(grpc_completion_queue *cc, void *tag, static bool cq_is_pluck_finished(grpc_exec_ctx *exec_ctx, void *arg) { cq_is_finished_arg *a = arg; grpc_completion_queue *cq = a->cq; + cq_data *cqd = &cq->data; + GPR_ASSERT(a->stolen_completion == NULL); gpr_atm current_last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cq->things_queued_ever); + gpr_atm_no_barrier_load(&cqd->things_queued_ever); if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { - gpr_mu_lock(cq->mu); + gpr_mu_lock(cqd->mu); a->last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cq->things_queued_ever); + gpr_atm_no_barrier_load(&cqd->things_queued_ever); grpc_cq_completion *c; - grpc_cq_completion *prev = &cq->completed_head; + grpc_cq_completion *prev = &cqd->completed_head; while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != - &cq->completed_head) { + &cqd->completed_head) { if (c->tag == a->tag) { prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); - if (c == cq->completed_tail) { - cq->completed_tail = prev; + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; } - gpr_mu_unlock(cq->mu); + gpr_mu_unlock(cqd->mu); a->stolen_completion = c; return true; } prev = c; } - gpr_mu_unlock(cq->mu); + gpr_mu_unlock(cqd->mu); } return !a->first_loop && gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; } -grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, - gpr_timespec deadline, void *reserved) { +static grpc_event cq_pluck(grpc_completion_queue *cc, void *tag, + gpr_timespec deadline, void *reserved) { grpc_event ret; grpc_cq_completion *c; grpc_cq_completion *prev; grpc_pollset_worker *worker = NULL; gpr_timespec now; + cq_data *cqd = &cc->data; GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0); - if (cc->completion_type != GRPC_CQ_PLUCK) { - gpr_log(GPR_ERROR, - "grpc_completion_queue_pluck() cannot be called on this completion " - "queue since its completion type is not GRPC_CQ_PLUCK"); - abort(); - } - if (GRPC_TRACER_ON(grpc_cq_pluck_trace)) { GRPC_API_TRACE( "grpc_completion_queue_pluck(" @@ -699,10 +959,10 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); GRPC_CQ_INTERNAL_REF(cc, "pluck"); - gpr_mu_lock(cc->mu); + gpr_mu_lock(cqd->mu); cq_is_finished_arg is_finished_arg = { .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cc->things_queued_ever), + gpr_atm_no_barrier_load(&cqd->things_queued_ever), .cq = cc, .deadline = deadline, .stolen_completion = NULL, @@ -712,7 +972,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, GRPC_EXEC_CTX_INITIALIZER(0, cq_is_pluck_finished, &is_finished_arg); for (;;) { if (is_finished_arg.stolen_completion != NULL) { - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); c = is_finished_arg.stolen_completion; is_finished_arg.stolen_completion = NULL; ret.type = GRPC_OP_COMPLETE; @@ -721,15 +981,15 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, c->done(&exec_ctx, c->done_arg, c); break; } - prev = &cc->completed_head; + prev = &cqd->completed_head; while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != - &cc->completed_head) { + &cqd->completed_head) { if (c->tag == tag) { prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); - if (c == cc->completed_tail) { - cc->completed_tail = prev; + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; } - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); ret.type = GRPC_OP_COMPLETE; ret.success = c->next & 1u; ret.tag = c->tag; @@ -738,8 +998,8 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, } prev = c; } - if (cc->shutdown) { - gpr_mu_unlock(cc->mu); + if (gpr_atm_no_barrier_load(&cqd->shutdown)) { + gpr_mu_unlock(cqd->mu); memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_SHUTDOWN; break; @@ -749,7 +1009,7 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, "Too many outstanding grpc_completion_queue_pluck calls: maximum " "is %d", GRPC_MAX_COMPLETION_QUEUE_PLUCKERS); - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); memset(&ret, 0, sizeof(ret)); /* TODO(ctiller): should we use a different result here */ ret.type = GRPC_QUEUE_TIMEOUT; @@ -759,19 +1019,21 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, now = gpr_now(GPR_CLOCK_MONOTONIC); if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { del_plucker(cc, tag, &worker); - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); memset(&ret, 0, sizeof(ret)); ret.type = GRPC_QUEUE_TIMEOUT; dump_pending_tags(cc); break; } + + cqd->num_polls++; grpc_error *err = cc->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now, deadline); if (err != GRPC_ERROR_NONE) { del_plucker(cc, tag, &worker); - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg); + gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg); GRPC_ERROR_UNREF(err); memset(&ret, 0, sizeof(ret)); @@ -793,26 +1055,48 @@ done: return ret; } +grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, + gpr_timespec deadline, void *reserved) { + return cc->vtable->pluck(cc, tag, deadline, reserved); +} + +/* Finishes the completion queue shutdown. This means that there are no more + completion events / tags expected from the completion queue + - Must be called under completion queue lock + - Must be called only once in completion queue's lifetime + - grpc_completion_queue_shutdown() MUST have been called before calling + this function */ +static void cq_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cc) { + cq_data *cqd = &cc->data; + + GPR_ASSERT(cqd->shutdown_called); + GPR_ASSERT(!gpr_atm_no_barrier_load(&cqd->shutdown)); + gpr_atm_no_barrier_store(&cqd->shutdown, 1); + + cc->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cc), + &cqd->pollset_shutdown_done); +} + /* Shutdown simply drops a ref that we reserved at creation time; if we drop to zero here, then enter shutdown mode and wake up any waiters */ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) { grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; GPR_TIMER_BEGIN("grpc_completion_queue_shutdown", 0); GRPC_API_TRACE("grpc_completion_queue_shutdown(cc=%p)", 1, (cc)); - gpr_mu_lock(cc->mu); - if (cc->shutdown_called) { - gpr_mu_unlock(cc->mu); + cq_data *cqd = &cc->data; + + gpr_mu_lock(cqd->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cqd->mu); GPR_TIMER_END("grpc_completion_queue_shutdown", 0); return; } - cc->shutdown_called = 1; - if (gpr_unref(&cc->pending_events)) { - GPR_ASSERT(!cc->shutdown); - cc->shutdown = 1; - cc->poller_vtable->shutdown(&exec_ctx, POLLSET_FROM_CQ(cc), - &cc->pollset_shutdown_done); + cqd->shutdown_called = 1; + if (gpr_unref(&cqd->pending_events)) { + cq_finish_shutdown(&exec_ctx, cc); } - gpr_mu_unlock(cc->mu); + gpr_mu_unlock(cqd->mu); grpc_exec_ctx_finish(&exec_ctx); GPR_TIMER_END("grpc_completion_queue_shutdown", 0); } @@ -821,6 +1105,13 @@ void grpc_completion_queue_destroy(grpc_completion_queue *cc) { GRPC_API_TRACE("grpc_completion_queue_destroy(cc=%p)", 1, (cc)); GPR_TIMER_BEGIN("grpc_completion_queue_destroy", 0); grpc_completion_queue_shutdown(cc); + + /* TODO (sreek): This should not ideally be here. Refactor it into the + * cq_vtable (perhaps have a create/destroy methods in the cq vtable) */ + if (cc->vtable->cq_completion_type == GRPC_CQ_NEXT) { + GPR_ASSERT(cq_event_queue_num_items(&cc->data.queue) == 0); + } + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cc, "destroy"); grpc_exec_ctx_finish(&exec_ctx); @@ -835,22 +1126,12 @@ grpc_completion_queue *grpc_cq_from_pollset(grpc_pollset *ps) { return CQ_FROM_POLLSET(ps); } -void grpc_cq_mark_non_listening_server_cq(grpc_completion_queue *cc) { - /* TODO: sreek - use cc->polling_type field here and add a validation check - (i.e grpc_cq_mark_non_listening_server_cq can only be called on a cc whose - polling_type is set to GRPC_CQ_NON_LISTENING */ - cc->is_non_listening_server_cq = 1; +void grpc_cq_mark_server_cq(grpc_completion_queue *cc) { + cc->data.is_server_cq = 1; } -bool grpc_cq_is_non_listening_server_cq(grpc_completion_queue *cc) { - /* TODO (sreek) - return (cc->polling_type == GRPC_CQ_NON_LISTENING) */ - return (cc->is_non_listening_server_cq == 1); -} - -void grpc_cq_mark_server_cq(grpc_completion_queue *cc) { cc->is_server_cq = 1; } - bool grpc_cq_is_server_cq(grpc_completion_queue *cc) { - return cc->is_server_cq; + return cc->data.is_server_cq; } bool grpc_cq_can_listen(grpc_completion_queue *cc) { diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h index 8d9ce2ec02..7963ea75e7 100644 --- a/src/core/lib/surface/completion_queue.h +++ b/src/core/lib/surface/completion_queue.h @@ -49,7 +49,13 @@ extern grpc_tracer_flag grpc_trace_operation_failures; extern grpc_tracer_flag grpc_trace_pending_tags; #endif +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_cq_completion { + gpr_mpscq_node node; + /** user supplied tag */ void *tag; /** done callback - called when this queue element is no longer @@ -101,7 +107,13 @@ bool grpc_cq_can_listen(grpc_completion_queue *cc); grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue *cc); +int grpc_get_cq_poll_num(grpc_completion_queue *cc); + grpc_completion_queue *grpc_completion_queue_create_internal( grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */ diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c index 560229e892..7e4ae421a0 100644 --- a/src/core/lib/surface/server.c +++ b/src/core/lib/surface/server.c @@ -47,7 +47,8 @@ #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/stack_lockfree.h" +#include "src/core/lib/support/mpscq.h" +#include "src/core/lib/support/spinlock.h" #include "src/core/lib/support/string.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/call.h" @@ -76,6 +77,7 @@ typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; grpc_tracer_flag grpc_server_channel_trace = GRPC_TRACER_INITIALIZER(false); typedef struct requested_call { + gpr_mpscq_node request_link; /* must be first */ requested_call_type type; size_t cq_idx; void *tag; @@ -175,7 +177,7 @@ struct request_matcher { grpc_server *server; call_data *pending_head; call_data *pending_tail; - gpr_stack_lockfree **requests_per_cq; + gpr_locked_mpscq *requests_per_cq; }; struct registered_method { @@ -220,11 +222,6 @@ struct grpc_server { registered_method *registered_methods; /** one request matcher for unregistered methods */ request_matcher unregistered_request_matcher; - /** free list of available requested_calls_per_cq indices */ - gpr_stack_lockfree **request_freelist_per_cq; - /** requested call backing data */ - requested_call **requested_calls_per_cq; - int max_requested_calls_per_cq; gpr_atm shutdown_flag; uint8_t shutdown_published; @@ -324,21 +321,20 @@ static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx, * request_matcher */ -static void request_matcher_init(request_matcher *rm, size_t entries, - grpc_server *server) { +static void request_matcher_init(request_matcher *rm, grpc_server *server) { memset(rm, 0, sizeof(*rm)); rm->server = server; rm->requests_per_cq = gpr_malloc(sizeof(*rm->requests_per_cq) * server->cq_count); for (size_t i = 0; i < server->cq_count; i++) { - rm->requests_per_cq[i] = gpr_stack_lockfree_create(entries); + gpr_locked_mpscq_init(&rm->requests_per_cq[i]); } } static void request_matcher_destroy(request_matcher *rm) { for (size_t i = 0; i < rm->server->cq_count; i++) { - GPR_ASSERT(gpr_stack_lockfree_pop(rm->requests_per_cq[i]) == -1); - gpr_stack_lockfree_destroy(rm->requests_per_cq[i]); + GPR_ASSERT(gpr_locked_mpscq_pop(&rm->requests_per_cq[i]) == NULL); + gpr_locked_mpscq_destroy(&rm->requests_per_cq[i]); } gpr_free(rm->requests_per_cq); } @@ -368,13 +364,17 @@ static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx, grpc_server *server, request_matcher *rm, grpc_error *error) { - int request_id; + requested_call *rc; for (size_t i = 0; i < server->cq_count; i++) { - while ((request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[i])) != - -1) { - fail_call(exec_ctx, server, i, - &server->requested_calls_per_cq[i][request_id], - GRPC_ERROR_REF(error)); + /* Here we know: + 1. no requests are being added (since the server is shut down) + 2. no other threads are pulling (since the shut down process is single + threaded) + So, we can ignore the queue lock and just pop, with the guarantee that a + NULL returned here truly means that the queue is empty */ + while ((rc = (requested_call *)gpr_mpscq_pop( + &rm->requests_per_cq[i].queue)) != NULL) { + fail_call(exec_ctx, server, i, rc, GRPC_ERROR_REF(error)); } } GRPC_ERROR_UNREF(error); @@ -409,13 +409,7 @@ static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) { } for (i = 0; i < server->cq_count; i++) { GRPC_CQ_INTERNAL_UNREF(exec_ctx, server->cqs[i], "server"); - if (server->started) { - gpr_stack_lockfree_destroy(server->request_freelist_per_cq[i]); - gpr_free(server->requested_calls_per_cq[i]); - } } - gpr_free(server->request_freelist_per_cq); - gpr_free(server->requested_calls_per_cq); gpr_free(server->cqs); gpr_free(server->pollsets); gpr_free(server->shutdown_tags); @@ -473,21 +467,7 @@ static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand, static void done_request_event(grpc_exec_ctx *exec_ctx, void *req, grpc_cq_completion *c) { - requested_call *rc = req; - grpc_server *server = rc->server; - - if (rc >= server->requested_calls_per_cq[rc->cq_idx] && - rc < server->requested_calls_per_cq[rc->cq_idx] + - server->max_requested_calls_per_cq) { - GPR_ASSERT(rc - server->requested_calls_per_cq[rc->cq_idx] <= INT_MAX); - gpr_stack_lockfree_push( - server->request_freelist_per_cq[rc->cq_idx], - (int)(rc - server->requested_calls_per_cq[rc->cq_idx])); - } else { - gpr_free(req); - } - - server_unref(exec_ctx, server); + gpr_free(req); } static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server, @@ -516,10 +496,6 @@ static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server, GPR_UNREACHABLE_CODE(return ); } - grpc_call_element *elem = - grpc_call_stack_element(grpc_call_get_call_stack(call), 0); - channel_data *chand = elem->channel_data; - server_ref(chand->server); grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, GRPC_ERROR_NONE, done_request_event, rc, &rc->completion); } @@ -547,15 +523,15 @@ static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg, for (size_t i = 0; i < server->cq_count; i++) { size_t cq_idx = (chand->cq_idx + i) % server->cq_count; - int request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); - if (request_id == -1) { + requested_call *rc = + (requested_call *)gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx]); + if (rc == NULL) { continue; } else { gpr_mu_lock(&calld->mu_state); calld->state = ACTIVATED; gpr_mu_unlock(&calld->mu_state); - publish_call(exec_ctx, server, calld, cq_idx, - &server->requested_calls_per_cq[cq_idx][request_id]); + publish_call(exec_ctx, server, calld, cq_idx, rc); return; /* early out */ } } @@ -1029,8 +1005,6 @@ grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { server->root_channel_data.next = server->root_channel_data.prev = &server->root_channel_data; - /* TODO(ctiller): expose a channel_arg for this */ - server->max_requested_calls_per_cq = 32768; server->channel_args = grpc_channel_args_copy(args); return server; @@ -1103,29 +1077,15 @@ void grpc_server_start(grpc_server *server) { server->started = true; server->pollset_count = 0; server->pollsets = gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); - server->request_freelist_per_cq = - gpr_malloc(sizeof(*server->request_freelist_per_cq) * server->cq_count); - server->requested_calls_per_cq = - gpr_malloc(sizeof(*server->requested_calls_per_cq) * server->cq_count); for (i = 0; i < server->cq_count; i++) { if (grpc_cq_can_listen(server->cqs[i])) { server->pollsets[server->pollset_count++] = grpc_cq_pollset(server->cqs[i]); } - server->request_freelist_per_cq[i] = - gpr_stack_lockfree_create((size_t)server->max_requested_calls_per_cq); - for (int j = 0; j < server->max_requested_calls_per_cq; j++) { - gpr_stack_lockfree_push(server->request_freelist_per_cq[i], j); - } - server->requested_calls_per_cq[i] = - gpr_malloc((size_t)server->max_requested_calls_per_cq * - sizeof(*server->requested_calls_per_cq[i])); } - request_matcher_init(&server->unregistered_request_matcher, - (size_t)server->max_requested_calls_per_cq, server); + request_matcher_init(&server->unregistered_request_matcher, server); for (registered_method *rm = server->registered_methods; rm; rm = rm->next) { - request_matcher_init(&rm->request_matcher, - (size_t)server->max_requested_calls_per_cq, server); + request_matcher_init(&rm->request_matcher, server); } server_ref(server); @@ -1379,21 +1339,11 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, requested_call *rc) { call_data *calld = NULL; request_matcher *rm = NULL; - int request_id; if (gpr_atm_acq_load(&server->shutdown_flag)) { fail_call(exec_ctx, server, cq_idx, rc, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); return GRPC_CALL_OK; } - request_id = gpr_stack_lockfree_pop(server->request_freelist_per_cq[cq_idx]); - if (request_id == -1) { - /* out of request ids: just fail this one */ - fail_call(exec_ctx, server, cq_idx, rc, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Out of request ids"), - GRPC_ERROR_INT_LIMIT, server->max_requested_calls_per_cq)); - return GRPC_CALL_OK; - } switch (rc->type) { case BATCH_CALL: rm = &server->unregistered_request_matcher; @@ -1402,15 +1352,13 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, rm = &rc->data.registered.registered_method->request_matcher; break; } - server->requested_calls_per_cq[cq_idx][request_id] = *rc; - gpr_free(rc); - if (gpr_stack_lockfree_push(rm->requests_per_cq[cq_idx], request_id)) { + if (gpr_locked_mpscq_push(&rm->requests_per_cq[cq_idx], &rc->request_link)) { /* this was the first queued request: we need to lock and start matching calls */ gpr_mu_lock(&server->mu_call); while ((calld = rm->pending_head) != NULL) { - request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); - if (request_id == -1) break; + rc = (requested_call *)gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx]); + if (rc == NULL) break; rm->pending_head = calld->pending_next; gpr_mu_unlock(&server->mu_call); gpr_mu_lock(&calld->mu_state); @@ -1426,8 +1374,7 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, GPR_ASSERT(calld->state == PENDING); calld->state = ACTIVATED; gpr_mu_unlock(&calld->mu_state); - publish_call(exec_ctx, server, calld, cq_idx, - &server->requested_calls_per_cq[cq_idx][request_id]); + publish_call(exec_ctx, server, calld, cq_idx, rc); } gpr_mu_lock(&server->mu_call); } @@ -1534,7 +1481,6 @@ static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, rc->initial_metadata->count = 0; GPR_ASSERT(error != GRPC_ERROR_NONE); - server_ref(server); grpc_cq_end_op(exec_ctx, server->cqs[cq_idx], rc->tag, error, done_request_event, rc, &rc->completion); } diff --git a/src/core/lib/transport/bdp_estimator.c b/src/core/lib/transport/bdp_estimator.c index da8019d98b..e3a82b492a 100644 --- a/src/core/lib/transport/bdp_estimator.c +++ b/src/core/lib/transport/bdp_estimator.c @@ -53,6 +53,11 @@ bool grpc_bdp_estimator_get_estimate(grpc_bdp_estimator *estimator, return true; } +bool grpc_bdp_estimator_get_bw(grpc_bdp_estimator *estimator, double *bw) { + *bw = estimator->bw_est; + return true; +} + bool grpc_bdp_estimator_add_incoming_bytes(grpc_bdp_estimator *estimator, int64_t num_bytes) { estimator->accumulator += num_bytes; diff --git a/src/core/lib/transport/bdp_estimator.h b/src/core/lib/transport/bdp_estimator.h index 135376edd7..b9a7fc84bb 100644 --- a/src/core/lib/transport/bdp_estimator.h +++ b/src/core/lib/transport/bdp_estimator.h @@ -64,6 +64,8 @@ void grpc_bdp_estimator_init(grpc_bdp_estimator *estimator, const char *name); // Returns true if a reasonable estimate could be obtained bool grpc_bdp_estimator_get_estimate(grpc_bdp_estimator *estimator, int64_t *estimate); +// Returns true if a reasonable estimate could be obtained +bool grpc_bdp_estimator_get_bw(grpc_bdp_estimator *estimator, double *bw); // Returns true if the user should schedule a ping bool grpc_bdp_estimator_add_incoming_bytes(grpc_bdp_estimator *estimator, int64_t num_bytes); diff --git a/src/core/plugin_registry/grpc_plugin_registry.c b/src/core/plugin_registry/grpc_plugin_registry.c index 25bda7a262..510cf5d5a0 100644 --- a/src/core/plugin_registry/grpc_plugin_registry.c +++ b/src/core/plugin_registry/grpc_plugin_registry.c @@ -61,6 +61,8 @@ extern void grpc_max_age_filter_init(void); extern void grpc_max_age_filter_shutdown(void); extern void grpc_message_size_filter_init(void); extern void grpc_message_size_filter_shutdown(void); +extern void grpc_workaround_cronet_compression_filter_init(void); +extern void grpc_workaround_cronet_compression_filter_shutdown(void); void grpc_register_built_in_plugins(void) { grpc_register_plugin(grpc_http_filters_init, @@ -91,4 +93,6 @@ void grpc_register_built_in_plugins(void) { grpc_max_age_filter_shutdown); grpc_register_plugin(grpc_message_size_filter_init, grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); } diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c index 05d4771bce..e5eb68f934 100644 --- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c +++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c @@ -61,6 +61,8 @@ extern void grpc_max_age_filter_init(void); extern void grpc_max_age_filter_shutdown(void); extern void grpc_message_size_filter_init(void); extern void grpc_message_size_filter_shutdown(void); +extern void grpc_workaround_cronet_compression_filter_init(void); +extern void grpc_workaround_cronet_compression_filter_shutdown(void); void grpc_register_built_in_plugins(void) { grpc_register_plugin(grpc_http_filters_init, @@ -91,4 +93,6 @@ void grpc_register_built_in_plugins(void) { grpc_max_age_filter_shutdown); grpc_register_plugin(grpc_message_size_filter_init, grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); } diff --git a/src/cpp/server/server_builder.cc b/src/cpp/server/server_builder.cc index 2ead048a1f..6dca6a6862 100644 --- a/src/cpp/server/server_builder.cc +++ b/src/cpp/server/server_builder.cc @@ -358,4 +358,14 @@ void ServerBuilder::InternalAddPluginFactory( (*g_plugin_factory_list).push_back(CreatePlugin); } +ServerBuilder& ServerBuilder::EnableWorkaround(grpc_workaround_list id) { + switch (id) { + case GRPC_WORKAROUND_ID_CRONET_COMPRESSION: + return AddChannelArgument(GRPC_ARG_WORKAROUND_CRONET_COMPRESSION, 1); + default: + gpr_log(GPR_ERROR, "Workaround %u does not exist or is obsolete.", id); + return *this; + } +} + } // namespace grpc diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc index 2f89aa3dce..7c93bb8683 100644 --- a/src/cpp/server/server_cc.cc +++ b/src/cpp/server/server_cc.cc @@ -686,6 +686,7 @@ bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag, StringFromCopiedSlice(call_details_.method); static_cast<GenericServerContext*>(context_)->host_ = StringFromCopiedSlice(call_details_.host); + context_->deadline_ = call_details_.deadline; } grpc_slice_unref(call_details_.method); grpc_slice_unref(call_details_.host); diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 7e0f3f053d..c0865001a8 100755 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -19,27 +19,27 @@ <ItemGroup> <EmbeddedResource Include="..\..\..\etc\roots.pem" /> - <Content Include="..\nativelibs\macosx_x64\libgrpc_csharp_ext.dylib"> + <Content Include="..\nativelibs\csharp_ext_macos_x64\libgrpc_csharp_ext.dylib"> <PackagePath>runtimes/osx/native/libgrpc_csharp_ext.x64.dylib</PackagePath> <Pack>true</Pack> </Content> - <Content Include="..\nativelibs\macosx_x86\libgrpc_csharp_ext.dylib"> + <Content Include="..\nativelibs\csharp_ext_macos_x86\libgrpc_csharp_ext.dylib"> <PackagePath>runtimes/osx/native/libgrpc_csharp_ext.x86.dylib</PackagePath> <Pack>true</Pack> </Content> - <Content Include="..\nativelibs\linux_x64\libgrpc_csharp_ext.so"> + <Content Include="..\nativelibs\csharp_ext_linux_x64\libgrpc_csharp_ext.so"> <PackagePath>runtimes/linux/native/libgrpc_csharp_ext.x64.so</PackagePath> <Pack>true</Pack> </Content> - <Content Include="..\nativelibs\linux_x86\libgrpc_csharp_ext.so"> + <Content Include="..\nativelibs\csharp_ext_linux_x86\libgrpc_csharp_ext.so"> <PackagePath>runtimes/linux/native/libgrpc_csharp_ext.x86.so</PackagePath> <Pack>true</Pack> </Content> - <Content Include="..\nativelibs\windows_x64\grpc_csharp_ext.dll"> + <Content Include="..\nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll"> <PackagePath>runtimes/win/native/grpc_csharp_ext.x64.dll</PackagePath> <Pack>true</Pack> </Content> - <Content Include="..\nativelibs\windows_x86\grpc_csharp_ext.dll"> + <Content Include="..\nativelibs\csharp_ext_windows_x86\grpc_csharp_ext.dll"> <PackagePath>runtimes/win/native/grpc_csharp_ext.x86.dll</PackagePath> <Pack>true</Pack> </Content> diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 3c368fbc6c..8ed0c0b92f 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -117,7 +117,7 @@ namespace Grpc.Core.Internal { var ctx = BatchContextSafeHandle.Create(); completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); - Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk(); + Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk(); } } @@ -140,7 +140,7 @@ namespace Grpc.Core.Internal var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success)); var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); - Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata, + Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); } } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs index a98861af61..696987d2a8 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs @@ -346,11 +346,11 @@ namespace Grpc.Core.Internal public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, bool sendEmptyInitialMetadata); + BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, + BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); @@ -406,7 +406,7 @@ namespace Grpc.Core.Internal public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor); public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); - public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth); + public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth); public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials); public delegate ServerSafeHandle grpcsharp_server_create_delegate(ChannelArgsSafeHandle args); diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs index 24f686fddc..c14fa7c827 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -53,7 +53,7 @@ namespace Grpc.Core.Internal return Native.grpcsharp_ssl_server_credentials_create(pemRootCerts, keyCertPairCertChainArray, keyCertPairPrivateKeyArray, new UIntPtr((ulong)keyCertPairCertChainArray.Length), - forceClientAuth); + forceClientAuth ? 1 : 0); } protected override bool ReleaseHandle() diff --git a/src/csharp/Grpc.Core/Version.csproj.include b/src/csharp/Grpc.Core/Version.csproj.include index 6af2af10bd..8388bfd9cc 100755 --- a/src/csharp/Grpc.Core/Version.csproj.include +++ b/src/csharp/Grpc.Core/Version.csproj.include @@ -2,6 +2,6 @@ <Project> <PropertyGroup> <GrpcCsharpVersion>1.4.0-dev</GrpcCsharpVersion> - <GoogleProtobufVersion>3.2.0</GoogleProtobufVersion> + <GoogleProtobufVersion>3.3.0</GoogleProtobufVersion> </PropertyGroup> </Project> diff --git a/src/csharp/Grpc.Tools.nuspec b/src/csharp/Grpc.Tools.nuspec index ba4e1d674c..0cae5572fd 100644 --- a/src/csharp/Grpc.Tools.nuspec +++ b/src/csharp/Grpc.Tools.nuspec @@ -17,17 +17,17 @@ </metadata> <files> <!-- forward slashes in src path enable building on Linux --> - <file src="protoc_plugins/windows_x86/protoc.exe" target="tools/windows_x86/protoc.exe" /> - <file src="protoc_plugins/windows_x86/grpc_csharp_plugin.exe" target="tools/windows_x86/grpc_csharp_plugin.exe" /> - <file src="protoc_plugins/windows_x64/protoc.exe" target="tools/windows_x64/protoc.exe" /> - <file src="protoc_plugins/windows_x64/grpc_csharp_plugin.exe" target="tools/windows_x64/grpc_csharp_plugin.exe" /> - <file src="protoc_plugins/linux_x86/protoc" target="tools/linux_x86/protoc" /> - <file src="protoc_plugins/linux_x86/grpc_csharp_plugin" target="tools/linux_x86/grpc_csharp_plugin" /> - <file src="protoc_plugins/linux_x64/protoc" target="tools/linux_x64/protoc" /> - <file src="protoc_plugins/linux_x64/grpc_csharp_plugin" target="tools/linux_x64/grpc_csharp_plugin" /> - <file src="protoc_plugins/macosx_x86/protoc" target="tools/macosx_x86/protoc" /> - <file src="protoc_plugins/macosx_x86/grpc_csharp_plugin" target="tools/macosx_x86/grpc_csharp_plugin" /> - <file src="protoc_plugins/macosx_x64/protoc" target="tools/macosx_x64/protoc" /> - <file src="protoc_plugins/macosx_x64/grpc_csharp_plugin" target="tools/macosx_x64/grpc_csharp_plugin" /> + <file src="protoc_plugins/protoc_windows_x86/protoc.exe" target="tools/windows_x86/protoc.exe" /> + <file src="protoc_plugins/protoc_windows_x86/grpc_csharp_plugin.exe" target="tools/windows_x86/grpc_csharp_plugin.exe" /> + <file src="protoc_plugins/protoc_windows_x64/protoc.exe" target="tools/windows_x64/protoc.exe" /> + <file src="protoc_plugins/protoc_windows_x64/grpc_csharp_plugin.exe" target="tools/windows_x64/grpc_csharp_plugin.exe" /> + <file src="protoc_plugins/protoc_linux_x86/protoc" target="tools/linux_x86/protoc" /> + <file src="protoc_plugins/protoc_linux_x86/grpc_csharp_plugin" target="tools/linux_x86/grpc_csharp_plugin" /> + <file src="protoc_plugins/protoc_linux_x64/protoc" target="tools/linux_x64/protoc" /> + <file src="protoc_plugins/protoc_linux_x64/grpc_csharp_plugin" target="tools/linux_x64/grpc_csharp_plugin" /> + <file src="protoc_plugins/protoc_macos_x86/protoc" target="tools/macosx_x86/protoc" /> + <file src="protoc_plugins/protoc_macos_x86/grpc_csharp_plugin" target="tools/macosx_x86/grpc_csharp_plugin" /> + <file src="protoc_plugins/protoc_macos_x64/protoc" target="tools/macosx_x64/protoc" /> + <file src="protoc_plugins/protoc_macos_x64/grpc_csharp_plugin" target="tools/macosx_x64/grpc_csharp_plugin" /> </files> </package> diff --git a/src/csharp/build_packages_dotnetcli.bat b/src/csharp/build_packages_dotnetcli.bat index 673642e3d8..d823942be5 100755 --- a/src/csharp/build_packages_dotnetcli.bat +++ b/src/csharp/build_packages_dotnetcli.bat @@ -36,35 +36,26 @@ set DOTNET=dotnet set -ex -mkdir -p ..\..\artifacts\ +mkdir ..\..\artifacts @rem Collect the artifacts built by the previous build step if running on Jenkins -@rem TODO(jtattermusch): is there a better way to do this? -xcopy /Y /I ..\..\architecture=x86,language=csharp,platform=windows\artifacts\* nativelibs\windows_x86\ -xcopy /Y /I ..\..\architecture=x64,language=csharp,platform=windows\artifacts\* nativelibs\windows_x64\ -xcopy /Y /I ..\..\architecture=x86,language=csharp,platform=linux\artifacts\* nativelibs\linux_x86\ -xcopy /Y /I ..\..\architecture=x64,language=csharp,platform=linux\artifacts\* nativelibs\linux_x64\ -xcopy /Y /I ..\..\architecture=x86,language=csharp,platform=macos\artifacts\* nativelibs\macosx_x86\ -xcopy /Y /I ..\..\architecture=x64,language=csharp,platform=macos\artifacts\* nativelibs\macosx_x64\ +mkdir nativelibs +powershell -Command "cp -r ..\..\platform=*\artifacts\csharp_ext_* nativelibs" @rem Collect protoc artifacts built by the previous build step -xcopy /Y /I ..\..\architecture=x86,language=protoc,platform=windows\artifacts\* protoc_plugins\windows_x86\ -xcopy /Y /I ..\..\architecture=x64,language=protoc,platform=windows\artifacts\* protoc_plugins\windows_x64\ -xcopy /Y /I ..\..\architecture=x86,language=protoc,platform=linux\artifacts\* protoc_plugins\linux_x86\ -xcopy /Y /I ..\..\architecture=x64,language=protoc,platform=linux\artifacts\* protoc_plugins\linux_x64\ -xcopy /Y /I ..\..\architecture=x86,language=protoc,platform=macos\artifacts\* protoc_plugins\macosx_x86\ -xcopy /Y /I ..\..\architecture=x64,language=protoc,platform=macos\artifacts\* protoc_plugins\macosx_x64\ +mkdir protoc_plugins +powershell -Command "cp -r ..\..\platform=*\artifacts\protoc_* protoc_plugins" %DOTNET% restore Grpc.sln || goto :error @rem To be able to build, we also need to put grpc_csharp_ext to its normal location -xcopy /Y /I nativelibs\windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release\ +xcopy /Y /I nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll ..\..\cmake\build\x64\Release\ -%DOTNET% pack --configuration Release Grpc.Core --output ..\..\..\artifacts || goto :error -%DOTNET% pack --configuration Release Grpc.Core.Testing --output ..\..\..\artifacts || goto :error -%DOTNET% pack --configuration Release Grpc.Auth --output ..\..\..\artifacts || goto :error -%DOTNET% pack --configuration Release Grpc.HealthCheck --output ..\..\..\artifacts || goto :error -%DOTNET% pack --configuration Release Grpc.Reflection --output ..\..\..\artifacts || goto :error +%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core --output ..\..\..\artifacts || goto :error +%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ..\..\..\artifacts || goto :error +%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Auth --output ..\..\..\artifacts || goto :error +%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ..\..\..\artifacts || goto :error +%DOTNET% pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ..\..\..\artifacts || goto :error %NUGET% pack Grpc.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts || goto :error %NUGET% pack Grpc.Tools.nuspec -Version %VERSION% -OutputDirectory ..\..\artifacts diff --git a/src/csharp/build_packages_dotnetcli.sh b/src/csharp/build_packages_dotnetcli.sh index ee923e3d87..f79c97fbbc 100755 --- a/src/csharp/build_packages_dotnetcli.sh +++ b/src/csharp/build_packages_dotnetcli.sh @@ -34,41 +34,25 @@ cd $(dirname $0) mkdir -p ../../artifacts/ -mkdir -p nativelibs/windows_x86 nativelibs/windows_x64 \ - nativelibs/linux_x86 nativelibs/linux_x64 \ - nativelibs/macosx_x86 nativelibs/macosx_x64 - -mkdir -p protoc_plugins/windows_x86 protoc_plugins/windows_x64 \ - protoc_plugins/linux_x86 protoc_plugins/linux_x64 \ - protoc_plugins/macosx_x86 protoc_plugins/macosx_x64 - -# Collect the artifacts built by the previous build step if running on Jenkins -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=windows/artifacts/* nativelibs/windows_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=windows/artifacts/* nativelibs/windows_x64 || true -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=linux/artifacts/* nativelibs/linux_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=linux/artifacts/* nativelibs/linux_x64 || true -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=csharp,platform=macos/artifacts/* nativelibs/macosx_x64 || true +# Collect the artifacts built by the previous build step +mkdir -p nativelibs +cp -r $EXTERNAL_GIT_ROOT/platform={windows,linux,macos}/artifacts/csharp_ext_* nativelibs || true # Collect protoc artifacts built by the previous build step -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=windows/artifacts/* protoc_plugins/windows_x64 || true -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=linux/artifacts/* protoc_plugins/linux_x64 || true -cp $EXTERNAL_GIT_ROOT/architecture=x86,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x86 || true -cp $EXTERNAL_GIT_ROOT/architecture=x64,language=protoc,platform=macos/artifacts/* protoc_plugins/macosx_x64 || true +mkdir -p protoc_plugins +cp -r $EXTERNAL_GIT_ROOT/platform={windows,linux,macos}/artifacts/protoc_* protoc_plugins || true dotnet restore Grpc.sln # To be able to build, we also need to put grpc_csharp_ext to its normal location mkdir -p ../../libs/opt -cp nativelibs/linux_x64/libgrpc_csharp_ext.so ../../libs/opt +cp nativelibs/csharp_ext_linux_x64/libgrpc_csharp_ext.so ../../libs/opt -dotnet pack --configuration Release Grpc.Core --output ../../../artifacts -dotnet pack --configuration Release Grpc.Core.Testing --output ../../../artifacts -dotnet pack --configuration Release Grpc.Auth --output ../../../artifacts -dotnet pack --configuration Release Grpc.HealthCheck --output ../../../artifacts -dotnet pack --configuration Release Grpc.Reflection --output ../../../artifacts +dotnet pack --configuration Release --include-symbols --include-source Grpc.Core --output ../../../artifacts +dotnet pack --configuration Release --include-symbols --include-source Grpc.Core.Testing --output ../../../artifacts +dotnet pack --configuration Release --include-symbols --include-source Grpc.Auth --output ../../../artifacts +dotnet pack --configuration Release --include-symbols --include-source Grpc.HealthCheck --output ../../../artifacts +dotnet pack --configuration Release --include-symbols --include-source Grpc.Reflection --output ../../../artifacts nuget pack Grpc.nuspec -Version "1.4.0-dev" -OutputDirectory ../../artifacts nuget pack Grpc.Tools.nuspec -Version "1.4.0-dev" -OutputDirectory ../../artifacts diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc index e193e82179..c444ad0b7b 100644 --- a/src/node/ext/node_grpc.cc +++ b/src/node/ext/node_grpc.cc @@ -85,98 +85,6 @@ logger_state grpc_logger_state; static char *pem_root_certs = NULL; -void InitStatusConstants(Local<Object> exports) { - Nan::HandleScope scope; - Local<Object> status = Nan::New<Object>(); - Nan::Set(exports, Nan::New("status").ToLocalChecked(), status); - Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_STATUS_OK)); - Nan::Set(status, Nan::New("OK").ToLocalChecked(), OK); - Local<Value> CANCELLED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_CANCELLED)); - Nan::Set(status, Nan::New("CANCELLED").ToLocalChecked(), CANCELLED); - Local<Value> UNKNOWN(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN)); - Nan::Set(status, Nan::New("UNKNOWN").ToLocalChecked(), UNKNOWN); - Local<Value> INVALID_ARGUMENT( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT)); - Nan::Set(status, Nan::New("INVALID_ARGUMENT").ToLocalChecked(), - INVALID_ARGUMENT); - Local<Value> DEADLINE_EXCEEDED( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED)); - Nan::Set(status, Nan::New("DEADLINE_EXCEEDED").ToLocalChecked(), - DEADLINE_EXCEEDED); - Local<Value> NOT_FOUND(Nan::New<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND)); - Nan::Set(status, Nan::New("NOT_FOUND").ToLocalChecked(), NOT_FOUND); - Local<Value> ALREADY_EXISTS( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS)); - Nan::Set(status, Nan::New("ALREADY_EXISTS").ToLocalChecked(), ALREADY_EXISTS); - Local<Value> PERMISSION_DENIED( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED)); - Nan::Set(status, Nan::New("PERMISSION_DENIED").ToLocalChecked(), - PERMISSION_DENIED); - Local<Value> UNAUTHENTICATED( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED)); - Nan::Set(status, Nan::New("UNAUTHENTICATED").ToLocalChecked(), - UNAUTHENTICATED); - Local<Value> RESOURCE_EXHAUSTED( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED)); - Nan::Set(status, Nan::New("RESOURCE_EXHAUSTED").ToLocalChecked(), - RESOURCE_EXHAUSTED); - Local<Value> FAILED_PRECONDITION( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION)); - Nan::Set(status, Nan::New("FAILED_PRECONDITION").ToLocalChecked(), - FAILED_PRECONDITION); - Local<Value> ABORTED(Nan::New<Uint32, uint32_t>(GRPC_STATUS_ABORTED)); - Nan::Set(status, Nan::New("ABORTED").ToLocalChecked(), ABORTED); - Local<Value> OUT_OF_RANGE( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE)); - Nan::Set(status, Nan::New("OUT_OF_RANGE").ToLocalChecked(), OUT_OF_RANGE); - Local<Value> UNIMPLEMENTED( - Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED)); - Nan::Set(status, Nan::New("UNIMPLEMENTED").ToLocalChecked(), UNIMPLEMENTED); - Local<Value> INTERNAL(Nan::New<Uint32, uint32_t>(GRPC_STATUS_INTERNAL)); - Nan::Set(status, Nan::New("INTERNAL").ToLocalChecked(), INTERNAL); - Local<Value> UNAVAILABLE(Nan::New<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE)); - Nan::Set(status, Nan::New("UNAVAILABLE").ToLocalChecked(), UNAVAILABLE); - Local<Value> DATA_LOSS(Nan::New<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS)); - Nan::Set(status, Nan::New("DATA_LOSS").ToLocalChecked(), DATA_LOSS); -} - -void InitCallErrorConstants(Local<Object> exports) { - Nan::HandleScope scope; - Local<Object> call_error = Nan::New<Object>(); - Nan::Set(exports, Nan::New("callError").ToLocalChecked(), call_error); - Local<Value> OK(Nan::New<Uint32, uint32_t>(GRPC_CALL_OK)); - Nan::Set(call_error, Nan::New("OK").ToLocalChecked(), OK); - Local<Value> CALL_ERROR(Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR)); - Nan::Set(call_error, Nan::New("ERROR").ToLocalChecked(), CALL_ERROR); - Local<Value> NOT_ON_SERVER( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER)); - Nan::Set(call_error, Nan::New("NOT_ON_SERVER").ToLocalChecked(), - NOT_ON_SERVER); - Local<Value> NOT_ON_CLIENT( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT)); - Nan::Set(call_error, Nan::New("NOT_ON_CLIENT").ToLocalChecked(), - NOT_ON_CLIENT); - Local<Value> ALREADY_INVOKED( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED)); - Nan::Set(call_error, Nan::New("ALREADY_INVOKED").ToLocalChecked(), - ALREADY_INVOKED); - Local<Value> NOT_INVOKED( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED)); - Nan::Set(call_error, Nan::New("NOT_INVOKED").ToLocalChecked(), NOT_INVOKED); - Local<Value> ALREADY_FINISHED( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED)); - Nan::Set(call_error, Nan::New("ALREADY_FINISHED").ToLocalChecked(), - ALREADY_FINISHED); - Local<Value> TOO_MANY_OPERATIONS( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS)); - Nan::Set(call_error, Nan::New("TOO_MANY_OPERATIONS").ToLocalChecked(), - TOO_MANY_OPERATIONS); - Local<Value> INVALID_FLAGS( - Nan::New<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS)); - Nan::Set(call_error, Nan::New("INVALID_FLAGS").ToLocalChecked(), - INVALID_FLAGS); -} - void InitOpTypeConstants(Local<Object> exports) { Nan::HandleScope scope; Local<Object> op_type = Nan::New<Object>(); @@ -211,27 +119,6 @@ void InitOpTypeConstants(Local<Object> exports) { RECV_CLOSE_ON_SERVER); } -void InitPropagateConstants(Local<Object> exports) { - Nan::HandleScope scope; - Local<Object> propagate = Nan::New<Object>(); - Nan::Set(exports, Nan::New("propagate").ToLocalChecked(), propagate); - Local<Value> DEADLINE(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEADLINE)); - Nan::Set(propagate, Nan::New("DEADLINE").ToLocalChecked(), DEADLINE); - Local<Value> CENSUS_STATS_CONTEXT( - Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)); - Nan::Set(propagate, Nan::New("CENSUS_STATS_CONTEXT").ToLocalChecked(), - CENSUS_STATS_CONTEXT); - Local<Value> CENSUS_TRACING_CONTEXT( - Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT)); - Nan::Set(propagate, Nan::New("CENSUS_TRACING_CONTEXT").ToLocalChecked(), - CENSUS_TRACING_CONTEXT); - Local<Value> CANCELLATION( - Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_CANCELLATION)); - Nan::Set(propagate, Nan::New("CANCELLATION").ToLocalChecked(), CANCELLATION); - Local<Value> DEFAULTS(Nan::New<Uint32, uint32_t>(GRPC_PROPAGATE_DEFAULTS)); - Nan::Set(propagate, Nan::New("DEFAULTS").ToLocalChecked(), DEFAULTS); -} - void InitConnectivityStateConstants(Local<Object> exports) { Nan::HandleScope scope; Local<Object> channel_state = Nan::New<Object>(); @@ -252,28 +139,6 @@ void InitConnectivityStateConstants(Local<Object> exports) { FATAL_FAILURE); } -void InitWriteFlags(Local<Object> exports) { - Nan::HandleScope scope; - Local<Object> write_flags = Nan::New<Object>(); - Nan::Set(exports, Nan::New("writeFlags").ToLocalChecked(), write_flags); - Local<Value> BUFFER_HINT(Nan::New<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT)); - Nan::Set(write_flags, Nan::New("BUFFER_HINT").ToLocalChecked(), BUFFER_HINT); - Local<Value> NO_COMPRESS(Nan::New<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS)); - Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS); -} - -void InitLogConstants(Local<Object> exports) { - Nan::HandleScope scope; - Local<Object> log_verbosity = Nan::New<Object>(); - Nan::Set(exports, Nan::New("logVerbosity").ToLocalChecked(), log_verbosity); - Local<Value> LOG_DEBUG(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_DEBUG)); - Nan::Set(log_verbosity, Nan::New("DEBUG").ToLocalChecked(), LOG_DEBUG); - Local<Value> LOG_INFO(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_INFO)); - Nan::Set(log_verbosity, Nan::New("INFO").ToLocalChecked(), LOG_INFO); - Local<Value> LOG_ERROR(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_ERROR)); - Nan::Set(log_verbosity, Nan::New("ERROR").ToLocalChecked(), LOG_ERROR); -} - NAN_METHOD(MetadataKeyIsLegal) { if (!info[0]->IsString()) { return Nan::ThrowTypeError("headerKeyIsLegal's argument must be a string"); @@ -421,13 +286,8 @@ void init(Local<Object> exports) { grpc_set_ssl_roots_override_callback(get_ssl_roots_override); init_logger(); - InitStatusConstants(exports); - InitCallErrorConstants(exports); InitOpTypeConstants(exports); - InitPropagateConstants(exports); InitConnectivityStateConstants(exports); - InitWriteFlags(exports); - InitLogConstants(exports); grpc_pollset_work_run_loop = 0; diff --git a/src/node/index.js b/src/node/index.js index 76ab1744b0..2da77c3eae 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -59,6 +59,8 @@ var grpc = require('./src/grpc_extension'); var protobuf_js_5_common = require('./src/protobuf_js_5_common'); var protobuf_js_6_common = require('./src/protobuf_js_6_common'); +var constants = require('./src/constants.js'); + grpc.setDefaultRootsPem(fs.readFileSync(SSL_ROOTS_PATH, 'ascii')); /** @@ -68,8 +70,6 @@ grpc.setDefaultRootsPem(fs.readFileSync(SSL_ROOTS_PATH, 'ascii')); * Buffers. Defaults to false * - longsAsStrings: deserialize long values as strings instead of objects. * Defaults to true - * - enumsAsStrings: deserialize enum values as strings instead of numbers. - * Defaults to true * - deprecatedArgumentOrder: Use the beta method argument order for client * methods, with optional arguments after the callback. Defaults to false. * This option is only a temporary stopgap measure to smooth an API breakage. @@ -103,10 +103,6 @@ exports.loadObject = function loadObject(value, options) { switch (protobufjsVersion) { case 6: return protobuf_js_6_common.loadObject(value, options); case 5: - var deprecation_message = 'Calling grpc.loadObject with an object ' + - 'generated by ProtoBuf.js 5 is deprecated. Please upgrade to ' + - 'ProtoBuf.js 6.'; - common.log(grpc.logVerbosity.INFO, deprecation_message); return protobuf_js_5_common.loadObject(value, options); default: throw new Error('Unrecognized protobufjsVersion', protobufjsVersion); @@ -115,19 +111,6 @@ exports.loadObject = function loadObject(value, options) { var loadObject = exports.loadObject; -function applyProtoRoot(filename, root) { - if (_.isString(filename)) { - return filename; - } - filename.root = path.resolve(filename.root) + '/'; - root.resolvePath = function(originPath, importPath, alreadyNormalized) { - return ProtoBuf.util.path.resolve(filename.root, - importPath, - alreadyNormalized); - }; - return filename.file; -} - /** * Load a gRPC object from a .proto file. The options object can provide the * following options: @@ -137,8 +120,6 @@ function applyProtoRoot(filename, root) { * Buffers. Defaults to false * - longsAsStrings: deserialize long values as strings instead of objects. * Defaults to true - * - enumsAsStrings: deserialize enum values as strings instead of numbers. - * Defaults to true * - deprecatedArgumentOrder: Use the beta method argument order for client * methods, with optional arguments after the callback. Defaults to false. * This option is only a temporary stopgap measure to smooth an API breakage. @@ -150,17 +131,31 @@ function applyProtoRoot(filename, root) { * @return {Object<string, *>} The resulting gRPC object */ exports.load = function load(filename, format, options) { - /* Note: format is currently unused, because the API for loading a proto - file or a JSON file is identical in Protobuf.js 6. In the future, there is - still the possibility of adding other formats that would be loaded - differently */ options = _.defaults(options, common.defaultGrpcOptions); - options.protobufjs_version = 6; - var root = new ProtoBuf.Root(); - var parse_options = {keepCase: !options.convertFieldsToCamelCase}; - return loadObject(root.loadSync(applyProtoRoot(filename, root), - parse_options), - options); + options.protobufjsVersion = 5; + if (!format) { + format = 'proto'; + } + var convertFieldsToCamelCaseOriginal = ProtoBuf.convertFieldsToCamelCase; + if(options && options.hasOwnProperty('convertFieldsToCamelCase')) { + ProtoBuf.convertFieldsToCamelCase = options.convertFieldsToCamelCase; + } + var builder; + try { + switch(format) { + case 'proto': + builder = ProtoBuf.loadProtoFile(filename); + break; + case 'json': + builder = ProtoBuf.loadJsonFile(filename); + break; + default: + throw new Error('Unrecognized format "' + format + '"'); + } + } finally { + ProtoBuf.convertFieldsToCamelCase = convertFieldsToCamelCaseOriginal; + } + return loadObject(builder.ns, options); }; var log_template = _.template( @@ -212,27 +207,27 @@ exports.Metadata = Metadata; /** * Status name to code number mapping */ -exports.status = grpc.status; +exports.status = constants.status; /** * Propagate flag name to number mapping */ -exports.propagate = grpc.propagate; +exports.propagate = constants.propagate; /** * Call error name to code number mapping */ -exports.callError = grpc.callError; +exports.callError = constants.callError; /** * Write flag name to code number mapping */ -exports.writeFlags = grpc.writeFlags; +exports.writeFlags = constants.writeFlags; /** * Log verbosity setting name to code number mapping */ -exports.logVerbosity = grpc.logVerbosity; +exports.logVerbosity = constants.logVerbosity; /** * Credentials factories diff --git a/src/node/jsdoc_conf.json b/src/node/jsdoc_conf.json index c3a0174f0e..2d967753c1 100644 --- a/src/node/jsdoc_conf.json +++ b/src/node/jsdoc_conf.json @@ -11,7 +11,7 @@ "package": "package.json", "readme": "src/node/README.md" }, - "plugins": [], + "plugins": ["plugins/markdown"], "templates": { "cleverLinks": false, "monospaceLinks": false, diff --git a/src/node/src/client.js b/src/node/src/client.js index 43502da6af..16fe06a54d 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -58,6 +58,8 @@ var common = require('./common'); var Metadata = require('./metadata'); +var constants = require('./constants'); + var EventEmitter = require('events').EventEmitter; var stream = require('stream'); @@ -127,7 +129,8 @@ function _write(chunk, encoding, callback) { but passing an object that causes a serialization failure is a misuse of the API anyway, so that's OK. The primary purpose here is to give the programmer a useful error and to stop the stream properly */ - this.call.cancelWithStatus(grpc.status.INTERNAL, 'Serialization failure'); + this.call.cancelWithStatus(constants.status.INTERNAL, + 'Serialization failure'); callback(e); } if (_.isFinite(encoding)) { @@ -185,9 +188,9 @@ function ClientReadableStream(call, deserialize) { function _readsDone(status) { /* jshint validthis: true */ if (!status) { - status = {code: grpc.status.OK, details: 'OK'}; + status = {code: constants.status.OK, details: 'OK'}; } - if (status.code !== grpc.status.OK) { + if (status.code !== constants.status.OK) { this.call.cancelWithStatus(status.code, status.details); } this.finished = true; @@ -218,12 +221,12 @@ function _emitStatusIfDone() { /* jshint validthis: true */ var status; if (this.read_status && this.received_status) { - if (this.read_status.code !== grpc.status.OK) { + if (this.read_status.code !== constants.status.OK) { status = this.read_status; } else { status = this.received_status; } - if (status.code === grpc.status.OK) { + if (status.code === constants.status.OK) { this.push(null); } else { var error = new Error(status.details); @@ -262,7 +265,7 @@ function _read(size) { try { deserialized = self.deserialize(data); } catch (e) { - self._readsDone({code: grpc.status.INTERNAL, + self._readsDone({code: constants.status.INTERNAL, details: 'Failed to parse server response'}); return; } @@ -510,7 +513,7 @@ Client.prototype.makeUnaryRequest = function(method, serialize, deserialize, var deserialized; emitter.emit('metadata', Metadata._fromCoreRepresentation( response.metadata)); - if (status.code === grpc.status.OK) { + if (status.code === constants.status.OK) { if (err) { // Got a batch error, but OK status. Something went wrong args.callback(err); @@ -522,13 +525,13 @@ Client.prototype.makeUnaryRequest = function(method, serialize, deserialize, /* Change status to indicate bad server response. This will result * in passing an error to the callback */ status = { - code: grpc.status.INTERNAL, + code: constants.status.INTERNAL, details: 'Failed to parse server response' }; } } } - if (status.code !== grpc.status.OK) { + if (status.code !== constants.status.OK) { error = new Error(status.details); error.code = status.code; error.metadata = status.metadata; @@ -593,7 +596,7 @@ Client.prototype.makeClientStreamRequest = function(method, serialize, var status = response.status; var error; var deserialized; - if (status.code === grpc.status.OK) { + if (status.code === constants.status.OK) { if (err) { // Got a batch error, but OK status. Something went wrong args.callback(err); @@ -605,13 +608,13 @@ Client.prototype.makeClientStreamRequest = function(method, serialize, /* Change status to indicate bad server response. This will result * in passing an error to the callback */ status = { - code: grpc.status.INTERNAL, + code: constants.status.INTERNAL, details: 'Failed to parse server response' }; } } } - if (status.code !== grpc.status.OK) { + if (status.code !== constants.status.OK) { error = new Error(response.status.details); error.code = status.code; error.metadata = status.metadata; @@ -921,7 +924,7 @@ exports.waitForClientReady = function(client, deadline, callback) { /** * Map of status code names to status codes */ -exports.status = grpc.status; +exports.status = constants.status; /** * See docs for client.callError diff --git a/src/node/src/constants.js b/src/node/src/constants.js new file mode 100644 index 0000000000..528dab120e --- /dev/null +++ b/src/node/src/constants.js @@ -0,0 +1,241 @@ +/* + * + * Copyright 2017, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @module + */ + +/* The comments about status codes are copied verbatim (with some formatting + * modifications) from include/grpc/impl/codegen/status.h, for the purpose of + * including them in generated documentation. + */ +/** + * Enum of status codes that gRPC can return + * @readonly + * @enum {number} + */ +exports.status = { + /** Not an error; returned on success */ + OK: 0, + /** The operation was cancelled (typically by the caller). */ + CANCELLED: 1, + /** + * Unknown error. An example of where this error may be returned is + * if a status value received from another address space belongs to + * an error-space that is not known in this address space. Also + * errors raised by APIs that do not return enough error information + * may be converted to this error. + */ + UNKNOWN: 2, + /** + * Client specified an invalid argument. Note that this differs + * from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + * that are problematic regardless of the state of the system + * (e.g., a malformed file name). + */ + INVALID_ARGUMENT: 3, + /** + * Deadline expired before operation could complete. For operations + * that change the state of the system, this error may be returned + * even if the operation has completed successfully. For example, a + * successful response from a server could have been delayed long + * enough for the deadline to expire. + */ + DEADLINE_EXCEEDED: 4, + /** Some requested entity (e.g., file or directory) was not found. */ + NOT_FOUND: 5, + /** + * Some entity that we attempted to create (e.g., file or directory) + * already exists. + */ + ALREADY_EXISTS: 6, + /** + * The caller does not have permission to execute the specified + * operation. PERMISSION_DENIED must not be used for rejections + * caused by exhausting some resource (use RESOURCE_EXHAUSTED + * instead for those errors). PERMISSION_DENIED must not be + * used if the caller can not be identified (use UNAUTHENTICATED + * instead for those errors). + */ + PERMISSION_DENIED: 7, + /** + * Some resource has been exhausted, perhaps a per-user quota, or + * perhaps the entire file system is out of space. + */ + RESOURCE_EXHAUSTED: 8, + /** + * Operation was rejected because the system is not in a state + * required for the operation's execution. For example, directory + * to be deleted may be non-empty, an rmdir operation is applied to + * a non-directory, etc. + * + * A litmus test that may help a service implementor in deciding + * between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + * + * - Use UNAVAILABLE if the client can retry just the failing call. + * - Use ABORTED if the client should retry at a higher-level + * (e.g., restarting a read-modify-write sequence). + * - Use FAILED_PRECONDITION if the client should not retry until + * the system state has been explicitly fixed. E.g., if an "rmdir" + * fails because the directory is non-empty, FAILED_PRECONDITION + * should be returned since the client should not retry unless + * they have first fixed up the directory by deleting files from it. + * - Use FAILED_PRECONDITION if the client performs conditional + * REST Get/Update/Delete on a resource and the resource on the + * server does not match the condition. E.g., conflicting + * read-modify-write on the same resource. + */ + FAILED_PRECONDITION: 9, + /** + * The operation was aborted, typically due to a concurrency issue + * like sequencer check failures, transaction aborts, etc. + * + * See litmus test above for deciding between FAILED_PRECONDITION, + * ABORTED, and UNAVAILABLE. + */ + ABORTED: 10, + /** + * Operation was attempted past the valid range. E.g., seeking or + * reading past end of file. + * + * Unlike INVALID_ARGUMENT, this error indicates a problem that may + * be fixed if the system state changes. For example, a 32-bit file + * system will generate INVALID_ARGUMENT if asked to read at an + * offset that is not in the range [0,2^32-1], but it will generate + * OUT_OF_RANGE if asked to read from an offset past the current + * file size. + * + * There is a fair bit of overlap between FAILED_PRECONDITION and + * OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific + * error) when it applies so that callers who are iterating through + * a space can easily look for an OUT_OF_RANGE error to detect when + * they are done. + */ + OUT_OF_RANGE: 11, + /** Operation is not implemented or not supported/enabled in this service. */ + UNIMPLEMENTED: 12, + /** + * Internal errors. Means some invariants expected by underlying + * system has been broken. If you see one of these errors, + * something is very broken. + */ + INTERNAL: 13, + /** + * The service is currently unavailable. This is a most likely a + * transient condition and may be corrected by retrying with + * a backoff. + * + * See litmus test above for deciding between FAILED_PRECONDITION, + * ABORTED, and UNAVAILABLE. */ + UNAVAILABLE: 14, + /** Unrecoverable data loss or corruption. */ + DATA_LOSS: 15, + /** + * The request does not have valid authentication credentials for the + * operation. + */ + UNAUTHENTICATED: 16 +}; + +/* The comments about propagation bit flags are copied rom + * include/grpc/impl/codegen/propagation_bits.h for the purpose of including + * them in generated documentation. + */ +/** + * Propagation flags: these can be bitwise or-ed to form the propagation option + * for calls. + * + * Users are encouraged to write propagation masks as deltas from the default. + * i.e. write `grpc.propagate.DEFAULTS & ~grpc.propagate.DEADLINE` to disable + * deadline propagation. + * @enum {number} + */ +exports.propagate = { + DEADLINE: 1, + CENSUS_STATS_CONTEXT: 2, + CENSUS_TRACING_CONTEXT: 4, + CANCELLATION: 8, + DEFAULTS: 65535 +}; + +/* Many of the following comments are copied from + * include/grpc/impl/codegen/grpc_types.h + */ +/** + * Call error constants. Call errors almost always indicate bugs in the gRPC + * library, and these error codes are mainly useful for finding those bugs. + * @enum {number} + */ +exports.callError = { + OK: 0, + ERROR: 1, + NOT_ON_SERVER: 2, + NOT_ON_CLIENT: 3, + ALREADY_INVOKED: 5, + NOT_INVOKED: 6, + ALREADY_FINISHED: 7, + TOO_MANY_OPERATIONS: 8, + INVALID_FLAGS: 9, + INVALID_METADATA: 10, + INVALID_MESSAGE: 11, + NOT_SERVER_COMPLETION_QUEUE: 12, + BATCH_TOO_BIG: 13, + PAYLOAD_TYPE_MISMATCH: 14 +}; + +/** + * Write flags: these can be bitwise or-ed to form write options that modify + * how data is written. + * @enum {number} + */ +exports.writeFlags = { + /** + * Hint that the write may be buffered and need not go out on the wire + * immediately. GRPC is free to buffer the message until the next non-buffered + * write, or until writes_done, but it need not buffer completely or at all. + */ + BUFFER_HINT: 1, + /** + * Force compression to be disabled for a particular write + */ + NO_COMPRESS: 2 +}; + +/** + * @enum {number} + */ +exports.logVerbosity = { + DEBUG: 0, + INFO: 1, + ERROR: 2 +}; diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js index 51ff1da01e..b1e86bbd09 100644 --- a/src/node/src/credentials.js +++ b/src/node/src/credentials.js @@ -71,6 +71,8 @@ var Metadata = require('./metadata.js'); var common = require('./common.js'); +var constants = require('./constants'); + var _ = require('lodash'); /** @@ -97,14 +99,14 @@ exports.createFromMetadataGenerator = function(metadata_generator) { return CallCredentials.createFromPlugin(function(service_url, cb_data, callback) { metadata_generator({service_url: service_url}, function(error, metadata) { - var code = grpc.status.OK; + var code = constants.status.OK; var message = ''; if (error) { message = error.message; if (error.hasOwnProperty('code') && _.isFinite(error.code)) { code = error.code; } else { - code = grpc.status.UNAUTHENTICATED; + code = constants.status.UNAUTHENTICATED; } if (!metadata) { metadata = new Metadata(); @@ -125,7 +127,7 @@ exports.createFromGoogleCredential = function(google_credential) { var service_url = auth_context.service_url; google_credential.getRequestMetadata(service_url, function(err, header) { if (err) { - common.log(grpc.logVerbosity.INFO, 'Auth error:' + err); + common.log(constants.logVerbosity.INFO, 'Auth error:' + err); callback(err); return; } diff --git a/src/node/src/protobuf_js_5_common.js b/src/node/src/protobuf_js_5_common.js index 62cf2f4aca..4041e05390 100644 --- a/src/node/src/protobuf_js_5_common.js +++ b/src/node/src/protobuf_js_5_common.js @@ -45,8 +45,7 @@ var client = require('./client'); * objects. Defaults to true * @return {function(Buffer):cls} The deserialization function */ -exports.deserializeCls = function deserializeCls(cls, binaryAsBase64, - longsAsStrings) { +exports.deserializeCls = function deserializeCls(cls, options) { /** * Deserialize a buffer to a message object * @param {Buffer} arg_buf The buffer to deserialize @@ -55,7 +54,8 @@ exports.deserializeCls = function deserializeCls(cls, binaryAsBase64, return function deserialize(arg_buf) { // Convert to a native object with binary fields as Buffers (first argument) // and longs as strings (second argument) - return cls.decode(arg_buf).toRaw(binaryAsBase64, longsAsStrings); + return cls.decode(arg_buf).toRaw(options.binaryAsBase64, + options.longsAsStrings); }; }; @@ -128,10 +128,10 @@ exports.getProtobufServiceAttrs = function getProtobufServiceAttrs(service, responseType: method.resolvedResponseType, requestSerialize: serializeCls(method.resolvedRequestType.build()), requestDeserialize: deserializeCls(method.resolvedRequestType.build(), - binaryAsBase64, longsAsStrings), + options), responseSerialize: serializeCls(method.resolvedResponseType.build()), responseDeserialize: deserializeCls(method.resolvedResponseType.build(), - binaryAsBase64, longsAsStrings) + options) }; })); }; diff --git a/src/node/src/server.js b/src/node/src/server.js index 3450abed08..1d9cc7d2c1 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -57,6 +57,8 @@ var common = require('./common'); var Metadata = require('./metadata'); +var constants = require('./constants'); + var stream = require('stream'); var Readable = stream.Readable; @@ -75,7 +77,7 @@ var EventEmitter = require('events').EventEmitter; function handleError(call, error) { var statusMetadata = new Metadata(); var status = { - code: grpc.status.UNKNOWN, + code: constants.status.UNKNOWN, details: 'Unknown Error' }; if (error.hasOwnProperty('message')) { @@ -115,7 +117,7 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { var end_batch = {}; var statusMetadata = new Metadata(); var status = { - code: grpc.status.OK, + code: constants.status.OK, details: 'OK' }; if (metadata) { @@ -125,7 +127,7 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { try { message = serialize(value); } catch (e) { - e.code = grpc.status.INTERNAL; + e.code = constants.status.INTERNAL; handleError(call, e); return; } @@ -151,7 +153,7 @@ function sendUnaryResponse(call, value, serialize, metadata, flags) { function setUpWritable(stream, serialize) { stream.finished = false; stream.status = { - code : grpc.status.OK, + code : constants.status.OK, details : 'OK', metadata : new Metadata() }; @@ -178,7 +180,7 @@ function setUpWritable(stream, serialize) { * @param {Error} err The error object */ function setStatus(err) { - var code = grpc.status.UNKNOWN; + var code = constants.status.UNKNOWN; var details = 'Unknown Error'; var metadata = new Metadata(); if (err.hasOwnProperty('message')) { @@ -284,7 +286,7 @@ function _write(chunk, encoding, callback) { try { message = this.serialize(chunk); } catch (e) { - e.code = grpc.status.INTERNAL; + e.code = constants.status.INTERNAL; callback(e); return; } @@ -353,7 +355,7 @@ function _read(size) { try { deserialized = self.deserialize(data); } catch (e) { - e.code = grpc.status.INTERNAL; + e.code = constants.status.INTERNAL; self.emit('error', e); return; } @@ -489,7 +491,7 @@ function handleUnary(call, handler, metadata) { try { emitter.request = handler.deserialize(result.read); } catch (e) { - e.code = grpc.status.INTERNAL; + e.code = constants.status.INTERNAL; handleError(call, e); return; } @@ -530,7 +532,7 @@ function handleServerStreaming(call, handler, metadata) { try { stream.request = handler.deserialize(result.read); } catch (e) { - e.code = grpc.status.INTERNAL; + e.code = constants.status.INTERNAL; stream.emit('error', e); return; } @@ -636,7 +638,7 @@ function Server(options) { batch[grpc.opType.SEND_INITIAL_METADATA] = (new Metadata())._getCoreRepresentation(); batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { - code: grpc.status.UNIMPLEMENTED, + code: constants.status.UNIMPLEMENTED, details: '', metadata: {} }; @@ -699,7 +701,7 @@ Server.prototype.register = function(name, handler, serialize, deserialize, }; var unimplementedStatusResponse = { - code: grpc.status.UNIMPLEMENTED, + code: constants.status.UNIMPLEMENTED, details: 'The server does not implement this method' }; @@ -759,8 +761,8 @@ Server.prototype.addService = function(service, implementation) { written in the proto file, instead of using JavaScript function naming style */ if (implementation[attrs.originalName] === undefined) { - common.log(grpc.logVerbosity.ERROR, 'Method handler ' + name + ' for ' + - attrs.path + ' expected but not provided'); + common.log(constants.logVerbosity.ERROR, 'Method handler ' + name + + ' for ' + attrs.path + ' expected but not provided'); impl = defaultHandler[method_type]; } else { impl = _.bind(implementation[attrs.originalName], implementation); @@ -779,6 +781,11 @@ Server.prototype.addService = function(service, implementation) { }); }; +var logAddProtoServiceDeprecationOnce = _.once(function() { + common.log(constants.logVerbosity.INFO, + 'Server#addProtoService is deprecated. Use addService instead'); +}); + /** * Add a proto service to the server, with a corresponding implementation * @deprecated Use grpc.load and Server#addService instead @@ -790,8 +797,7 @@ Server.prototype.addProtoService = function(service, implementation) { var options; var protobuf_js_5_common = require('./protobuf_js_5_common'); var protobuf_js_6_common = require('./protobuf_js_6_common'); - common.log(grpc.logVerbosity.INFO, - 'Server#addProtoService is deprecated. Use addService instead'); + logAddProtoServiceDeprecationOnce(); if (protobuf_js_5_common.isProbablyProtobufJs5(service)) { options = _.defaults(service.grpc_options, common.defaultGrpcOptions); this.addService( diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js index eb268603ea..f25268e8e6 100644 --- a/src/node/test/call_test.js +++ b/src/node/test/call_test.js @@ -35,6 +35,7 @@ var assert = require('assert'); var grpc = require('../src/grpc_extension'); +var constants = require('../src/constants'); /** * Helper function to return an absolute deadline given a relative timeout in @@ -120,7 +121,8 @@ describe('call', function() { var batch = {}; batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; call.startBatch(batch, function(err, response) { - assert.strictEqual(response.status.code, grpc.status.DEADLINE_EXCEEDED); + assert.strictEqual(response.status.code, + constants.status.DEADLINE_EXCEEDED); done(); }); }); diff --git a/src/node/test/common_test.js b/src/node/test/common_test.js index e1ce864f97..b7c2c6a8d6 100644 --- a/src/node/test/common_test.js +++ b/src/node/test/common_test.js @@ -37,16 +37,15 @@ var assert = require('assert'); var _ = require('lodash'); var common = require('../src/common'); -var protobuf_js_6_common = require('../src/protobuf_js_6_common'); +var protobuf_js_5_common = require('../src/protobuf_js_5_common'); -var serializeCls = protobuf_js_6_common.serializeCls; -var deserializeCls = protobuf_js_6_common.deserializeCls; +var serializeCls = protobuf_js_5_common.serializeCls; +var deserializeCls = protobuf_js_5_common.deserializeCls; var ProtoBuf = require('protobufjs'); -var messages_proto = new ProtoBuf.Root(); -messages_proto = messages_proto.loadSync( - __dirname + '/test_messages.proto', {keepCase: true}).resolveAll(); +var messages_proto = ProtoBuf.loadProtoFile( + __dirname + '/test_messages.proto').build(); var default_options = common.defaultGrpcOptions; @@ -101,6 +100,7 @@ describe('Proto message long int serialize and deserialize', function() { var longNumDeserialize = deserializeCls(messages_proto.LongValues, num_options); var serialized = longSerialize({int_64: pos_value}); + console.log(longDeserialize(serialized)); assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string'); /* With the longsAsStrings option disabled, long values are represented as * objects with 3 keys: low, high, and unsigned */ @@ -136,7 +136,8 @@ describe('Proto message bytes serialize and deserialize', function() { var serialized = sequenceSerialize({repeated_field: [10]}); assert.strictEqual(expected_serialize.compare(serialized), 0); }); - it('should deserialize packed or unpacked repeated', function() { + // This tests a bug that was fixed in Protobuf.js 6 + it.skip('should deserialize packed or unpacked repeated', function() { var expectedDeserialize = { bytes_field: new Buffer(''), repeated_field: [10] @@ -155,7 +156,8 @@ describe('Proto message bytes serialize and deserialize', function() { assert.deepEqual(unpackedDeserialized, expectedDeserialize); }); }); -describe('Proto message oneof serialize and deserialize', function() { +// This tests a bug that was fixed in Protobuf.js 6 +describe.skip('Proto message oneof serialize and deserialize', function() { var oneofSerialize = serializeCls(messages_proto.OneOfValues); var oneofDeserialize = deserializeCls( messages_proto.OneOfValues, default_options); @@ -193,7 +195,8 @@ describe('Proto message enum serialize and deserialize', function() { assert.deepEqual(enumDeserialize(nameSerialized), enumDeserialize(numberSerialized)); }); - it('Should deserialize as a string the enumsAsStrings option', function() { + // This tests a bug that was fixed in Protobuf.js 6 + it.skip('Should correctly handle the enumsAsStrings option', function() { var serialized = enumSerialize({enum_value: 'TWO'}); var nameDeserialized = enumDeserialize(serialized); var numberDeserialized = enumIntDeserialize(serialized); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js deleted file mode 100644 index 414b1ac9c0..0000000000 --- a/src/node/test/constant_test.js +++ /dev/null @@ -1,131 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -'use strict'; - -var assert = require('assert'); -var grpc = require('../src/grpc_extension'); - -/** - * List of all status names - * @const - * @type {Array.<string>} - */ -var statusNames = [ - 'OK', - 'CANCELLED', - 'UNKNOWN', - 'INVALID_ARGUMENT', - 'DEADLINE_EXCEEDED', - 'NOT_FOUND', - 'ALREADY_EXISTS', - 'PERMISSION_DENIED', - 'UNAUTHENTICATED', - 'RESOURCE_EXHAUSTED', - 'FAILED_PRECONDITION', - 'ABORTED', - 'OUT_OF_RANGE', - 'UNIMPLEMENTED', - 'INTERNAL', - 'UNAVAILABLE', - 'DATA_LOSS' -]; - -/** - * List of all call error names - * @const - * @type {Array.<string>} - */ -var callErrorNames = [ - 'OK', - 'ERROR', - 'NOT_ON_SERVER', - 'NOT_ON_CLIENT', - 'ALREADY_INVOKED', - 'NOT_INVOKED', - 'ALREADY_FINISHED', - 'TOO_MANY_OPERATIONS', - 'INVALID_FLAGS' -]; - -/** - * List of all propagate flag names - * @const - * @type {Array.<string>} - */ -var propagateFlagNames = [ - 'DEADLINE', - 'CENSUS_STATS_CONTEXT', - 'CENSUS_TRACING_CONTEXT', - 'CANCELLATION', - 'DEFAULTS' -]; -/* - * List of all connectivity state names - * @const - * @type {Array.<string>} - */ -var connectivityStateNames = [ - 'IDLE', - 'CONNECTING', - 'READY', - 'TRANSIENT_FAILURE', - 'FATAL_FAILURE' -]; - -describe('constants', function() { - it('should have all of the status constants', function() { - for (var i = 0; i < statusNames.length; i++) { - assert(grpc.status.hasOwnProperty(statusNames[i]), - 'status missing: ' + statusNames[i]); - } - }); - it('should have all of the call errors', function() { - for (var i = 0; i < callErrorNames.length; i++) { - assert(grpc.callError.hasOwnProperty(callErrorNames[i]), - 'call error missing: ' + callErrorNames[i]); - } - }); - it('should have all of the propagate flags', function() { - for (var i = 0; i < propagateFlagNames.length; i++) { - assert(grpc.propagate.hasOwnProperty(propagateFlagNames[i]), - 'call error missing: ' + propagateFlagNames[i]); - } - }); - it('should have all of the connectivity states', function() { - for (var i = 0; i < connectivityStateNames.length; i++) { - assert(grpc.connectivityState.hasOwnProperty(connectivityStateNames[i]), - 'connectivity status missing: ' + connectivityStateNames[i]); - } - }); -}); diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js index f127a41de9..af455e2716 100644 --- a/src/node/test/end_to_end_test.js +++ b/src/node/test/end_to_end_test.js @@ -35,6 +35,7 @@ var assert = require('assert'); var grpc = require('../src/grpc_extension'); +var constants = require('../src/constants'); /** * This is used for testing functions with multiple asynchronous calls that @@ -90,7 +91,7 @@ describe('end-to-end', function() { client_close: true, metadata: {}, status: { - code: grpc.status.OK, + code: constants.status.OK, details: status_text, metadata: {} } @@ -107,7 +108,7 @@ describe('end-to-end', function() { server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { metadata: {}, - code: grpc.status.OK, + code: constants.status.OK, details: status_text }; server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; @@ -141,7 +142,7 @@ describe('end-to-end', function() { send_metadata: true, client_close: true, metadata: {server_key: ['server_value']}, - status: {code: grpc.status.OK, + status: {code: constants.status.OK, details: status_text, metadata: {}} }); @@ -161,7 +162,7 @@ describe('end-to-end', function() { }; server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { metadata: {}, - code: grpc.status.OK, + code: constants.status.OK, details: status_text }; server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; @@ -198,7 +199,7 @@ describe('end-to-end', function() { assert.deepEqual(response.metadata, {}); assert(response.send_message); assert.strictEqual(response.read.toString(), reply_text); - assert.deepEqual(response.status, {code: grpc.status.OK, + assert.deepEqual(response.status, {code: constants.status.OK, details: status_text, metadata: {}}); done(); @@ -220,7 +221,7 @@ describe('end-to-end', function() { response_batch[grpc.opType.SEND_MESSAGE] = new Buffer(reply_text); response_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { metadata: {}, - code: grpc.status.OK, + code: constants.status.OK, details: status_text }; response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; @@ -260,7 +261,7 @@ describe('end-to-end', function() { send_message: true, client_close: true, status: { - code: grpc.status.OK, + code: constants.status.OK, details: status_text, metadata: {} } @@ -290,7 +291,7 @@ describe('end-to-end', function() { end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { metadata: {}, - code: grpc.status.OK, + code: constants.status.OK, details: status_text }; server_call.startBatch(end_batch, function(err, response) { diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index 783028fa99..d2f0511af2 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -43,9 +43,8 @@ var ProtoBuf = require('protobufjs'); var grpc = require('..'); -var math_proto = new ProtoBuf.Root(); -math_proto = math_proto.loadSync(__dirname + - '/../../proto/math/math.proto', {keepCase: true}); +var math_proto = ProtoBuf.loadProtoFile(__dirname + + '/../../proto/math/math.proto'); var mathService = math_proto.lookup('math.Math'); var mathServiceAttrs = grpc.loadObject( @@ -332,9 +331,7 @@ describe('Echo service', function() { var server; var client; before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/echo_service.proto', - {keepCase: true}); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/echo_service.proto'); var echo_service = test_proto.lookup('EchoService'); var Client = grpc.loadObject(echo_service); server = new grpc.Server(); @@ -357,6 +354,13 @@ describe('Echo service', function() { done(); }); }); + it('Should convert an undefined argument to default values', function(done) { + client.echo(undefined, function(error, response) { + assert.ifError(error); + assert.deepEqual(response, {value: '', value2: 0}); + done(); + }); + }); }); describe('Generic client and server', function() { function toString(val) { @@ -457,9 +461,7 @@ describe('Echo metadata', function() { var server; var metadata; before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/test_service.proto', - {keepCase: true}); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); var test_service = test_proto.lookup('TestService'); var Client = grpc.loadObject(test_service); server = new grpc.Server(); @@ -560,9 +562,7 @@ describe('Client malformed response handling', function() { var client; var badArg = new Buffer([0xFF]); before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/test_service.proto', - {keepCase: true}); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); var test_service = test_proto.lookup('TestService'); var malformed_test_service = { unary: { @@ -669,9 +669,7 @@ describe('Server serialization failure handling', function() { var client; var server; before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/test_service.proto', - {keepCase: true}); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); var test_service = test_proto.lookup('TestService'); var malformed_test_service = { unary: { @@ -772,16 +770,13 @@ describe('Server serialization failure handling', function() { }); }); describe('Other conditions', function() { - var test_service; var Client; var client; var server; var port; before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/test_service.proto', - {keepCase: true}); - test_service = test_proto.lookup('TestService'); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + var test_service = test_proto.lookup('TestService'); Client = grpc.loadObject(test_service); server = new grpc.Server(); var trailer_metadata = new grpc.Metadata(); @@ -1121,15 +1116,12 @@ describe('Call propagation', function() { var proxy; var proxy_impl; - var test_service; var Client; var client; var server; before(function() { - var test_proto = new ProtoBuf.Root(); - test_proto = test_proto.loadSync(__dirname + '/test_service.proto', - {keepCase: true}); - test_service = test_proto.lookup('TestService'); + var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto'); + var test_service = test_proto.lookup('TestService'); server = new grpc.Server(); Client = grpc.loadObject(test_service); server.addService(Client.service, { diff --git a/src/php/README.md b/src/php/README.md index f9f93ba815..90c8cb386a 100644 --- a/src/php/README.md +++ b/src/php/README.md @@ -174,6 +174,28 @@ $ sudo make install ``` +### Protobuf Runtime library + +There are two protobuf runtime libraries to choose from. They are idenfical in terms of APIs offered. + +1. C implementation (for better performance) + +``` sh +$ sudo pecl install protobuf +``` + +2. PHP implementation (for easier installation) + + +Add this to your `composer.json` file: + +``` + "require": { + "google/protobuf": "^v3.3.0" + } +``` + + ### PHP Protoc Plugin You need the gRPC PHP protoc plugin to generate the client stub classes. diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index d3fd88416b..94e0b73a49 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -125,7 +125,12 @@ zval *grpc_parse_metadata_array(grpc_metadata_array php_grpc_add_next_index_stringl(inner_array, str_val, GRPC_SLICE_LENGTH(elem->value), false); add_assoc_zval(array, str_key, inner_array); + PHP_GRPC_FREE_STD_ZVAL(inner_array); } + efree(str_key); +#if PHP_MAJOR_VERSION >= 7 + efree(str_val); +#endif } return array; } @@ -256,8 +261,6 @@ PHP_METHOD(Call, startBatch) { object_init(result); php_grpc_ulong index; zval *recv_status; - PHP_GRPC_MAKE_STD_ZVAL(recv_status); - object_init(recv_status); zval *value; zval *inner_value; zval *message_value; @@ -439,7 +442,7 @@ PHP_METHOD(Call, startBatch) { grpc_completion_queue_pluck(completion_queue, call->wrapped, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); #if PHP_MAJOR_VERSION >= 7 - zval recv_md; + zval *recv_md; #endif for (int i = 0; i < op_num; i++) { switch(ops[i].op) { @@ -460,8 +463,10 @@ PHP_METHOD(Call, startBatch) { array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC); add_property_zval(result, "metadata", array); #else - recv_md = *grpc_parse_metadata_array(&recv_metadata); - add_property_zval(result, "metadata", &recv_md); + recv_md = grpc_parse_metadata_array(&recv_metadata); + add_property_zval(result, "metadata", recv_md); + zval_ptr_dtor(recv_md); + PHP_GRPC_FREE_STD_ZVAL(recv_md); #endif PHP_GRPC_DELREF(array); break; @@ -475,12 +480,16 @@ PHP_METHOD(Call, startBatch) { } break; case GRPC_OP_RECV_STATUS_ON_CLIENT: + PHP_GRPC_MAKE_STD_ZVAL(recv_status); + object_init(recv_status); #if PHP_MAJOR_VERSION < 7 array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC); add_property_zval(recv_status, "metadata", array); #else - recv_md = *grpc_parse_metadata_array(&recv_trailing_metadata); - add_property_zval(recv_status, "metadata", &recv_md); + recv_md = grpc_parse_metadata_array(&recv_trailing_metadata); + add_property_zval(recv_status, "metadata", recv_md); + zval_ptr_dtor(recv_md); + PHP_GRPC_FREE_STD_ZVAL(recv_md); #endif PHP_GRPC_DELREF(array); add_property_long(recv_status, "code", status); @@ -489,6 +498,9 @@ PHP_METHOD(Call, startBatch) { true); gpr_free(status_details_text); add_property_zval(result, "status", recv_status); +#if PHP_MAJOR_VERSION >= 7 + zval_ptr_dtor(recv_status); +#endif PHP_GRPC_DELREF(recv_status); PHP_GRPC_FREE_STD_ZVAL(recv_status); break; diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index 625c0c62ae..da7100f24a 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -172,34 +172,54 @@ void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context, object_init(arg); php_grpc_add_property_string(arg, "service_url", context.service_url, true); php_grpc_add_property_string(arg, "method_name", context.method_name, true); - zval *retval; - PHP_GRPC_MAKE_STD_ZVAL(retval); + zval *retval = NULL; #if PHP_MAJOR_VERSION < 7 zval **params[1]; params[0] = &arg; state->fci->params = params; state->fci->retval_ptr_ptr = &retval; #else + PHP_GRPC_MAKE_STD_ZVAL(retval); state->fci->params = arg; state->fci->retval = retval; #endif state->fci->param_count = 1; + PHP_GRPC_DELREF(arg); + /* call the user callback function */ zend_call_function(state->fci, state->fci_cache TSRMLS_CC); grpc_status_code code = GRPC_STATUS_OK; grpc_metadata_array metadata; + bool cleanup = true; if (Z_TYPE_P(retval) != IS_ARRAY) { + cleanup = false; code = GRPC_STATUS_INVALID_ARGUMENT; } else if (!create_metadata_array(retval, &metadata)) { - grpc_metadata_array_destroy(&metadata); code = GRPC_STATUS_INVALID_ARGUMENT; } + if (retval != NULL) { +#if PHP_MAJOR_VERSION < 7 + zval_ptr_dtor(&retval); +#else + zval_ptr_dtor(arg); + zval_ptr_dtor(retval); + PHP_GRPC_FREE_STD_ZVAL(arg); + PHP_GRPC_FREE_STD_ZVAL(retval); +#endif + } + /* Pass control back to core */ cb(user_data, metadata.metadata, metadata.count, code, NULL); + if (cleanup) { + for (int i = 0; i < metadata.count; i++) { + grpc_slice_unref(metadata.metadata[i].value); + } + grpc_metadata_array_destroy(&metadata); + } } /* Cleanup function for plugin creds API */ @@ -207,8 +227,10 @@ void plugin_destroy_state(void *ptr) { plugin_state *state = (plugin_state *)ptr; efree(state->fci); efree(state->fci_cache); +#if PHP_MAJOR_VERSION < 7 PHP_GRPC_FREE_STD_ZVAL(state->fci->params); PHP_GRPC_FREE_STD_ZVAL(state->fci->retval); +#endif efree(state); } diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h index 13083b0bc7..51f9dd5fe6 100644 --- a/src/php/ext/grpc/php_grpc.h +++ b/src/php/ext/grpc/php_grpc.h @@ -40,9 +40,6 @@ extern zend_module_entry grpc_module_entry; #define phpext_grpc_ptr &grpc_module_entry -#define PHP_GRPC_VERSION \ - "0.1.0" /* Replace with version number for your extension */ - #ifdef PHP_WIN32 #define PHP_GRPC_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 @@ -56,10 +53,9 @@ extern zend_module_entry grpc_module_entry; #endif #include "php.h" - #include "php7_wrapper.h" - #include "grpc/grpc.h" +#include "version.h" /* These are all function declarations */ /* Code that runs at module initialization */ diff --git a/src/php/ext/grpc/version.h b/src/php/ext/grpc/version.h new file mode 100644 index 0000000000..993ef2de27 --- /dev/null +++ b/src/php/ext/grpc/version.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef VERSION_H +#define VERSION_H + +#define PHP_GRPC_VERSION "1.4.0" + +#endif /* VERSION_H */ diff --git a/src/php/tests/qps/composer.json b/src/php/tests/qps/composer.json index 0fc87098f5..8c1e7b6c74 100644 --- a/src/php/tests/qps/composer.json +++ b/src/php/tests/qps/composer.json @@ -1,7 +1,8 @@ { "minimum-stability": "dev", "require": { - "grpc/grpc": "dev-master" + "grpc/grpc": "dev-master", + "google/protobuf": "^v3.3.0" }, "autoload": { "psr-4": { diff --git a/src/proto/grpc/testing/BUILD b/src/proto/grpc/testing/BUILD index 805988c337..5f1c005faf 100644 --- a/src/proto/grpc/testing/BUILD +++ b/src/proto/grpc/testing/BUILD @@ -42,8 +42,11 @@ grpc_proto_library( grpc_proto_library( name = "control_proto", srcs = ["control.proto"], - deps = ["payloads_proto", "stats_proto"], has_services = False, + deps = [ + "payloads_proto", + "stats_proto", + ], ) grpc_proto_library( @@ -101,5 +104,8 @@ grpc_proto_library( grpc_proto_library( name = "test_proto", srcs = ["test.proto"], - deps = ["empty_proto", "messages_proto"], + deps = [ + "empty_proto", + "messages_proto", + ], ) diff --git a/src/proto/grpc/testing/control.proto b/src/proto/grpc/testing/control.proto index 02b156d0cd..1f4569e278 100644 --- a/src/proto/grpc/testing/control.proto +++ b/src/proto/grpc/testing/control.proto @@ -244,6 +244,10 @@ message ScenarioResultSummary // Number of requests that succeeded/failed double successful_requests_per_second = 13; double failed_requests_per_second = 14; + + // Number of polls called inside completion queue per request + double client_polls_per_request = 15; + double server_polls_per_request = 16; } // Results of a single benchmark scenario. diff --git a/src/proto/grpc/testing/stats.proto b/src/proto/grpc/testing/stats.proto index 80014161a1..e236cf159b 100644 --- a/src/proto/grpc/testing/stats.proto +++ b/src/proto/grpc/testing/stats.proto @@ -47,6 +47,9 @@ message ServerStats { // change in idle time of the server (data from proc/stat) uint64 idle_cpu_time = 5; + + // Number of polls called inside completion queue + uint64 cq_poll_count = 6; } // Histogram params based on grpc/support/histogram.c @@ -81,4 +84,7 @@ message ClientStats { // Number of failed requests (one row per status code seen) repeated RequestResultCount request_results = 5; + + // Number of polls called inside completion queue + uint64 cq_poll_count = 6; } diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index dd2e550f72..9770301d09 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -103,6 +103,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/ev_epollsig_linux.c', 'src/core/lib/iomgr/ev_poll_posix.c', 'src/core/lib/iomgr/ev_posix.c', + 'src/core/lib/iomgr/ev_windows.c', 'src/core/lib/iomgr/exec_ctx.c', 'src/core/lib/iomgr/executor.c', 'src/core/lib/iomgr/iocp_windows.c', @@ -320,6 +321,8 @@ CORE_SOURCE_FILES = [ 'src/core/ext/census/tracing.c', 'src/core/ext/filters/max_age/max_age_filter.c', 'src/core/ext/filters/message_size/message_size_filter.c', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', + 'src/core/ext/filters/workarounds/workaround_utils.c', 'src/core/plugin_registry/grpc_plugin_registry.c', 'src/boringssl/err_data.c', 'third_party/boringssl/crypto/aes/aes.c', diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py index 7ee5336a7d..658994d780 100644 --- a/src/python/grpcio_tests/setup.py +++ b/src/python/grpcio_tests/setup.py @@ -56,7 +56,8 @@ INSTALL_REQUIRES = ( 'grpcio>={version}'.format(version=grpc_version.VERSION), 'grpcio-tools>={version}'.format(version=grpc_version.VERSION), 'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION), - 'oauth2client>=1.4.7', 'protobuf>=3.3.0', 'six>=1.10',) + 'oauth2client>=1.4.7', 'protobuf>=3.3.0', 'six>=1.10', 'google-auth>=1.0.0', + 'requests>=2.14.2') COMMAND_CLASS = { # Run `preprocess` *before* doing any packaging! diff --git a/src/python/grpcio_tests/tests/interop/client.py b/src/python/grpcio_tests/tests/interop/client.py index 97f6843d3c..9be3ba5945 100644 --- a/src/python/grpcio_tests/tests/interop/client.py +++ b/src/python/grpcio_tests/tests/interop/client.py @@ -29,10 +29,11 @@ """The Python implementation of the GRPC interoperability test client.""" import argparse -from oauth2client import client as oauth2client_client +import os +from google import auth as google_auth +from google.auth import jwt as google_auth_jwt import grpc -from grpc.beta import implementations from src.proto.grpc.testing import test_pb2 from tests.interop import methods @@ -84,25 +85,24 @@ def _application_default_credentials(): def _stub(args): target = '{}:{}'.format(args.server_host, args.server_port) if args.test_case == 'oauth2_auth_token': - google_credentials = _application_default_credentials() - scoped_credentials = google_credentials.create_scoped( - [args.oauth_scope]) - access_token = scoped_credentials.get_access_token().access_token - call_credentials = grpc.access_token_call_credentials(access_token) + google_credentials, unused_project_id = google_auth.default( + scopes=[args.oauth_scope]) + google_credentials.refresh(google_auth.transport.requests.Request()) + call_credentials = grpc.access_token_call_credentials( + google_credentials.token) elif args.test_case == 'compute_engine_creds': - google_credentials = _application_default_credentials() - scoped_credentials = google_credentials.create_scoped( - [args.oauth_scope]) - # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last - # remaining use of the Beta API. - call_credentials = implementations.google_call_credentials( - scoped_credentials) + google_credentials, unused_project_id = google_auth.default( + scopes=[args.oauth_scope]) + call_credentials = grpc.metadata_call_credentials( + google_auth.transport.grpc.AuthMetadataPlugin( + credentials=google_credentials, + request=google_auth.transport.requests.Request())) elif args.test_case == 'jwt_token_creds': - google_credentials = _application_default_credentials() - # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last - # remaining use of the Beta API. - call_credentials = implementations.google_call_credentials( - google_credentials) + google_credentials = google_auth_jwt.OnDemandCredentials.from_service_account_file( + os.environ[google_auth.environment_vars.CREDENTIALS]) + call_credentials = grpc.metadata_call_credentials( + google_auth.transport.grpc.AuthMetadataPlugin( + credentials=google_credentials, request=None)) else: call_credentials = None if args.use_tls: diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py index e1016f7c0d..354b51da25 100644 --- a/src/python/grpcio_tests/tests/interop/methods.py +++ b/src/python/grpcio_tests/tests/interop/methods.py @@ -33,8 +33,10 @@ import json import os import threading -from oauth2client import client as oauth2client_client - +from google import auth as google_auth +from google.auth import environment_vars as google_auth_environment_vars +from google.auth.transport import grpc as google_auth_transport_grpc +from google.auth.transport import requests as google_auth_transport_requests import grpc from grpc.beta import implementations @@ -401,8 +403,7 @@ def _compute_engine_creds(stub, args): def _oauth2_auth_token(stub, args): - json_key_filename = os.environ[ - oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] + json_key_filename = os.environ[google_auth_environment_vars.CREDENTIALS] wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] response = _large_unary_common_behavior(stub, True, True, None) if wanted_email != response.username: @@ -414,8 +415,7 @@ def _oauth2_auth_token(stub, args): def _jwt_token_creds(stub, args): - json_key_filename = os.environ[ - oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] + json_key_filename = os.environ[google_auth_environment_vars.CREDENTIALS] wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] response = _large_unary_common_behavior(stub, True, False, None) if wanted_email != response.username: @@ -424,15 +424,14 @@ def _jwt_token_creds(stub, args): def _per_rpc_creds(stub, args): - json_key_filename = os.environ[ - oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] + json_key_filename = os.environ[google_auth_environment_vars.CREDENTIALS] wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] - credentials = oauth2client_client.GoogleCredentials.get_application_default() - scoped_credentials = credentials.create_scoped([args.oauth_scope]) - # TODO(https://github.com/grpc/grpc/issues/6799): Eliminate this last - # remaining use of the Beta API. - call_credentials = implementations.google_call_credentials( - scoped_credentials) + google_credentials, unused_project_id = google_auth.default( + scopes=[args.oauth_scope]) + call_credentials = grpc.metadata_call_credentials( + google_auth_transport_grpc.AuthMetadataPlugin( + credentials=google_credentials, + request=google_auth_transport_requests.Request())) response = _large_unary_common_behavior(stub, True, False, call_credentials) if wanted_email != response.username: raise ValueError('expected username %s, got %s' % |