diff options
Diffstat (limited to 'src')
118 files changed, 4695 insertions, 2646 deletions
diff --git a/src/core/census/grpc_context.c b/src/core/census/grpc_context.c index cf2353199f..0ed63469b6 100644 --- a/src/core/census/grpc_context.c +++ b/src/core/census/grpc_context.c @@ -34,12 +34,28 @@ #include <grpc/census.h> #include "src/core/census/grpc_context.h" -void *grpc_census_context_create() { - census_context *context; - census_context_deserialize(NULL, &context); - return (void *)context; +static void grpc_census_context_destroy(void *context) { + census_context_destroy((census_context *)context); } -void grpc_census_context_destroy(void *context) { - census_context_destroy((census_context *)context); +void grpc_census_call_set_context(grpc_call *call, census_context *context) { + if (!census_available()) { + return; + } + if (context == NULL) { + if (grpc_call_is_client(call)) { + census_context *context_ptr; + census_context_deserialize(NULL, &context_ptr); + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context_ptr, + grpc_census_context_destroy); + } else { + /* TODO(aveitch): server side context code to be implemented. */ + } + } else { + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL); + } +} + +census_context *grpc_census_call_get_context(grpc_call *call) { + return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); } diff --git a/src/core/census/grpc_context.h b/src/core/census/grpc_context.h index f610f6ce21..4637e7218e 100644 --- a/src/core/census/grpc_context.h +++ b/src/core/census/grpc_context.h @@ -36,7 +36,22 @@ #ifndef CENSUS_GRPC_CONTEXT_H #define CENSUS_GRPC_CONTEXT_H -void *grpc_census_context_create(); -void grpc_census_context_destroy(void *context); +#include <grpc/census.h> +#include "src/core/surface/call.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set census context for the call; Must be called before first call to + grpc_call_start_batch(). */ +void grpc_census_call_set_context(grpc_call *call, census_context *context); + +/* Retrieve the calls current census context. */ +census_context *grpc_census_call_get_context(grpc_call *call); + +#ifdef __cplusplus +} +#endif #endif /* CENSUS_GRPC_CONTEXT_H */ diff --git a/src/core/census/initialize.c b/src/core/census/initialize.c index 057ac78ee7..8016520641 100644 --- a/src/core/census/initialize.c +++ b/src/core/census/initialize.c @@ -48,3 +48,5 @@ int census_initialize(int functions) { } void census_shutdown() { census_fns_enabled = CENSUS_NONE; } + +int census_available() { return (census_fns_enabled != CENSUS_NONE); } diff --git a/src/core/channel/census_filter.c b/src/core/channel/census_filter.c index 7e393a01a6..83b7682848 100644 --- a/src/core/channel/census_filter.c +++ b/src/core/channel/census_filter.c @@ -84,7 +84,8 @@ static void extract_and_annotate_method_tag(grpc_stream_op_buffer* sopb, } } -static void client_mutate_op(grpc_call_element* elem, grpc_transport_op* op) { +static void client_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { call_data* calld = elem->call_data; channel_data* chand = elem->channel_data; if (op->send_ops) { @@ -93,7 +94,7 @@ static void client_mutate_op(grpc_call_element* elem, grpc_transport_op* op) { } static void client_start_transport_op(grpc_call_element* elem, - grpc_transport_op* op) { + grpc_transport_stream_op* op) { call_data* calld = elem->call_data; GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); client_mutate_op(elem, op); @@ -110,7 +111,8 @@ static void server_on_done_recv(void* ptr, int success) { calld->on_done_recv(calld->recv_user_data, success); } -static void server_mutate_op(grpc_call_element* elem, grpc_transport_op* op) { +static void server_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op* op) { call_data* calld = elem->call_data; if (op->recv_ops) { /* substitute our callback for the op callback */ @@ -123,7 +125,7 @@ static void server_mutate_op(grpc_call_element* elem, grpc_transport_op* op) { } static void server_start_transport_op(grpc_call_element* elem, - grpc_transport_op* op) { + grpc_transport_stream_op* op) { call_data* calld = elem->call_data; GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); server_mutate_op(elem, op); @@ -145,7 +147,7 @@ static void channel_op(grpc_channel_element* elem, static void client_init_call_elem(grpc_call_element* elem, const void* server_transport_data, - grpc_transport_op* initial_op) { + grpc_transport_stream_op* initial_op) { call_data* d = elem->call_data; GPR_ASSERT(d != NULL); init_rpc_stats(&d->stats); @@ -163,7 +165,7 @@ static void client_destroy_call_elem(grpc_call_element* elem) { static void server_init_call_elem(grpc_call_element* elem, const void* server_transport_data, - grpc_transport_op* initial_op) { + grpc_transport_stream_op* initial_op) { call_data* d = elem->call_data; GPR_ASSERT(d != NULL); init_rpc_stats(&d->stats); @@ -200,11 +202,23 @@ static void destroy_channel_elem(grpc_channel_element* elem) { } const grpc_channel_filter grpc_client_census_filter = { - client_start_transport_op, channel_op, sizeof(call_data), - client_init_call_elem, client_destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "census-client"}; + client_start_transport_op, + channel_op, + sizeof(call_data), + client_init_call_elem, + client_destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "census-client"}; const grpc_channel_filter grpc_server_census_filter = { - server_start_transport_op, channel_op, sizeof(call_data), - server_init_call_elem, server_destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "census-server"}; + server_start_transport_op, + channel_op, + sizeof(call_data), + server_init_call_elem, + server_destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "census-server"}; diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c index 166d559a45..140f8bd656 100644 --- a/src/core/channel/channel_args.c +++ b/src/core/channel/channel_args.c @@ -62,7 +62,8 @@ static grpc_arg copy_arg(const grpc_arg *src) { } grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, - const grpc_arg *to_add) { + const grpc_arg *to_add, + size_t num_to_add) { grpc_channel_args *dst = gpr_malloc(sizeof(grpc_channel_args)); size_t i; size_t src_num_args = (src == NULL) ? 0 : src->num_args; @@ -71,17 +72,24 @@ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, dst->args = NULL; return dst; } - dst->num_args = src_num_args + ((to_add == NULL) ? 0 : 1); + dst->num_args = src_num_args + num_to_add; dst->args = gpr_malloc(sizeof(grpc_arg) * dst->num_args); for (i = 0; i < src_num_args; i++) { dst->args[i] = copy_arg(&src->args[i]); } - if (to_add != NULL) dst->args[src_num_args] = copy_arg(to_add); + for (i = 0; i < num_to_add; i++) { + dst->args[i + src_num_args] = copy_arg(&to_add[i]); + } return dst; } grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src) { - return grpc_channel_args_copy_and_add(src, NULL); + return grpc_channel_args_copy_and_add(src, NULL, 0); +} + +grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, + const grpc_channel_args *b) { + return grpc_channel_args_copy_and_add(a, b->args, b->num_args); } void grpc_channel_args_destroy(grpc_channel_args *a) { @@ -131,11 +139,11 @@ grpc_compression_level grpc_channel_args_get_compression_level( return GRPC_COMPRESS_LEVEL_NONE; } -void grpc_channel_args_set_compression_level( - grpc_channel_args **a, grpc_compression_level level) { +void grpc_channel_args_set_compression_level(grpc_channel_args **a, + grpc_compression_level level) { grpc_arg tmp; tmp.type = GRPC_ARG_INTEGER; tmp.key = GRPC_COMPRESSION_LEVEL_ARG; tmp.value.integer = level; - *a = grpc_channel_args_copy_and_add(*a, &tmp); + *a = grpc_channel_args_copy_and_add(*a, &tmp, 1); } diff --git a/src/core/channel/channel_args.h b/src/core/channel/channel_args.h index bf747b26e6..17849b7e59 100644 --- a/src/core/channel/channel_args.h +++ b/src/core/channel/channel_args.h @@ -43,7 +43,12 @@ grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src); /** Copy some arguments and add the to_add parameter in the end. If to_add is NULL, it is equivalent to call grpc_channel_args_copy. */ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, - const grpc_arg *to_add); + const grpc_arg *to_add, + size_t num_to_add); + +/** Copy args from a then args from b into a new channel args */ +grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, + const grpc_channel_args *b); /** Destroy arguments created by grpc_channel_args_copy */ void grpc_channel_args_destroy(grpc_channel_args *a); @@ -58,7 +63,7 @@ grpc_compression_level grpc_channel_args_get_compression_level( /** Sets the compression level in \a a to \a level. Setting it to * GRPC_COMPRESS_LEVEL_NONE disables compression for the channel. */ -void grpc_channel_args_set_compression_level( - grpc_channel_args **a, grpc_compression_level level); +void grpc_channel_args_set_compression_level(grpc_channel_args **a, + grpc_compression_level level); -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H */ +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHANNEL_ARGS_H */ diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c index 9eec8163f5..e38dcb58b7 100644 --- a/src/core/channel/channel_stack.c +++ b/src/core/channel/channel_stack.c @@ -102,7 +102,8 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, } void grpc_channel_stack_init(const grpc_channel_filter **filters, - size_t filter_count, const grpc_channel_args *args, + size_t filter_count, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *metadata_context, grpc_channel_stack *stack) { size_t call_size = @@ -122,8 +123,9 @@ void grpc_channel_stack_init(const grpc_channel_filter **filters, for (i = 0; i < filter_count; i++) { elems[i].filter = filters[i]; elems[i].channel_data = user_data; - elems[i].filter->init_channel_elem(&elems[i], args, metadata_context, - i == 0, i == (filter_count - 1)); + elems[i].filter->init_channel_elem(&elems[i], master, args, + metadata_context, i == 0, + i == (filter_count - 1)); user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); } @@ -148,7 +150,7 @@ void grpc_channel_stack_destroy(grpc_channel_stack *stack) { void grpc_call_stack_init(grpc_channel_stack *channel_stack, const void *transport_server_data, - grpc_transport_op *initial_op, + grpc_transport_stream_op *initial_op, grpc_call_stack *call_stack) { grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack); size_t count = channel_stack->count; @@ -184,14 +186,14 @@ void grpc_call_stack_destroy(grpc_call_stack *stack) { } } -void grpc_call_next_op(grpc_call_element *elem, grpc_transport_op *op) { +void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op) { grpc_call_element *next_elem = elem + 1; - next_elem->filter->start_transport_op(next_elem, op); + next_elem->filter->start_transport_stream_op(next_elem, op); } -void grpc_channel_next_op(grpc_channel_element *elem, grpc_channel_op *op) { - grpc_channel_element *next_elem = elem + op->dir; - next_elem->filter->channel_op(next_elem, elem, op); +void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op) { + grpc_channel_element *next_elem = elem + 1; + next_elem->filter->start_transport_op(next_elem, op); } grpc_channel_stack *grpc_channel_stack_from_top_element( @@ -206,7 +208,7 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { } void grpc_call_element_send_cancel(grpc_call_element *cur_elem) { - grpc_transport_op op; + grpc_transport_stream_op op; memset(&op, 0, sizeof(op)); op.cancel_with_status = GRPC_STATUS_CANCELLED; grpc_call_next_op(cur_elem, &op); diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h index de0e4e4518..785be8925b 100644 --- a/src/core/channel/channel_stack.h +++ b/src/core/channel/channel_stack.h @@ -51,45 +51,6 @@ typedef struct grpc_channel_element grpc_channel_element; typedef struct grpc_call_element grpc_call_element; -/* The direction of the call. - The values of the enums (1, -1) matter here - they are used to increment - or decrement a pointer to find the next element to call */ -typedef enum { GRPC_CALL_DOWN = 1, GRPC_CALL_UP = -1 } grpc_call_dir; - -typedef enum { - /* send a goaway message to remote channels indicating that we are going - to disconnect in the future */ - GRPC_CHANNEL_GOAWAY, - /* disconnect any underlying transports */ - GRPC_CHANNEL_DISCONNECT, - /* transport received a new call */ - GRPC_ACCEPT_CALL, - /* an underlying transport was closed */ - GRPC_TRANSPORT_CLOSED, - /* an underlying transport is about to be closed */ - GRPC_TRANSPORT_GOAWAY -} grpc_channel_op_type; - -/* A single filterable operation to be performed on a channel */ -typedef struct { - /* The type of operation we're performing */ - grpc_channel_op_type type; - /* The directionality of this call - is it bubbling up the stack, or down? */ - grpc_call_dir dir; - - /* Argument data, matching up with grpc_channel_op_type names */ - union { - struct { - grpc_transport *transport; - const void *transport_server_data; - } accept_call; - struct { - grpc_status_code status; - gpr_slice message; - } goaway; - } data; -} grpc_channel_op; - /* Channel filters specify: 1. the amount of memory needed in the channel & call (via the sizeof_XXX members) @@ -103,12 +64,12 @@ typedef struct { typedef struct { /* Called to eg. send/receive data on a call. See grpc_call_next_op on how to call the next element in the stack */ - void (*start_transport_op)(grpc_call_element *elem, grpc_transport_op *op); + void (*start_transport_stream_op)(grpc_call_element *elem, + grpc_transport_stream_op *op); /* Called to handle channel level operations - e.g. new calls, or transport closure. See grpc_channel_next_op on how to call the next element in the stack */ - void (*channel_op)(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op); + void (*start_transport_op)(grpc_channel_element *elem, grpc_transport_op *op); /* sizeof(per call data) */ size_t sizeof_call_data; @@ -122,7 +83,7 @@ typedef struct { argument.*/ void (*init_call_elem)(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op); + grpc_transport_stream_op *initial_op); /* Destroy per call data. The filter does not need to do any chaining */ void (*destroy_call_elem)(grpc_call_element *elem); @@ -135,7 +96,7 @@ typedef struct { is_first, is_last designate this elements position in the stack, and are useful for asserting correct configuration by upper layer code. The filter does not need to do any chaining */ - void (*init_channel_elem)(grpc_channel_element *elem, + void (*init_channel_elem)(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last); @@ -190,7 +151,8 @@ size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count); /* Initialize a channel stack given some filters */ void grpc_channel_stack_init(const grpc_channel_filter **filters, - size_t filter_count, const grpc_channel_args *args, + size_t filter_count, grpc_channel *master, + const grpc_channel_args *args, grpc_mdctx *metadata_context, grpc_channel_stack *stack); /* Destroy a channel stack */ @@ -201,16 +163,16 @@ void grpc_channel_stack_destroy(grpc_channel_stack *stack); server. */ void grpc_call_stack_init(grpc_channel_stack *channel_stack, const void *transport_server_data, - grpc_transport_op *initial_op, + grpc_transport_stream_op *initial_op, grpc_call_stack *call_stack); /* Destroy a call stack */ void grpc_call_stack_destroy(grpc_call_stack *stack); /* Call the next operation in a call stack */ -void grpc_call_next_op(grpc_call_element *elem, grpc_transport_op *op); +void grpc_call_next_op(grpc_call_element *elem, grpc_transport_stream_op *op); /* Call the next operation (depending on call directionality) in a channel stack */ -void grpc_channel_next_op(grpc_channel_element *elem, grpc_channel_op *op); +void grpc_channel_next_op(grpc_channel_element *elem, grpc_transport_op *op); /* Given the top element of a channel stack, get the channel stack itself */ grpc_channel_stack *grpc_channel_stack_from_top_element( @@ -219,7 +181,7 @@ grpc_channel_stack *grpc_channel_stack_from_top_element( grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem); void grpc_call_log_op(char *file, int line, gpr_log_severity severity, - grpc_call_element *elem, grpc_transport_op *op); + grpc_call_element *elem, grpc_transport_stream_op *op); void grpc_call_element_send_cancel(grpc_call_element *cur_elem); diff --git a/src/core/channel/child_channel.c b/src/core/channel/child_channel.c deleted file mode 100644 index 6690265d75..0000000000 --- a/src/core/channel/child_channel.c +++ /dev/null @@ -1,308 +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. - * - */ - -#include "src/core/channel/child_channel.h" -#include "src/core/iomgr/iomgr.h" -#include <grpc/support/alloc.h> - -/* Link back filter: passes up calls to the client channel, pushes down calls - down */ - -static void maybe_destroy_channel(grpc_child_channel *channel); - -typedef struct { - gpr_mu mu; - gpr_cv cv; - grpc_channel_element *back; - /* # of active calls on the channel */ - gpr_uint32 active_calls; - /* has grpc_child_channel_destroy been called? */ - gpr_uint8 destroyed; - /* has the transport reported itself disconnected? */ - gpr_uint8 disconnected; - /* are we calling 'back' - our parent channel */ - gpr_uint8 calling_back; - /* have we or our parent sent goaway yet? - dup suppression */ - gpr_uint8 sent_goaway; - /* are we currently sending farewell (in this file: goaway + disconnect) */ - gpr_uint8 sending_farewell; - /* have we sent farewell (goaway + disconnect) */ - gpr_uint8 sent_farewell; - - grpc_iomgr_closure finally_destroy_channel_closure; - grpc_iomgr_closure send_farewells_closure; -} lb_channel_data; - -typedef struct { grpc_child_channel *channel; } lb_call_data; - -static void lb_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { - grpc_call_next_op(elem, op); -} - -/* Currently we assume all channel operations should just be pushed up. */ -static void lb_channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, - grpc_channel_op *op) { - lb_channel_data *chand = elem->channel_data; - grpc_channel_element *back; - int calling_back = 0; - - switch (op->dir) { - case GRPC_CALL_UP: - gpr_mu_lock(&chand->mu); - back = chand->back; - if (back) { - chand->calling_back++; - calling_back = 1; - } - gpr_mu_unlock(&chand->mu); - if (back) { - back->filter->channel_op(chand->back, elem, op); - } else if (op->type == GRPC_TRANSPORT_GOAWAY) { - gpr_slice_unref(op->data.goaway.message); - } - break; - case GRPC_CALL_DOWN: - grpc_channel_next_op(elem, op); - break; - } - - gpr_mu_lock(&chand->mu); - switch (op->type) { - case GRPC_TRANSPORT_CLOSED: - chand->disconnected = 1; - maybe_destroy_channel(grpc_channel_stack_from_top_element(elem)); - break; - case GRPC_CHANNEL_GOAWAY: - chand->sent_goaway = 1; - break; - case GRPC_CHANNEL_DISCONNECT: - case GRPC_TRANSPORT_GOAWAY: - case GRPC_ACCEPT_CALL: - break; - } - - if (calling_back) { - chand->calling_back--; - gpr_cv_signal(&chand->cv); - maybe_destroy_channel(grpc_channel_stack_from_top_element(elem)); - } - gpr_mu_unlock(&chand->mu); -} - -/* Constructor for call_data */ -static void lb_init_call_elem(grpc_call_element *elem, - const void *server_transport_data, - grpc_transport_op *initial_op) {} - -/* Destructor for call_data */ -static void lb_destroy_call_elem(grpc_call_element *elem) {} - -/* Constructor for channel_data */ -static void lb_init_channel_elem(grpc_channel_element *elem, - const grpc_channel_args *args, - grpc_mdctx *metadata_context, int is_first, - int is_last) { - lb_channel_data *chand = elem->channel_data; - GPR_ASSERT(is_first); - GPR_ASSERT(!is_last); - gpr_mu_init(&chand->mu); - gpr_cv_init(&chand->cv); - chand->back = NULL; - chand->destroyed = 0; - chand->disconnected = 0; - chand->active_calls = 0; - chand->sent_goaway = 0; - chand->calling_back = 0; - chand->sending_farewell = 0; - chand->sent_farewell = 0; -} - -/* Destructor for channel_data */ -static void lb_destroy_channel_elem(grpc_channel_element *elem) { - lb_channel_data *chand = elem->channel_data; - gpr_mu_destroy(&chand->mu); - gpr_cv_destroy(&chand->cv); -} - -const grpc_channel_filter grpc_child_channel_top_filter = { - lb_start_transport_op, lb_channel_op, - sizeof(lb_call_data), lb_init_call_elem, lb_destroy_call_elem, - sizeof(lb_channel_data), lb_init_channel_elem, lb_destroy_channel_elem, - "child-channel", -}; - -/* grpc_child_channel proper */ - -#define LINK_BACK_ELEM_FROM_CHANNEL(channel) \ - grpc_channel_stack_element((channel), 0) - -#define LINK_BACK_ELEM_FROM_CALL(call) grpc_call_stack_element((call), 0) - -static void finally_destroy_channel(void *c, int success) { - /* ignore success or not... this is a destruction callback and will only - happen once - the only purpose here is to release resources */ - grpc_child_channel *channel = c; - lb_channel_data *chand = LINK_BACK_ELEM_FROM_CHANNEL(channel)->channel_data; - /* wait for the initiator to leave the mutex */ - gpr_mu_lock(&chand->mu); - gpr_mu_unlock(&chand->mu); - grpc_channel_stack_destroy(channel); - gpr_free(channel); -} - -static void send_farewells(void *c, int success) { - grpc_child_channel *channel = c; - grpc_channel_element *lbelem = LINK_BACK_ELEM_FROM_CHANNEL(channel); - lb_channel_data *chand = lbelem->channel_data; - int send_goaway; - grpc_channel_op op; - - gpr_mu_lock(&chand->mu); - send_goaway = !chand->sent_goaway; - chand->sent_goaway = 1; - gpr_mu_unlock(&chand->mu); - - if (send_goaway) { - op.type = GRPC_CHANNEL_GOAWAY; - op.dir = GRPC_CALL_DOWN; - op.data.goaway.status = GRPC_STATUS_OK; - op.data.goaway.message = gpr_slice_from_copied_string("Client disconnect"); - grpc_channel_next_op(lbelem, &op); - } - - op.type = GRPC_CHANNEL_DISCONNECT; - op.dir = GRPC_CALL_DOWN; - grpc_channel_next_op(lbelem, &op); - - gpr_mu_lock(&chand->mu); - chand->sending_farewell = 0; - chand->sent_farewell = 1; - maybe_destroy_channel(channel); - gpr_mu_unlock(&chand->mu); -} - -static void maybe_destroy_channel(grpc_child_channel *channel) { - lb_channel_data *chand = LINK_BACK_ELEM_FROM_CHANNEL(channel)->channel_data; - if (chand->destroyed && chand->disconnected && chand->active_calls == 0 && - !chand->sending_farewell && !chand->calling_back) { - chand->finally_destroy_channel_closure.cb = finally_destroy_channel; - chand->finally_destroy_channel_closure.cb_arg = channel; - grpc_iomgr_add_callback(&chand->finally_destroy_channel_closure); - } else if (chand->destroyed && !chand->disconnected && - chand->active_calls == 0 && !chand->sending_farewell && - !chand->sent_farewell) { - chand->sending_farewell = 1; - chand->send_farewells_closure.cb = send_farewells; - chand->send_farewells_closure.cb_arg = channel; - grpc_iomgr_add_callback(&chand->send_farewells_closure); - } -} - -grpc_child_channel *grpc_child_channel_create( - grpc_channel_element *parent, const grpc_channel_filter **filters, - size_t filter_count, const grpc_channel_args *args, - grpc_mdctx *metadata_context) { - grpc_channel_stack *stk = - gpr_malloc(grpc_channel_stack_size(filters, filter_count)); - lb_channel_data *lb; - - grpc_channel_stack_init(filters, filter_count, args, metadata_context, stk); - - lb = LINK_BACK_ELEM_FROM_CHANNEL(stk)->channel_data; - gpr_mu_lock(&lb->mu); - lb->back = parent; - gpr_mu_unlock(&lb->mu); - - return stk; -} - -void grpc_child_channel_destroy(grpc_child_channel *channel, - int wait_for_callbacks) { - grpc_channel_element *lbelem = LINK_BACK_ELEM_FROM_CHANNEL(channel); - lb_channel_data *chand = lbelem->channel_data; - - gpr_mu_lock(&chand->mu); - while (wait_for_callbacks && chand->calling_back) { - gpr_cv_wait(&chand->cv, &chand->mu, gpr_inf_future); - } - - chand->back = NULL; - chand->destroyed = 1; - maybe_destroy_channel(channel); - gpr_mu_unlock(&chand->mu); -} - -void grpc_child_channel_handle_op(grpc_child_channel *channel, - grpc_channel_op *op) { - grpc_channel_next_op(LINK_BACK_ELEM_FROM_CHANNEL(channel), op); -} - -grpc_child_call *grpc_child_channel_create_call(grpc_child_channel *channel, - grpc_call_element *parent, - grpc_transport_op *initial_op) { - grpc_call_stack *stk = gpr_malloc((channel)->call_stack_size); - grpc_call_element *lbelem; - lb_call_data *lbcalld; - lb_channel_data *lbchand; - - grpc_call_stack_init(channel, NULL, initial_op, stk); - lbelem = LINK_BACK_ELEM_FROM_CALL(stk); - lbchand = lbelem->channel_data; - lbcalld = lbelem->call_data; - lbcalld->channel = channel; - - gpr_mu_lock(&lbchand->mu); - lbchand->active_calls++; - gpr_mu_unlock(&lbchand->mu); - - return stk; -} - -void grpc_child_call_destroy(grpc_child_call *call) { - grpc_call_element *lbelem = LINK_BACK_ELEM_FROM_CALL(call); - lb_call_data *calld = lbelem->call_data; - lb_channel_data *chand = lbelem->channel_data; - grpc_child_channel *channel = calld->channel; - grpc_call_stack_destroy(call); - gpr_free(call); - gpr_mu_lock(&chand->mu); - chand->active_calls--; - maybe_destroy_channel(channel); - gpr_mu_unlock(&chand->mu); -} - -grpc_call_element *grpc_child_call_get_top_element(grpc_child_call *call) { - return LINK_BACK_ELEM_FROM_CALL(call); -} diff --git a/src/core/channel/child_channel.h b/src/core/channel/child_channel.h deleted file mode 100644 index 556a1c731c..0000000000 --- a/src/core/channel/child_channel.h +++ /dev/null @@ -1,65 +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. - * - */ - -#ifndef GRPC_INTERNAL_CORE_CHANNEL_CHILD_CHANNEL_H -#define GRPC_INTERNAL_CORE_CHANNEL_CHILD_CHANNEL_H - -#include "src/core/channel/channel_stack.h" - -/* helper for filters that need to host child channel stacks... handles - lifetime and upwards propagation cleanly */ - -extern const grpc_channel_filter grpc_child_channel_top_filter; - -typedef grpc_channel_stack grpc_child_channel; -typedef grpc_call_stack grpc_child_call; - -/* filters[0] must be &grpc_child_channel_top_filter */ -grpc_child_channel *grpc_child_channel_create( - grpc_channel_element *parent, const grpc_channel_filter **filters, - size_t filter_count, const grpc_channel_args *args, - grpc_mdctx *metadata_context); -void grpc_child_channel_handle_op(grpc_child_channel *channel, - grpc_channel_op *op); -grpc_channel_element *grpc_child_channel_get_bottom_element( - grpc_child_channel *channel); -void grpc_child_channel_destroy(grpc_child_channel *channel, - int wait_for_callbacks); - -grpc_child_call *grpc_child_channel_create_call(grpc_child_channel *channel, - grpc_call_element *parent, - grpc_transport_op *initial_op); -grpc_call_element *grpc_child_call_get_top_element(grpc_child_call *call); -void grpc_child_call_destroy(grpc_child_call *call); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CHILD_CHANNEL_H */ diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index 711e105464..871e970eb8 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -34,13 +34,15 @@ #include "src/core/channel/client_channel.h" #include <stdio.h> +#include <string.h> #include "src/core/channel/channel_args.h" -#include "src/core/channel/child_channel.h" #include "src/core/channel/connected_channel.h" +#include "src/core/surface/channel.h" #include "src/core/iomgr/iomgr.h" #include "src/core/iomgr/pollset_set.h" #include "src/core/support/string.h" +#include "src/core/transport/connectivity_state.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/sync.h> @@ -51,31 +53,38 @@ typedef struct call_data call_data; typedef struct { - /* protects children, child_count, child_capacity, active_child, - transport_setup_initiated - does not protect channel stacks held by children - transport_setup is assumed to be set once during construction */ - gpr_mu mu; - - /* the sending child (may be null) */ - grpc_child_channel *active_child; + /** metadata context for this channel */ grpc_mdctx *mdctx; - - /* calls waiting for a channel to be ready */ - call_data **waiting_children; - size_t waiting_child_count; - size_t waiting_child_capacity; - - /* transport setup for this channel */ - grpc_transport_setup *transport_setup; - int transport_setup_initiated; - - grpc_channel_args *args; + /** resolver for this channel */ + grpc_resolver *resolver; + /** master channel - the grpc_channel instance that ultimately owns + this channel_data via its channel stack. + We occasionally use this to bump the refcount on the master channel + to keep ourselves alive through an asynchronous operation. */ + grpc_channel *master; + + /** mutex protecting client configuration, including all + variables below in this data structure */ + gpr_mu mu_config; + /** currently active load balancer - guarded by mu_config */ + grpc_lb_policy *lb_policy; + /** incoming configuration - set by resolver.next + guarded by mu_config */ + grpc_client_config *incoming_configuration; + /** a list of closures that are all waiting for config to come in */ + grpc_iomgr_closure *waiting_for_config_closures; + /** resolver callback */ + grpc_iomgr_closure on_config_changed; + /** connectivity state being tracked */ + grpc_connectivity_state_tracker state_tracker; } channel_data; typedef enum { CALL_CREATED, - CALL_WAITING, + CALL_WAITING_FOR_SEND, + CALL_WAITING_FOR_CONFIG, + CALL_WAITING_FOR_PICK, + CALL_WAITING_FOR_CALL, CALL_ACTIVE, CALL_CANCELLED } call_state; @@ -84,75 +93,25 @@ struct call_data { /* owning element */ grpc_call_element *elem; + gpr_mu mu_state; + call_state state; gpr_timespec deadline; - union { - struct { - /* our child call stack */ - grpc_child_call *child_call; - } active; - grpc_transport_op waiting_op; - struct { - grpc_linked_mdelem status; - grpc_linked_mdelem details; - } cancelled; - } s; + grpc_subchannel *picked_channel; + grpc_iomgr_closure async_setup_task; + grpc_transport_stream_op waiting_op; + /* our child call stack */ + grpc_subchannel_call *subchannel_call; + grpc_linked_mdelem status; + grpc_linked_mdelem details; }; -static int prepare_activate(grpc_call_element *elem, - grpc_child_channel *on_child) { - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; - if (calld->state == CALL_CANCELLED) return 0; - - /* no more access to calld->s.waiting allowed */ - GPR_ASSERT(calld->state == CALL_WAITING); - - if (calld->s.waiting_op.bind_pollset) { - grpc_transport_setup_del_interested_party(chand->transport_setup, - calld->s.waiting_op.bind_pollset); - } - - calld->state = CALL_ACTIVE; - - /* create a child call */ - /* TODO(ctiller): pass the waiting op down here */ - calld->s.active.child_call = - grpc_child_channel_create_call(on_child, elem, NULL); - - return 1; -} - -static void complete_activate(grpc_call_element *elem, grpc_transport_op *op) { - call_data *calld = elem->call_data; - grpc_call_element *child_elem = - grpc_child_call_get_top_element(calld->s.active.child_call); - - GPR_ASSERT(calld->state == CALL_ACTIVE); - - /* continue the start call down the stack, this nees to happen after metadata - are flushed*/ - child_elem->filter->start_transport_op(child_elem, op); -} - -static void remove_waiting_child(channel_data *chand, call_data *calld) { - size_t new_count; - size_t i; - for (i = 0, new_count = 0; i < chand->waiting_child_count; i++) { - if (chand->waiting_children[i] == calld) { - grpc_transport_setup_del_interested_party( - chand->transport_setup, calld->s.waiting_op.bind_pollset); - continue; - } - chand->waiting_children[new_count++] = chand->waiting_children[i]; - } - GPR_ASSERT(new_count == chand->waiting_child_count - 1 || - new_count == chand->waiting_child_count); - chand->waiting_child_count = new_count; -} +static grpc_iomgr_closure *merge_into_waiting_op( + grpc_call_element *elem, + grpc_transport_stream_op *new_op) GRPC_MUST_USE_RESULT; static void handle_op_after_cancellation(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; if (op->send_ops) { @@ -163,15 +122,15 @@ static void handle_op_after_cancellation(grpc_call_element *elem, char status[GPR_LTOA_MIN_BUFSIZE]; grpc_metadata_batch mdb; gpr_ltoa(GRPC_STATUS_CANCELLED, status); - calld->s.cancelled.status.md = + calld->status.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-status", status); - calld->s.cancelled.details.md = + calld->details.md = grpc_mdelem_from_strings(chand->mdctx, "grpc-message", "Cancelled"); - calld->s.cancelled.status.prev = calld->s.cancelled.details.next = NULL; - calld->s.cancelled.status.next = &calld->s.cancelled.details; - calld->s.cancelled.details.prev = &calld->s.cancelled.status; - mdb.list.head = &calld->s.cancelled.status; - mdb.list.tail = &calld->s.cancelled.details; + calld->status.prev = calld->details.next = NULL; + calld->status.next = &calld->details; + calld->details.prev = &calld->status; + mdb.list.head = &calld->status; + mdb.list.tail = &calld->details; mdb.garbage.head = mdb.garbage.tail = NULL; mdb.deadline = gpr_inf_future; grpc_sopb_add_metadata(op->recv_ops, mdb); @@ -183,192 +142,372 @@ static void handle_op_after_cancellation(grpc_call_element *elem, } } -static void cc_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { +typedef struct { + grpc_iomgr_closure closure; + grpc_call_element *elem; +} waiting_call; + +static void perform_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation); + +static void continue_with_pick(void *arg, int iomgr_success) { + waiting_call *wc = arg; + call_data *calld = wc->elem->call_data; + perform_transport_stream_op(wc->elem, &calld->waiting_op, 1); + gpr_free(wc); +} + +static void add_to_lb_policy_wait_queue_locked_state_config( + grpc_call_element *elem) { + channel_data *chand = elem->channel_data; + waiting_call *wc = gpr_malloc(sizeof(*wc)); + grpc_iomgr_closure_init(&wc->closure, continue_with_pick, wc); + wc->elem = elem; + wc->closure.next = chand->waiting_for_config_closures; + chand->waiting_for_config_closures = &wc->closure; +} + +static int is_empty(void *p, int len) { + char *ptr = p; + int i; + for (i = 0; i < len; i++) { + if (ptr[i] != 0) return 0; + } + return 1; +} + +static void started_call(void *arg, int iomgr_success) { + call_data *calld = arg; + grpc_transport_stream_op op; + int have_waiting; + + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_CANCELLED && calld->subchannel_call != NULL) { + memset(&op, 0, sizeof(op)); + op.cancel_with_status = GRPC_STATUS_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(calld->subchannel_call, &op); + } else if (calld->state == CALL_WAITING_FOR_CALL) { + have_waiting = !is_empty(&calld->waiting_op, sizeof(calld->waiting_op)); + if (calld->subchannel_call != NULL) { + calld->state = CALL_ACTIVE; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + grpc_subchannel_call_process_op(calld->subchannel_call, + &calld->waiting_op); + } + } else { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&calld->mu_state); + if (have_waiting) { + handle_op_after_cancellation(calld->elem, &calld->waiting_op); + } + } + } else { + GPR_ASSERT(calld->state == CALL_CANCELLED); + gpr_mu_unlock(&calld->mu_state); + } +} + +static void picked_target(void *arg, int iomgr_success) { + call_data *calld = arg; + grpc_pollset *pollset; + + if (calld->picked_channel == NULL) { + /* treat this like a cancellation */ + calld->waiting_op.cancel_with_status = GRPC_STATUS_UNAVAILABLE; + perform_transport_stream_op(calld->elem, &calld->waiting_op, 1); + } else { + gpr_mu_lock(&calld->mu_state); + if (calld->state == CALL_CANCELLED) { + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(calld->elem, &calld->waiting_op); + } else { + GPR_ASSERT(calld->state == CALL_WAITING_FOR_PICK); + calld->state = CALL_WAITING_FOR_CALL; + pollset = calld->waiting_op.bind_pollset; + gpr_mu_unlock(&calld->mu_state); + grpc_iomgr_closure_init(&calld->async_setup_task, started_call, calld); + grpc_subchannel_create_call(calld->picked_channel, pollset, + &calld->subchannel_call, + &calld->async_setup_task); + } + } +} + +static void pick_target(grpc_lb_policy *lb_policy, call_data *calld) { + grpc_metadata_batch *initial_metadata; + grpc_transport_stream_op *op = &calld->waiting_op; + + GPR_ASSERT(op->bind_pollset); + GPR_ASSERT(op->send_ops); + GPR_ASSERT(op->send_ops->nops >= 1); + GPR_ASSERT(op->send_ops->ops[0].type == GRPC_OP_METADATA); + initial_metadata = &op->send_ops->ops[0].data.metadata; + + grpc_iomgr_closure_init(&calld->async_setup_task, picked_target, calld); + grpc_lb_policy_pick(lb_policy, op->bind_pollset, initial_metadata, + &calld->picked_channel, &calld->async_setup_task); +} + +static grpc_iomgr_closure *merge_into_waiting_op( + grpc_call_element *elem, grpc_transport_stream_op *new_op) { + call_data *calld = elem->call_data; + grpc_iomgr_closure *consumed_op = NULL; + grpc_transport_stream_op *waiting_op = &calld->waiting_op; + GPR_ASSERT((waiting_op->send_ops != NULL) + (new_op->send_ops != NULL) <= 1); + GPR_ASSERT((waiting_op->recv_ops != NULL) + (new_op->recv_ops != NULL) <= 1); + if (new_op->send_ops != NULL) { + waiting_op->send_ops = new_op->send_ops; + waiting_op->is_last_send = new_op->is_last_send; + waiting_op->on_done_send = new_op->on_done_send; + } + if (new_op->recv_ops != NULL) { + waiting_op->recv_ops = new_op->recv_ops; + waiting_op->recv_state = new_op->recv_state; + waiting_op->on_done_recv = new_op->on_done_recv; + } + if (new_op->on_consumed != NULL) { + if (waiting_op->on_consumed != NULL) { + consumed_op = waiting_op->on_consumed; + } + waiting_op->on_consumed = new_op->on_consumed; + } + if (new_op->cancel_with_status != GRPC_STATUS_OK) { + waiting_op->cancel_with_status = new_op->cancel_with_status; + } + return consumed_op; +} + +static void perform_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op, + int continuation) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; - grpc_call_element *child_elem; - grpc_transport_op waiting_op; + grpc_subchannel_call *subchannel_call; + grpc_lb_policy *lb_policy; + grpc_transport_stream_op op2; + grpc_iomgr_closure *consumed_op = NULL; GPR_ASSERT(elem->filter == &grpc_client_channel_filter); GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - gpr_mu_lock(&chand->mu); + gpr_mu_lock(&calld->mu_state); switch (calld->state) { case CALL_ACTIVE: - child_elem = grpc_child_call_get_top_element(calld->s.active.child_call); - gpr_mu_unlock(&chand->mu); - child_elem->filter->start_transport_op(child_elem, op); + GPR_ASSERT(!continuation); + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + grpc_subchannel_call_process_op(subchannel_call, op); break; - case CALL_CREATED: - if (op->cancel_with_status != GRPC_STATUS_OK) { - calld->state = CALL_CANCELLED; - gpr_mu_unlock(&chand->mu); - handle_op_after_cancellation(elem, op); - } else { - calld->state = CALL_WAITING; - calld->s.waiting_op.bind_pollset = NULL; - if (chand->active_child) { - /* channel is connected - use the connected stack */ - if (prepare_activate(elem, chand->active_child)) { - gpr_mu_unlock(&chand->mu); - /* activate the request (pass it down) outside the lock */ - complete_activate(elem, op); - } else { - gpr_mu_unlock(&chand->mu); + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + break; + case CALL_WAITING_FOR_SEND: + GPR_ASSERT(!continuation); + consumed_op = merge_into_waiting_op(elem, op); + if (!calld->waiting_op.send_ops && + calld->waiting_op.cancel_with_status == GRPC_STATUS_OK) { + gpr_mu_unlock(&calld->mu_state); + break; + } + *op = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + continuation = 1; + /* fall through */ + case CALL_WAITING_FOR_CONFIG: + case CALL_WAITING_FOR_PICK: + case CALL_WAITING_FOR_CALL: + if (!continuation) { + if (op->cancel_with_status != GRPC_STATUS_OK) { + calld->state = CALL_CANCELLED; + op2 = calld->waiting_op; + memset(&calld->waiting_op, 0, sizeof(calld->waiting_op)); + if (op->on_consumed) { + calld->waiting_op.on_consumed = op->on_consumed; + op->on_consumed = NULL; + } else if (op2.on_consumed) { + calld->waiting_op.on_consumed = op2.on_consumed; + op2.on_consumed = NULL; } + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + handle_op_after_cancellation(elem, &op2); } else { - /* check to see if we should initiate a connection (if we're not - already), - but don't do so until outside the lock to avoid re-entrancy - problems if - the callback is immediate */ - int initiate_transport_setup = 0; - if (!chand->transport_setup_initiated) { - chand->transport_setup_initiated = 1; - initiate_transport_setup = 1; - } - /* add this call to the waiting set to be resumed once we have a child - channel stack, growing the waiting set if needed */ - if (chand->waiting_child_count == chand->waiting_child_capacity) { - chand->waiting_child_capacity = - GPR_MAX(chand->waiting_child_capacity * 2, 8); - chand->waiting_children = gpr_realloc( - chand->waiting_children, - chand->waiting_child_capacity * sizeof(call_data *)); - } - calld->s.waiting_op = *op; - chand->waiting_children[chand->waiting_child_count++] = calld; - grpc_transport_setup_add_interested_party(chand->transport_setup, - op->bind_pollset); - gpr_mu_unlock(&chand->mu); - - /* finally initiate transport setup if needed */ - if (initiate_transport_setup) { - grpc_transport_setup_initiate(chand->transport_setup); - } + consumed_op = merge_into_waiting_op(elem, op); + gpr_mu_unlock(&calld->mu_state); } + break; } - break; - case CALL_WAITING: + /* fall through */ + case CALL_CREATED: if (op->cancel_with_status != GRPC_STATUS_OK) { - waiting_op = calld->s.waiting_op; - remove_waiting_child(chand, calld); calld->state = CALL_CANCELLED; - gpr_mu_unlock(&chand->mu); - handle_op_after_cancellation(elem, &waiting_op); + gpr_mu_unlock(&calld->mu_state); handle_op_after_cancellation(elem, op); } else { - GPR_ASSERT((calld->s.waiting_op.send_ops == NULL) != - (op->send_ops == NULL)); - GPR_ASSERT((calld->s.waiting_op.recv_ops == NULL) != - (op->recv_ops == NULL)); - if (op->send_ops) { - calld->s.waiting_op.send_ops = op->send_ops; - calld->s.waiting_op.is_last_send = op->is_last_send; - calld->s.waiting_op.on_done_send = op->on_done_send; - } - if (op->recv_ops) { - calld->s.waiting_op.recv_ops = op->recv_ops; - calld->s.waiting_op.recv_state = op->recv_state; - calld->s.waiting_op.on_done_recv = op->on_done_recv; - } - gpr_mu_unlock(&chand->mu); - if (op->on_consumed) { - op->on_consumed->cb(op->on_consumed->cb_arg, 0); + calld->waiting_op = *op; + + if (op->send_ops == NULL) { + /* need to have some send ops before we can select the + lb target */ + calld->state = CALL_WAITING_FOR_SEND; + gpr_mu_unlock(&calld->mu_state); + } else { + gpr_mu_lock(&chand->mu_config); + lb_policy = chand->lb_policy; + if (lb_policy) { + GRPC_LB_POLICY_REF(lb_policy, "pick"); + gpr_mu_unlock(&chand->mu_config); + calld->state = CALL_WAITING_FOR_PICK; + gpr_mu_unlock(&calld->mu_state); + + pick_target(lb_policy, calld); + + GRPC_LB_POLICY_UNREF(lb_policy, "pick"); + } else if (chand->resolver != NULL) { + calld->state = CALL_WAITING_FOR_CONFIG; + add_to_lb_policy_wait_queue_locked_state_config(elem); + gpr_mu_unlock(&chand->mu_config); + gpr_mu_unlock(&calld->mu_state); + } else { + calld->state = CALL_CANCELLED; + gpr_mu_unlock(&chand->mu_config); + gpr_mu_unlock(&calld->mu_state); + handle_op_after_cancellation(elem, op); + } } } break; - case CALL_CANCELLED: - gpr_mu_unlock(&chand->mu); - handle_op_after_cancellation(elem, op); - break; } + + if (consumed_op != NULL) { + consumed_op->cb(consumed_op->cb_arg, 1); + } +} + +static void cc_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { + perform_transport_stream_op(elem, op, 0); +} + +static void cc_on_config_changed(void *arg, int iomgr_success) { + channel_data *chand = arg; + grpc_lb_policy *lb_policy = NULL; + grpc_lb_policy *old_lb_policy; + grpc_resolver *old_resolver; + grpc_iomgr_closure *wakeup_closures = NULL; + + if (chand->incoming_configuration != NULL) { + lb_policy = grpc_client_config_get_lb_policy(chand->incoming_configuration); + GRPC_LB_POLICY_REF(lb_policy, "channel"); + + grpc_client_config_unref(chand->incoming_configuration); + } + + chand->incoming_configuration = NULL; + + gpr_mu_lock(&chand->mu_config); + old_lb_policy = chand->lb_policy; + chand->lb_policy = lb_policy; + if (lb_policy != NULL || chand->resolver == NULL /* disconnected */) { + wakeup_closures = chand->waiting_for_config_closures; + chand->waiting_for_config_closures = NULL; + } + gpr_mu_unlock(&chand->mu_config); + + if (old_lb_policy) { + GRPC_LB_POLICY_UNREF(old_lb_policy, "channel"); + } + + gpr_mu_lock(&chand->mu_config); + if (iomgr_success && chand->resolver) { + grpc_resolver *resolver = chand->resolver; + GRPC_RESOLVER_REF(resolver, "channel-next"); + gpr_mu_unlock(&chand->mu_config); + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + grpc_resolver_next(chand->resolver, &chand->incoming_configuration, + &chand->on_config_changed); + GRPC_RESOLVER_UNREF(resolver, "channel-next"); + } else { + old_resolver = chand->resolver; + chand->resolver = NULL; + grpc_connectivity_state_set(&chand->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE); + gpr_mu_unlock(&chand->mu_config); + if (old_resolver != NULL) { + grpc_resolver_shutdown(old_resolver); + GRPC_RESOLVER_UNREF(old_resolver, "channel"); + } + } + + while (wakeup_closures) { + grpc_iomgr_closure *next = wakeup_closures->next; + grpc_iomgr_add_callback(wakeup_closures); + wakeup_closures = next; + } + + GRPC_CHANNEL_INTERNAL_UNREF(chand->master, "resolver"); } -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { +static void cc_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { + grpc_lb_policy *lb_policy = NULL; channel_data *chand = elem->channel_data; - grpc_child_channel *child_channel; - grpc_channel_op rop; - GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + grpc_resolver *destroy_resolver = NULL; + grpc_iomgr_closure *on_consumed = op->on_consumed; + op->on_consumed = NULL; + + GPR_ASSERT(op->set_accept_stream == NULL); + GPR_ASSERT(op->bind_pollset == NULL); + + gpr_mu_lock(&chand->mu_config); + if (op->on_connectivity_state_change != NULL) { + grpc_connectivity_state_notify_on_state_change( + &chand->state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + op->on_connectivity_state_change = NULL; + op->connectivity_state = NULL; + } - switch (op->type) { - case GRPC_CHANNEL_GOAWAY: - /* sending goaway: clear out the active child on the way through */ - gpr_mu_lock(&chand->mu); - child_channel = chand->active_child; - chand->active_child = NULL; - gpr_mu_unlock(&chand->mu); - if (child_channel) { - grpc_child_channel_handle_op(child_channel, op); - grpc_child_channel_destroy(child_channel, 1); - } else { - gpr_slice_unref(op->data.goaway.message); - } - break; - case GRPC_CHANNEL_DISCONNECT: - /* sending disconnect: clear out the active child on the way through */ - gpr_mu_lock(&chand->mu); - child_channel = chand->active_child; - chand->active_child = NULL; - gpr_mu_unlock(&chand->mu); - if (child_channel) { - grpc_child_channel_destroy(child_channel, 1); - } - /* fake a transport closed to satisfy the refcounting in client */ - rop.type = GRPC_TRANSPORT_CLOSED; - rop.dir = GRPC_CALL_UP; - grpc_channel_next_op(elem, &rop); - break; - case GRPC_TRANSPORT_GOAWAY: - /* receiving goaway: if it's from our active child, drop the active child; - in all cases consume the event here */ - gpr_mu_lock(&chand->mu); - child_channel = grpc_channel_stack_from_top_element(from_elem); - if (child_channel == chand->active_child) { - chand->active_child = NULL; - } else { - child_channel = NULL; - } - gpr_mu_unlock(&chand->mu); - if (child_channel) { - grpc_child_channel_destroy(child_channel, 0); - } - gpr_slice_unref(op->data.goaway.message); - break; - case GRPC_TRANSPORT_CLOSED: - /* receiving disconnect: if it's from our active child, drop the active - child; in all cases consume the event here */ - gpr_mu_lock(&chand->mu); - child_channel = grpc_channel_stack_from_top_element(from_elem); - if (child_channel == chand->active_child) { - chand->active_child = NULL; - } else { - child_channel = NULL; - } - gpr_mu_unlock(&chand->mu); - if (child_channel) { - grpc_child_channel_destroy(child_channel, 0); - } - break; - default: - switch (op->dir) { - case GRPC_CALL_UP: - grpc_channel_next_op(elem, op); - break; - case GRPC_CALL_DOWN: - gpr_log(GPR_ERROR, "unhandled channel op: %d", op->type); - abort(); - break; - } - break; + if (op->disconnect && chand->resolver != NULL) { + grpc_connectivity_state_set(&chand->state_tracker, + GRPC_CHANNEL_FATAL_FAILURE); + destroy_resolver = chand->resolver; + chand->resolver = NULL; + if (chand->lb_policy != NULL) { + grpc_lb_policy_shutdown(chand->lb_policy); + } + } + + if (!is_empty(op, sizeof(*op))) { + lb_policy = chand->lb_policy; + if (lb_policy) { + GRPC_LB_POLICY_REF(lb_policy, "broadcast"); + } + } + gpr_mu_unlock(&chand->mu_config); + + if (destroy_resolver) { + grpc_resolver_shutdown(destroy_resolver); + GRPC_RESOLVER_UNREF(destroy_resolver, "channel"); + } + + if (lb_policy) { + grpc_lb_policy_broadcast(lb_policy, op); + GRPC_LB_POLICY_UNREF(lb_policy, "broadcast"); + } + + if (on_consumed) { + grpc_iomgr_add_callback(on_consumed); } } /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; /* TODO(ctiller): is there something useful we can do here? */ @@ -376,6 +515,7 @@ static void init_call_elem(grpc_call_element *elem, GPR_ASSERT(elem->filter == &grpc_client_channel_filter); GPR_ASSERT(server_transport_data == NULL); + gpr_mu_init(&calld->mu_state); calld->elem = elem; calld->state = CALL_CREATED; calld->deadline = gpr_inf_future; @@ -384,161 +524,88 @@ static void init_call_elem(grpc_call_element *elem, /* Destructor for call_data */ static void destroy_call_elem(grpc_call_element *elem) { call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; + grpc_subchannel_call *subchannel_call; /* if the call got activated, we need to destroy the child stack also, and remove it from the in-flight requests tracked by the child_entry we picked */ - gpr_mu_lock(&chand->mu); + gpr_mu_lock(&calld->mu_state); switch (calld->state) { case CALL_ACTIVE: - gpr_mu_unlock(&chand->mu); - grpc_child_call_destroy(calld->s.active.child_call); + subchannel_call = calld->subchannel_call; + gpr_mu_unlock(&calld->mu_state); + GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "client_channel"); break; - case CALL_WAITING: - remove_waiting_child(chand, calld); - gpr_mu_unlock(&chand->mu); + case CALL_CREATED: + case CALL_CANCELLED: + gpr_mu_unlock(&calld->mu_state); break; - default: - gpr_mu_unlock(&chand->mu); + case CALL_WAITING_FOR_PICK: + case CALL_WAITING_FOR_CONFIG: + case CALL_WAITING_FOR_CALL: + case CALL_WAITING_FOR_SEND: + gpr_log(GPR_ERROR, "should never reach here"); + abort(); break; } - GPR_ASSERT(calld->state != CALL_WAITING); } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { channel_data *chand = elem->channel_data; - GPR_ASSERT(!is_first); + memset(chand, 0, sizeof(*chand)); + GPR_ASSERT(is_last); GPR_ASSERT(elem->filter == &grpc_client_channel_filter); - gpr_mu_init(&chand->mu); - chand->active_child = NULL; - chand->waiting_children = NULL; - chand->waiting_child_count = 0; - chand->waiting_child_capacity = 0; - chand->transport_setup = NULL; - chand->transport_setup_initiated = 0; - chand->args = grpc_channel_args_copy(args); + gpr_mu_init(&chand->mu_config); chand->mdctx = metadata_context; + chand->master = master; + grpc_iomgr_closure_init(&chand->on_config_changed, cc_on_config_changed, + chand); + + grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE); } /* Destructor for channel_data */ static void destroy_channel_elem(grpc_channel_element *elem) { channel_data *chand = elem->channel_data; - grpc_transport_setup_cancel(chand->transport_setup); - - if (chand->active_child) { - grpc_child_channel_destroy(chand->active_child, 1); - chand->active_child = NULL; + if (chand->resolver != NULL) { + grpc_resolver_shutdown(chand->resolver); + GRPC_RESOLVER_UNREF(chand->resolver, "channel"); } - - grpc_channel_args_destroy(chand->args); - - gpr_mu_destroy(&chand->mu); - GPR_ASSERT(chand->waiting_child_count == 0); - gpr_free(chand->waiting_children); + if (chand->lb_policy != NULL) { + GRPC_LB_POLICY_UNREF(chand->lb_policy, "channel"); + } + gpr_mu_destroy(&chand->mu_config); } const grpc_channel_filter grpc_client_channel_filter = { - cc_start_transport_op, channel_op, sizeof(call_data), - init_call_elem, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "client-channel", + cc_start_transport_stream_op, + cc_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "client-channel", }; -grpc_transport_setup_result grpc_client_channel_transport_setup_complete( - grpc_channel_stack *channel_stack, grpc_transport *transport, - grpc_channel_filter const **channel_filters, size_t num_channel_filters, - grpc_mdctx *mdctx) { - /* we just got a new transport: lets create a child channel stack for it */ - grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); - channel_data *chand = elem->channel_data; - size_t num_child_filters = 2 + num_channel_filters; - grpc_channel_filter const **child_filters; - grpc_transport_setup_result result; - grpc_child_channel *old_active = NULL; - call_data **waiting_children; - size_t waiting_child_count; - size_t i; - grpc_transport_op *call_ops; - - /* build the child filter stack */ - child_filters = gpr_malloc(sizeof(grpc_channel_filter *) * num_child_filters); - /* we always need a link back filter to get back to the connected channel */ - child_filters[0] = &grpc_child_channel_top_filter; - for (i = 0; i < num_channel_filters; i++) { - child_filters[i + 1] = channel_filters[i]; - } - /* and we always need a connected channel to talk to the transport */ - child_filters[num_child_filters - 1] = &grpc_connected_channel_filter; - - GPR_ASSERT(elem->filter == &grpc_client_channel_filter); - - /* BEGIN LOCKING CHANNEL */ - gpr_mu_lock(&chand->mu); - chand->transport_setup_initiated = 0; - - if (chand->active_child) { - old_active = chand->active_child; - } - chand->active_child = grpc_child_channel_create( - elem, child_filters, num_child_filters, chand->args, mdctx); - result = - grpc_connected_channel_bind_transport(chand->active_child, transport); - - /* capture the waiting children - we'll activate them outside the lock - to avoid re-entrancy problems */ - waiting_children = chand->waiting_children; - waiting_child_count = chand->waiting_child_count; - /* bumping up inflight_requests here avoids taking a lock per rpc below */ - - chand->waiting_children = NULL; - chand->waiting_child_count = 0; - chand->waiting_child_capacity = 0; - - call_ops = gpr_malloc(sizeof(*call_ops) * waiting_child_count); - - for (i = 0; i < waiting_child_count; i++) { - call_ops[i] = waiting_children[i]->s.waiting_op; - if (!prepare_activate(waiting_children[i]->elem, chand->active_child)) { - waiting_children[i] = NULL; - grpc_transport_op_finish_with_failure(&call_ops[i]); - } - } - - /* END LOCKING CHANNEL */ - gpr_mu_unlock(&chand->mu); - - /* activate any pending operations - this is safe to do as we guarantee one - and only one write operation per request at the surface api - if we lose - that guarantee we need to do some curly locking here */ - for (i = 0; i < waiting_child_count; i++) { - if (waiting_children[i]) { - complete_activate(waiting_children[i]->elem, &call_ops[i]); - } - } - gpr_free(waiting_children); - gpr_free(call_ops); - gpr_free(child_filters); - - if (old_active) { - grpc_child_channel_destroy(old_active, 1); - } - - return result; -} - -void grpc_client_channel_set_transport_setup(grpc_channel_stack *channel_stack, - grpc_transport_setup *setup) { +void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, + grpc_resolver *resolver) { /* post construction initialization: set the transport setup pointer */ grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); channel_data *chand = elem->channel_data; - GPR_ASSERT(!chand->transport_setup); - chand->transport_setup = setup; + GPR_ASSERT(!chand->resolver); + chand->resolver = resolver; + GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + GRPC_RESOLVER_REF(resolver, "channel"); + grpc_resolver_next(resolver, &chand->incoming_configuration, + &chand->on_config_changed); } diff --git a/src/core/channel/client_channel.h b/src/core/channel/client_channel.h index 7a67a9f21f..fd2be46145 100644 --- a/src/core/channel/client_channel.h +++ b/src/core/channel/client_channel.h @@ -35,6 +35,7 @@ #define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H #include "src/core/channel/channel_stack.h" +#include "src/core/client_config/resolver.h" /* A client channel is a channel that begins disconnected, and can connect to some endpoint on demand. If that endpoint disconnects, it will be @@ -48,15 +49,7 @@ extern const grpc_channel_filter grpc_client_channel_filter; /* post-construction initializer to let the client channel know which transport setup it should cancel upon destruction, or initiate when it needs a connection */ -void grpc_client_channel_set_transport_setup(grpc_channel_stack *channel_stack, - grpc_transport_setup *setup); +void grpc_client_channel_set_resolver(grpc_channel_stack *channel_stack, + grpc_resolver *resolver); -/* grpc_transport_setup_callback for binding new transports into a client - channel - user_data should be the channel stack containing the client - channel */ -grpc_transport_setup_result grpc_client_channel_transport_setup_complete( - grpc_channel_stack *channel_stack, grpc_transport *transport, - grpc_channel_filter const **channel_filters, size_t num_channel_filters, - grpc_mdctx *mdctx); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/src/core/channel/client_setup.c b/src/core/channel/client_setup.c deleted file mode 100644 index 5be8fa66e9..0000000000 --- a/src/core/channel/client_setup.c +++ /dev/null @@ -1,302 +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. - * - */ - -#include "src/core/channel/client_setup.h" -#include "src/core/channel/channel_args.h" -#include "src/core/channel/channel_stack.h" -#include "src/core/iomgr/alarm.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/time.h> - -struct grpc_client_setup { - grpc_transport_setup base; /* must be first */ - void (*initiate)(void *user_data, grpc_client_setup_request *request); - void (*done)(void *user_data); - void *user_data; - grpc_channel_args *args; - grpc_mdctx *mdctx; - grpc_alarm backoff_alarm; - gpr_timespec current_backoff_interval; - int in_alarm; - int in_cb; - int cancelled; - - gpr_mu mu; - gpr_cv cv; - grpc_client_setup_request *active_request; - int refs; - /** The set of pollsets that are currently interested in this - connection being established */ - grpc_pollset_set interested_parties; -}; - -struct grpc_client_setup_request { - /* pointer back to the setup object */ - grpc_client_setup *setup; - gpr_timespec deadline; -}; - -gpr_timespec grpc_client_setup_request_deadline(grpc_client_setup_request *r) { - return r->deadline; -} - -grpc_pollset_set *grpc_client_setup_get_interested_parties( - grpc_client_setup_request *r) { - return &r->setup->interested_parties; -} - -static void destroy_setup(grpc_client_setup *s) { - gpr_mu_destroy(&s->mu); - gpr_cv_destroy(&s->cv); - s->done(s->user_data); - grpc_channel_args_destroy(s->args); - grpc_pollset_set_destroy(&s->interested_parties); - gpr_free(s); -} - -static void destroy_request(grpc_client_setup_request *r) { gpr_free(r); } - -/* initiate handshaking */ -static void setup_initiate(grpc_transport_setup *sp) { - grpc_client_setup *s = (grpc_client_setup *)sp; - grpc_client_setup_request *r = gpr_malloc(sizeof(grpc_client_setup_request)); - int in_alarm = 0; - - r->setup = s; - r->deadline = gpr_time_add(gpr_now(), gpr_time_from_seconds(60)); - - gpr_mu_lock(&s->mu); - GPR_ASSERT(s->refs > 0); - /* there might be more than one request outstanding if the caller calls - initiate in some kind of rapid-fire way: we try to connect each time, - and keep track of the latest request (which is the only one that gets - to finish) */ - if (!s->in_alarm) { - s->active_request = r; - s->refs++; - } else { - /* TODO(klempner): Maybe do something more clever here */ - in_alarm = 1; - } - gpr_mu_unlock(&s->mu); - - if (!in_alarm) { - s->initiate(s->user_data, r); - } else { - destroy_request(r); - } -} - -/** implementation of add_interested_party for setup vtable */ -static void setup_add_interested_party(grpc_transport_setup *sp, - grpc_pollset *pollset) { - grpc_client_setup *s = (grpc_client_setup *)sp; - - gpr_mu_lock(&s->mu); - grpc_pollset_set_add_pollset(&s->interested_parties, pollset); - gpr_mu_unlock(&s->mu); -} - -/** implementation of del_interested_party for setup vtable */ -static void setup_del_interested_party(grpc_transport_setup *sp, - grpc_pollset *pollset) { - grpc_client_setup *s = (grpc_client_setup *)sp; - - gpr_mu_lock(&s->mu); - grpc_pollset_set_del_pollset(&s->interested_parties, pollset); - gpr_mu_unlock(&s->mu); -} - -/* cancel handshaking: cancel all requests, and shutdown (the caller promises - not to initiate again) */ -static void setup_cancel(grpc_transport_setup *sp) { - grpc_client_setup *s = (grpc_client_setup *)sp; - int cancel_alarm = 0; - - gpr_mu_lock(&s->mu); - s->cancelled = 1; - while (s->in_cb) { - gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future); - } - - GPR_ASSERT(s->refs > 0); - /* effectively cancels the current request (if any) */ - s->active_request = NULL; - if (s->in_alarm) { - cancel_alarm = 1; - } - if (--s->refs == 0) { - gpr_mu_unlock(&s->mu); - destroy_setup(s); - } else { - gpr_mu_unlock(&s->mu); - } - if (cancel_alarm) { - grpc_alarm_cancel(&s->backoff_alarm); - } -} - -int grpc_client_setup_cb_begin(grpc_client_setup_request *r, - const char *reason) { - gpr_mu_lock(&r->setup->mu); - if (r->setup->cancelled) { - gpr_mu_unlock(&r->setup->mu); - return 0; - } - r->setup->in_cb++; - gpr_mu_unlock(&r->setup->mu); - return 1; -} - -void grpc_client_setup_cb_end(grpc_client_setup_request *r, - const char *reason) { - gpr_mu_lock(&r->setup->mu); - r->setup->in_cb--; - if (r->setup->cancelled) gpr_cv_signal(&r->setup->cv); - gpr_mu_unlock(&r->setup->mu); -} - -/* vtable for transport setup */ -static const grpc_transport_setup_vtable setup_vtable = { - setup_initiate, setup_add_interested_party, setup_del_interested_party, - setup_cancel}; - -void grpc_client_setup_create_and_attach( - grpc_channel_stack *newly_minted_channel, const grpc_channel_args *args, - grpc_mdctx *mdctx, - void (*initiate)(void *user_data, grpc_client_setup_request *request), - void (*done)(void *user_data), void *user_data) { - grpc_client_setup *s = gpr_malloc(sizeof(grpc_client_setup)); - - s->base.vtable = &setup_vtable; - gpr_mu_init(&s->mu); - gpr_cv_init(&s->cv); - s->refs = 1; - s->mdctx = mdctx; - s->initiate = initiate; - s->done = done; - s->user_data = user_data; - s->active_request = NULL; - s->args = grpc_channel_args_copy(args); - s->current_backoff_interval = gpr_time_from_micros(1000000); - s->in_alarm = 0; - s->in_cb = 0; - s->cancelled = 0; - grpc_pollset_set_init(&s->interested_parties); - - grpc_client_channel_set_transport_setup(newly_minted_channel, &s->base); -} - -int grpc_client_setup_request_should_continue(grpc_client_setup_request *r, - const char *reason) { - int result; - if (gpr_time_cmp(gpr_now(), r->deadline) > 0) { - result = 0; - } else { - gpr_mu_lock(&r->setup->mu); - result = r->setup->active_request == r; - gpr_mu_unlock(&r->setup->mu); - } - return result; -} - -static void backoff_alarm_done(void *arg /* grpc_client_setup_request */, - int success) { - grpc_client_setup_request *r = arg; - grpc_client_setup *s = r->setup; - /* Handle status cancelled? */ - gpr_mu_lock(&s->mu); - s->in_alarm = 0; - if (s->active_request != NULL || !success) { - if (0 == --s->refs) { - gpr_mu_unlock(&s->mu); - destroy_setup(s); - destroy_request(r); - return; - } else { - gpr_mu_unlock(&s->mu); - destroy_request(r); - return; - } - } - s->active_request = r; - gpr_mu_unlock(&s->mu); - s->initiate(s->user_data, r); -} - -void grpc_client_setup_request_finish(grpc_client_setup_request *r, - int was_successful) { - int retry = !was_successful; - grpc_client_setup *s = r->setup; - - gpr_mu_lock(&s->mu); - if (s->active_request == r) { - s->active_request = NULL; - } else { - retry = 0; - } - - if (!retry && 0 == --s->refs) { - gpr_mu_unlock(&s->mu); - destroy_setup(s); - destroy_request(r); - } else if (retry) { - /* TODO(klempner): Replace these values with further consideration. 2x is - probably too aggressive of a backoff. */ - gpr_timespec max_backoff = gpr_time_from_minutes(2); - gpr_timespec now = gpr_now(); - gpr_timespec deadline = gpr_time_add(s->current_backoff_interval, now); - GPR_ASSERT(!s->in_alarm); - s->in_alarm = 1; - grpc_alarm_init(&s->backoff_alarm, deadline, backoff_alarm_done, r, now); - s->current_backoff_interval = - gpr_time_add(s->current_backoff_interval, s->current_backoff_interval); - if (gpr_time_cmp(s->current_backoff_interval, max_backoff) > 0) { - s->current_backoff_interval = max_backoff; - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - destroy_request(r); - } -} - -const grpc_channel_args *grpc_client_setup_get_channel_args( - grpc_client_setup_request *r) { - return r->setup->args; -} - -grpc_mdctx *grpc_client_setup_get_mdctx(grpc_client_setup_request *r) { - return r->setup->mdctx; -} diff --git a/src/core/channel/client_setup.h b/src/core/channel/client_setup.h deleted file mode 100644 index 7d40338840..0000000000 --- a/src/core/channel/client_setup.h +++ /dev/null @@ -1,77 +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. - * - */ - -#ifndef GRPC_INTERNAL_CORE_CHANNEL_CLIENT_SETUP_H -#define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_SETUP_H - -#include "src/core/channel/client_channel.h" -#include "src/core/transport/metadata.h" -#include <grpc/support/time.h> - -/* Convenience API's to simplify transport setup */ - -typedef struct grpc_client_setup grpc_client_setup; -typedef struct grpc_client_setup_request grpc_client_setup_request; - -void grpc_client_setup_create_and_attach( - grpc_channel_stack *newly_minted_channel, const grpc_channel_args *args, - grpc_mdctx *mdctx, - void (*initiate)(void *user_data, grpc_client_setup_request *request), - void (*done)(void *user_data), void *user_data); - -/* Check that r is the active request: needs to be performed at each callback. - If this races, we'll have two connection attempts running at once and the - old one will get cleaned up in due course, which is fine. */ -int grpc_client_setup_request_should_continue(grpc_client_setup_request *r, - const char *reason); -void grpc_client_setup_request_finish(grpc_client_setup_request *r, - int was_successful); -const grpc_channel_args *grpc_client_setup_get_channel_args( - grpc_client_setup_request *r); - -/* Call before calling back into the setup listener, and call only if - this function returns 1. If it returns 1, also promise to call - grpc_client_setup_cb_end */ -int grpc_client_setup_cb_begin(grpc_client_setup_request *r, - const char *reason); -void grpc_client_setup_cb_end(grpc_client_setup_request *r, const char *reason); - -/* Get the deadline for a request passed in to initiate. Implementations should - make a best effort to honor this deadline. */ -gpr_timespec grpc_client_setup_request_deadline(grpc_client_setup_request *r); -grpc_pollset_set *grpc_client_setup_get_interested_parties( - grpc_client_setup_request *r); - -grpc_mdctx *grpc_client_setup_get_mdctx(grpc_client_setup_request *r); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_SETUP_H */ diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index 14dda88698..34d07de519 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -61,42 +61,27 @@ typedef struct connected_channel_call_data { void *unused; } call_data; /* Intercept a call operation and either push it directly up or translate it into transport stream operations */ -static void con_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { +static void con_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - grpc_transport_perform_op(chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), op); + grpc_transport_perform_stream_op(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), op); } -/* Currently we assume all channel operations should just be pushed up. */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { +static void con_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { channel_data *chand = elem->channel_data; - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - - switch (op->type) { - case GRPC_CHANNEL_GOAWAY: - grpc_transport_goaway(chand->transport, op->data.goaway.status, - op->data.goaway.message); - break; - case GRPC_CHANNEL_DISCONNECT: - grpc_transport_close(chand->transport); - break; - default: - GPR_ASSERT(op->dir == GRPC_CALL_UP); - grpc_channel_next_op(elem, op); - break; - } + grpc_transport_perform_op(chand->transport, op); } /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; int r; @@ -118,11 +103,10 @@ static void destroy_call_elem(grpc_call_element *elem) { } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { channel_data *cd = (channel_data *)elem->channel_data; - GPR_ASSERT(!is_first); GPR_ASSERT(is_last); GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); cd->transport = NULL; @@ -136,70 +120,23 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_connected_channel_filter = { - con_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "connected", + con_start_transport_stream_op, + con_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "connected", }; -/* Transport callback to accept a new stream... calls up to handle it */ -static void accept_stream(void *user_data, grpc_transport *transport, - const void *transport_server_data) { - grpc_channel_element *elem = user_data; - channel_data *chand = elem->channel_data; - grpc_channel_op op; - - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - GPR_ASSERT(chand->transport == transport); - - op.type = GRPC_ACCEPT_CALL; - op.dir = GRPC_CALL_UP; - op.data.accept_call.transport = transport; - op.data.accept_call.transport_server_data = transport_server_data; - channel_op(elem, NULL, &op); -} - -static void transport_goaway(void *user_data, grpc_transport *transport, - grpc_status_code status, gpr_slice debug) { - /* transport got goaway ==> call up and handle it */ - grpc_channel_element *elem = user_data; - channel_data *chand = elem->channel_data; - grpc_channel_op op; - - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - GPR_ASSERT(chand->transport == transport); - - op.type = GRPC_TRANSPORT_GOAWAY; - op.dir = GRPC_CALL_UP; - op.data.goaway.status = status; - op.data.goaway.message = debug; - channel_op(elem, NULL, &op); -} - -static void transport_closed(void *user_data, grpc_transport *transport) { - /* transport was closed ==> call up and handle it */ - grpc_channel_element *elem = user_data; - channel_data *chand = elem->channel_data; - grpc_channel_op op; - - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - GPR_ASSERT(chand->transport == transport); - - op.type = GRPC_TRANSPORT_CLOSED; - op.dir = GRPC_CALL_UP; - channel_op(elem, NULL, &op); -} - -const grpc_transport_callbacks connected_channel_transport_callbacks = { - accept_stream, transport_goaway, transport_closed, -}; - -grpc_transport_setup_result grpc_connected_channel_bind_transport( - grpc_channel_stack *channel_stack, grpc_transport *transport) { +void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, + grpc_transport *transport) { /* Assumes that the connected channel filter is always the last filter in a channel stack */ grpc_channel_element *elem = grpc_channel_stack_last_element(channel_stack); channel_data *cd = (channel_data *)elem->channel_data; - grpc_transport_setup_result ret; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); GPR_ASSERT(cd->transport == NULL); cd->transport = transport; @@ -211,8 +148,4 @@ grpc_transport_setup_result grpc_connected_channel_bind_transport( the last call element, and the last call element MUST be the connected channel. */ channel_stack->call_stack_size += grpc_transport_stream_size(transport); - - ret.user_data = elem; - ret.callbacks = &connected_channel_transport_callbacks; - return ret; } diff --git a/src/core/channel/connected_channel.h b/src/core/channel/connected_channel.h index 8b35f69b26..b615b0d350 100644 --- a/src/core/channel/connected_channel.h +++ b/src/core/channel/connected_channel.h @@ -43,7 +43,7 @@ extern const grpc_channel_filter grpc_connected_channel_filter; /* Post construction fixup: set the transport in the connected channel. Must be called before any call stack using this filter is used. */ -grpc_transport_setup_result grpc_connected_channel_bind_transport( - grpc_channel_stack *channel_stack, grpc_transport *transport); +void grpc_connected_channel_bind_transport(grpc_channel_stack *channel_stack, + grpc_transport *transport); -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H */ +#endif /* GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H */ diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c index 08a2c0df3c..581eb13f58 100644 --- a/src/core/channel/http_client_filter.c +++ b/src/core/channel/http_client_filter.c @@ -92,7 +92,8 @@ static void hc_on_recv(void *user_data, int success) { calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); } -static void hc_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { +static void hc_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *channeld = elem->channel_data; @@ -127,33 +128,16 @@ static void hc_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { } static void hc_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); hc_mutate_op(elem, op); grpc_call_next_op(elem, op); } -/* Called on special channel events, such as disconnection or new incoming - calls on the server */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - /* grab pointers to our data from the channel element */ - channel_data *channeld = elem->channel_data; - - ignore_unused(channeld); - - switch (op->type) { - default: - /* pass control up or down the stack depending on op->dir */ - grpc_channel_next_op(elem, op); - break; - } -} - /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; calld->sent_initial_metadata = 0; calld->got_initial_metadata = 0; @@ -186,7 +170,7 @@ static const char *scheme_from_args(const grpc_channel_args *args) { } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -195,7 +179,6 @@ static void init_channel_elem(grpc_channel_element *elem, /* The first and the last filters tend to be implemented differently to handle the case that there's no 'next' filter to call on the up or down path */ - GPR_ASSERT(!is_first); GPR_ASSERT(!is_last); /* initialize members */ @@ -221,6 +204,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_http_client_filter = { - hc_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "http-client"}; + hc_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, "http-client"}; diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c index d3a01fd1a8..db0bf590c6 100644 --- a/src/core/channel/http_server_filter.c +++ b/src/core/channel/http_server_filter.c @@ -72,9 +72,6 @@ typedef struct channel_data { grpc_mdctx *mdctx; } channel_data; -/* used to silence 'variable not used' warnings */ -static void ignore_unused(void *ignored) {} - static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { grpc_call_element *elem = user_data; channel_data *channeld = elem->channel_data; @@ -181,7 +178,8 @@ static void hs_on_recv(void *user_data, int success) { calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); } -static void hs_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { +static void hs_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *channeld = elem->channel_data; @@ -209,33 +207,16 @@ static void hs_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { } static void hs_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); hs_mutate_op(elem, op); grpc_call_next_op(elem, op); } -/* Called on special channel events, such as disconnection or new incoming - calls on the server */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - /* grab pointers to our data from the channel element */ - channel_data *channeld = elem->channel_data; - - ignore_unused(channeld); - - switch (op->type) { - default: - /* pass control up or down the stack depending on op->dir */ - grpc_channel_next_op(elem, op); - break; - } -} - /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; /* initialize members */ @@ -248,7 +229,7 @@ static void init_call_elem(grpc_call_element *elem, static void destroy_call_elem(grpc_call_element *elem) {} /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -297,6 +278,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_http_server_filter = { - hs_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "http-server"}; + hs_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, "http-server"}; diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c index 1d2be716d7..5117723617 100644 --- a/src/core/channel/noop_filter.c +++ b/src/core/channel/noop_filter.c @@ -45,7 +45,8 @@ typedef struct channel_data { /* used to silence 'variable not used' warnings */ static void ignore_unused(void *ignored) {} -static void noop_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { +static void noop_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *channeld = elem->channel_data; @@ -61,35 +62,18 @@ static void noop_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { - a network event (or similar) from below, to receive something op contains type and call direction information, in addition to the data that is being sent or received. */ -static void noop_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { +static void noop_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { noop_mutate_op(elem, op); /* pass control down the stack */ grpc_call_next_op(elem, op); } -/* Called on special channel events, such as disconnection or new incoming - calls on the server */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - /* grab pointers to our data from the channel element */ - channel_data *channeld = elem->channel_data; - - ignore_unused(channeld); - - switch (op->type) { - default: - /* pass control up or down the stack depending on op->dir */ - grpc_channel_next_op(elem, op); - break; - } -} - /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *channeld = elem->channel_data; @@ -111,7 +95,7 @@ static void destroy_call_elem(grpc_call_element *elem) { } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { /* grab pointers to our data from the channel element */ @@ -135,7 +119,12 @@ static void destroy_channel_elem(grpc_channel_element *elem) { ignore_unused(channeld); } -const grpc_channel_filter grpc_no_op_filter = { - noop_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "no-op"}; +const grpc_channel_filter grpc_no_op_filter = {noop_start_transport_stream_op, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "no-op"}; diff --git a/src/core/client_config/README.md b/src/core/client_config/README.md new file mode 100644 index 0000000000..d7aed27223 --- /dev/null +++ b/src/core/client_config/README.md @@ -0,0 +1,60 @@ +Client Configuration Support for GRPC +===================================== + +This library provides high level configuration machinery to construct client +channels and load balance between them. + +Each grpc_channel is created with a grpc_resolver. It is the resolver's duty +to resolve a name into configuration data for the channel. Such configuration +data might include: + +- a list of (ip, port) addresses to connect to +- a load balancing policy to decide which server to send a request to +- a set of filters to mutate outgoing requests (say, by adding metadata) + +The resolver provides this data as a stream of grpc_client_config objects to +the channel. We represent configuration as a stream so that it can be changed +by the resolver during execution, by reacting to external events (such as a +new configuration file being pushed to some store). + + +Load Balancing +-------------- + +Load balancing configuration is provided by a grpc_lb_policy object, stored as +part of grpc_client_config. + +A load balancing policies primary job is to pick a target server given only the +initial metadata for a request. It does this by providing a grpc_subchannel +object to the owning channel. + + +Sub-Channels +------------ + +A sub-channel provides a connection to a server for a client channel. It has a +connectivity state like a regular channel, and so can be connected or +disconnected. This connectivity state can be used to inform load balancing +decisions (for example, by avoiding disconnected backends). + +Configured sub-channels are fully setup to participate in the grpc data plane. +Their behavior is specified by a set of grpc channel filters defined at their +construction. To customize this behavior, resolvers build grpc_subchannel_factory +objects, which use the decorator pattern to customize construction arguments for +concrete grpc_subchannel instances. + + +Naming for GRPC +=============== + +Names in GRPC are represented by a URI. + +The following schemes are currently supported: + +dns:///host:port - dns schemes are currently supported so long as authority is + empty (authority based dns resolution is expected in a future + release) + +unix:path - the unix scheme is used to create and connect to unix domain + sockets - the authority must be empty, and the path represents + the absolute or relative path to the desired socket diff --git a/src/core/client_config/client_config.c b/src/core/client_config/client_config.c new file mode 100644 index 0000000000..4453824148 --- /dev/null +++ b/src/core/client_config/client_config.c @@ -0,0 +1,74 @@ +/* + * + * 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/client_config/client_config.h" + +#include <string.h> + +#include <grpc/support/alloc.h> + +struct grpc_client_config { + gpr_refcount refs; + grpc_lb_policy *lb_policy; +}; + +grpc_client_config *grpc_client_config_create() { + grpc_client_config *c = gpr_malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + gpr_ref_init(&c->refs, 1); + return c; +} + +void grpc_client_config_ref(grpc_client_config *c) { gpr_ref(&c->refs); } + +void grpc_client_config_unref(grpc_client_config *c) { + if (gpr_unref(&c->refs)) { + GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); + gpr_free(c); + } +} + +void grpc_client_config_set_lb_policy(grpc_client_config *c, + grpc_lb_policy *lb_policy) { + if (lb_policy) { + GRPC_LB_POLICY_REF(lb_policy, "client_config"); + } + if (c->lb_policy) { + GRPC_LB_POLICY_UNREF(c->lb_policy, "client_config"); + } + c->lb_policy = lb_policy; +} + +grpc_lb_policy *grpc_client_config_get_lb_policy(grpc_client_config *c) { + return c->lb_policy; +} diff --git a/src/core/client_config/client_config.h b/src/core/client_config/client_config.h new file mode 100644 index 0000000000..47612da42c --- /dev/null +++ b/src/core/client_config/client_config.h @@ -0,0 +1,52 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H + +#include "src/core/client_config/lb_policy.h" + +/** Total configuration for a client. Provided, and updated, by + grpc_resolver */ +typedef struct grpc_client_config grpc_client_config; + +grpc_client_config *grpc_client_config_create(); +void grpc_client_config_ref(grpc_client_config *client_config); +void grpc_client_config_unref(grpc_client_config *client_config); + +void grpc_client_config_set_lb_policy(grpc_client_config *client_config, + grpc_lb_policy *lb_policy); +grpc_lb_policy *grpc_client_config_get_lb_policy( + grpc_client_config *client_config); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_CLIENT_CONFIG_H */ diff --git a/src/core/client_config/connector.c b/src/core/client_config/connector.c new file mode 100644 index 0000000000..a8cd5fc149 --- /dev/null +++ b/src/core/client_config/connector.c @@ -0,0 +1,49 @@ +/* + * + * 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/client_config/connector.h" + +void grpc_connector_ref(grpc_connector *connector) { + connector->vtable->ref(connector); +} + +void grpc_connector_unref(grpc_connector *connector) { + connector->vtable->unref(connector); +} + +void grpc_connector_connect(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, + grpc_iomgr_closure *notify) { + connector->vtable->connect(connector, in_args, out_args, notify); +} diff --git a/src/core/client_config/connector.h b/src/core/client_config/connector.h new file mode 100644 index 0000000000..edcb10a36e --- /dev/null +++ b/src/core/client_config/connector.h @@ -0,0 +1,85 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_CONNECTOR_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_CONNECTOR_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/iomgr/sockaddr.h" +#include "src/core/transport/transport.h" + +typedef struct grpc_connector grpc_connector; +typedef struct grpc_connector_vtable grpc_connector_vtable; + +struct grpc_connector { + const grpc_connector_vtable *vtable; +}; + +typedef struct { + /** set of pollsets interested in this connection */ + grpc_pollset_set *interested_parties; + /** address to connect to */ + const struct sockaddr *addr; + int addr_len; + /** deadline for connection */ + gpr_timespec deadline; + /** channel arguments (to be passed to transport) */ + const grpc_channel_args *channel_args; + /** metadata context */ + grpc_mdctx *metadata_context; +} grpc_connect_in_args; + +typedef struct { + /** the connected transport */ + grpc_transport *transport; + /** any additional filters (owned by the caller of connect) */ + const grpc_channel_filter **filters; + size_t num_filters; +} grpc_connect_out_args; + +struct grpc_connector_vtable { + void (*ref)(grpc_connector *connector); + void (*unref)(grpc_connector *connector); + void (*connect)(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, grpc_iomgr_closure *notify); +}; + +void grpc_connector_ref(grpc_connector *connector); +void grpc_connector_unref(grpc_connector *connector); +void grpc_connector_connect(grpc_connector *connector, + const grpc_connect_in_args *in_args, + grpc_connect_out_args *out_args, + grpc_iomgr_closure *notify); + +#endif diff --git a/src/core/client_config/lb_policies/pick_first.c b/src/core/client_config/lb_policies/pick_first.c new file mode 100644 index 0000000000..73da624aff --- /dev/null +++ b/src/core/client_config/lb_policies/pick_first.c @@ -0,0 +1,268 @@ +/* + * + * 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/client_config/lb_policies/pick_first.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include "src/core/transport/connectivity_state.h" + +typedef struct pending_pick { + struct pending_pick *next; + grpc_pollset *pollset; + grpc_subchannel **target; + grpc_iomgr_closure *on_complete; +} pending_pick; + +typedef struct { + /** base policy: must be first */ + grpc_lb_policy base; + /** all our subchannels */ + grpc_subchannel **subchannels; + size_t num_subchannels; + + grpc_iomgr_closure connectivity_changed; + + /** mutex protecting remaining members */ + gpr_mu mu; + /** the selected channel + TODO(ctiller): this should be atomically set so we don't + need to take a mutex in the common case */ + grpc_subchannel *selected; + /** have we started picking? */ + int started_picking; + /** which subchannel are we watching? */ + size_t checking_subchannel; + /** what is the connectivity of that channel? */ + grpc_connectivity_state checking_connectivity; + /** list of picks that are waiting on connectivity */ + pending_pick *pending_picks; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; +} pick_first_lb_policy; + +void pf_destroy(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + size_t i; + for (i = 0; i < p->num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(p->subchannels[i], "pick_first"); + } + gpr_free(p->subchannels); + gpr_mu_destroy(&p->mu); + gpr_free(p); +} + +void pf_shutdown(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + gpr_mu_lock(&p->mu); + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_iomgr_add_delayed_callback(pp->on_complete, 0); + gpr_free(pp); + } + gpr_mu_unlock(&p->mu); +} + +void pf_pick(grpc_lb_policy *pol, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, grpc_subchannel **target, + grpc_iomgr_closure *on_complete) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + gpr_mu_lock(&p->mu); + if (p->selected) { + gpr_mu_unlock(&p->mu); + *target = p->selected; + on_complete->cb(on_complete->cb_arg, 1); + } else { + if (!p->started_picking) { + p->started_picking = 1; + p->checking_subchannel = 0; + p->checking_connectivity = GRPC_CHANNEL_IDLE; + GRPC_LB_POLICY_REF(pol, "pick_first_connectivity"); + grpc_subchannel_notify_on_state_change( + p->subchannels[p->checking_subchannel], &p->checking_connectivity, + &p->connectivity_changed); + } + grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], + pollset); + pp = gpr_malloc(sizeof(*pp)); + pp->next = p->pending_picks; + pp->pollset = pollset; + pp->target = target; + pp->on_complete = on_complete; + p->pending_picks = pp; + gpr_mu_unlock(&p->mu); + } +} + +static void del_interested_parties_locked(pick_first_lb_policy *p) { + pending_pick *pp; + for (pp = p->pending_picks; pp; pp = pp->next) { + grpc_subchannel_del_interested_party(p->subchannels[p->checking_subchannel], + pp->pollset); + } +} + +static void add_interested_parties_locked(pick_first_lb_policy *p) { + pending_pick *pp; + for (pp = p->pending_picks; pp; pp = pp->next) { + grpc_subchannel_add_interested_party(p->subchannels[p->checking_subchannel], + pp->pollset); + } +} + +static void pf_connectivity_changed(void *arg, int iomgr_success) { + pick_first_lb_policy *p = arg; + pending_pick *pp; + int unref = 0; + + gpr_mu_lock(&p->mu); +loop: + switch (p->checking_connectivity) { + case GRPC_CHANNEL_READY: + p->selected = p->subchannels[p->checking_subchannel]; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = p->selected; + grpc_subchannel_del_interested_party(p->selected, pp->pollset); + grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + gpr_free(pp); + } + unref = 1; + break; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + del_interested_parties_locked(p); + p->checking_subchannel = + (p->checking_subchannel + 1) % p->num_subchannels; + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel]); + add_interested_parties_locked(p); + goto loop; + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + grpc_subchannel_notify_on_state_change( + p->subchannels[p->checking_subchannel], &p->checking_connectivity, + &p->connectivity_changed); + break; + case GRPC_CHANNEL_FATAL_FAILURE: + del_interested_parties_locked(p); + GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], + p->subchannels[p->num_subchannels - 1]); + p->num_subchannels--; + GRPC_SUBCHANNEL_UNREF(p->subchannels[p->num_subchannels], "pick_first"); + if (p->num_subchannels == 0) { + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + grpc_iomgr_add_delayed_callback(pp->on_complete, 1); + gpr_free(pp); + } + unref = 1; + } else { + p->checking_subchannel %= p->num_subchannels; + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel]); + add_interested_parties_locked(p); + goto loop; + } + } + gpr_mu_unlock(&p->mu); + + if (unref) { + GRPC_LB_POLICY_UNREF(&p->base, "pick_first_connectivity"); + } +} + +static void pf_broadcast(grpc_lb_policy *pol, grpc_transport_op *op) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + size_t i; + size_t n; + grpc_subchannel **subchannels; + + gpr_mu_lock(&p->mu); + n = p->num_subchannels; + subchannels = gpr_malloc(n * sizeof(*subchannels)); + for (i = 0; i < n; i++) { + subchannels[i] = p->subchannels[i]; + GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast"); + } + gpr_mu_unlock(&p->mu); + + for (i = 0; i < n; i++) { + grpc_subchannel_process_transport_op(subchannels[i], op); + GRPC_SUBCHANNEL_UNREF(subchannels[i], "pf_broadcast"); + } + gpr_free(subchannels); +} + +static grpc_connectivity_state pf_check_connectivity(grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + grpc_connectivity_state st; + gpr_mu_lock(&p->mu); + st = grpc_connectivity_state_check(&p->state_tracker); + gpr_mu_unlock(&p->mu); + return st; +} + +static void pf_notify_on_state_change(grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_iomgr_closure *notify) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + gpr_mu_lock(&p->mu); + grpc_connectivity_state_notify_on_state_change(&p->state_tracker, current, + notify); + gpr_mu_unlock(&p->mu); +} + +static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { + pf_destroy, pf_shutdown, pf_pick, + pf_broadcast, pf_check_connectivity, pf_notify_on_state_change}; + +grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, + size_t num_subchannels) { + pick_first_lb_policy *p = gpr_malloc(sizeof(*p)); + GPR_ASSERT(num_subchannels); + memset(p, 0, sizeof(*p)); + grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable); + p->subchannels = gpr_malloc(sizeof(grpc_subchannel *) * num_subchannels); + p->num_subchannels = num_subchannels; + memcpy(p->subchannels, subchannels, + sizeof(grpc_subchannel *) * num_subchannels); + grpc_iomgr_closure_init(&p->connectivity_changed, pf_connectivity_changed, p); + gpr_mu_init(&p->mu); + return &p->base; +} diff --git a/src/core/surface/client.h b/src/core/client_config/lb_policies/pick_first.h index 9db2ccf3d2..94c2a9f0c7 100644 --- a/src/core/surface/client.h +++ b/src/core/client_config/lb_policies/pick_first.h @@ -31,11 +31,12 @@ * */ -#ifndef GRPC_INTERNAL_CORE_SURFACE_CLIENT_H -#define GRPC_INTERNAL_CORE_SURFACE_CLIENT_H +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_PICK_FIRST_H -#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/lb_policy.h" -extern const grpc_channel_filter grpc_client_surface_filter; +grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, + size_t num_subchannels); -#endif /* GRPC_INTERNAL_CORE_SURFACE_CLIENT_H */ +#endif diff --git a/src/core/client_config/lb_policy.c b/src/core/client_config/lb_policy.c new file mode 100644 index 0000000000..6d1c788742 --- /dev/null +++ b/src/core/client_config/lb_policy.c @@ -0,0 +1,79 @@ +/* + * + * 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/client_config/lb_policy.h" + +void grpc_lb_policy_init(grpc_lb_policy *policy, + const grpc_lb_policy_vtable *vtable) { + policy->vtable = vtable; + gpr_ref_init(&policy->refs, 1); +} + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p ref %d -> %d %s", + policy, (int)policy->refs.count, (int)policy->refs.count + 1, reason); +#else +void grpc_lb_policy_ref(grpc_lb_policy *policy) { +#endif + gpr_ref(&policy->refs); +} + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p unref %d -> %d %s", + policy, (int)policy->refs.count, (int)policy->refs.count - 1, reason); +#else +void grpc_lb_policy_unref(grpc_lb_policy *policy) { +#endif + if (gpr_unref(&policy->refs)) { + policy->vtable->destroy(policy); + } +} + +void grpc_lb_policy_shutdown(grpc_lb_policy *policy) { + policy->vtable->shutdown(policy); +} + +void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, + grpc_iomgr_closure *on_complete) { + policy->vtable->pick(policy, pollset, initial_metadata, target, on_complete); +} + +void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op) { + policy->vtable->broadcast(policy, op); +} diff --git a/src/core/client_config/lb_policy.h b/src/core/client_config/lb_policy.h new file mode 100644 index 0000000000..a468f761cc --- /dev/null +++ b/src/core/client_config/lb_policy.h @@ -0,0 +1,109 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_H + +#include "src/core/client_config/subchannel.h" + +/** A load balancing policy: specified by a vtable and a struct (which + is expected to be extended to contain some parameters) */ +typedef struct grpc_lb_policy grpc_lb_policy; +typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable; + +typedef void (*grpc_lb_completion)(void *cb_arg, grpc_subchannel *subchannel, + grpc_status_code status, const char *errmsg); + +struct grpc_lb_policy { + const grpc_lb_policy_vtable *vtable; + gpr_refcount refs; +}; + +struct grpc_lb_policy_vtable { + void (*destroy)(grpc_lb_policy *policy); + + void (*shutdown)(grpc_lb_policy *policy); + + /** implement grpc_lb_policy_pick */ + void (*pick)(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, grpc_subchannel **target, + grpc_iomgr_closure *on_complete); + + /** broadcast a transport op to all subchannels */ + void (*broadcast)(grpc_lb_policy *policy, grpc_transport_op *op); + + /** check the current connectivity of the lb_policy */ + grpc_connectivity_state (*check_connectivity)(grpc_lb_policy *policy); + + /** call notify when the connectivity state of a channel changes from *state. + Updates *state with the new state of the policy */ + void (*notify_on_state_change)(grpc_lb_policy *policy, + grpc_connectivity_state *state, + grpc_iomgr_closure *closure); +}; + +#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG +#define GRPC_LB_POLICY_REF(p, r) \ + grpc_lb_policy_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_LB_POLICY_UNREF(p, r) \ + grpc_lb_policy_unref((p), __FILE__, __LINE__, (r)) +void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, + const char *reason); +void grpc_lb_policy_unref(grpc_lb_policy *policy, const char *file, int line, + const char *reason); +#else +#define GRPC_LB_POLICY_REF(p, r) grpc_lb_policy_ref((p)) +#define GRPC_LB_POLICY_UNREF(p, r) grpc_lb_policy_unref((p)) +void grpc_lb_policy_ref(grpc_lb_policy *policy); +void grpc_lb_policy_unref(grpc_lb_policy *policy); +#endif + +/** called by concrete implementations to initialize the base struct */ +void grpc_lb_policy_init(grpc_lb_policy *policy, + const grpc_lb_policy_vtable *vtable); + +/** Start shutting down (fail any pending picks) */ +void grpc_lb_policy_shutdown(grpc_lb_policy *policy); + +/** Given initial metadata in \a initial_metadata, find an appropriate + target for this rpc, and 'return' it by calling \a on_complete after setting + \a target. + Picking can be asynchronous. Any IO should be done under \a pollset. */ +void grpc_lb_policy_pick(grpc_lb_policy *policy, grpc_pollset *pollset, + grpc_metadata_batch *initial_metadata, + grpc_subchannel **target, + grpc_iomgr_closure *on_complete); + +void grpc_lb_policy_broadcast(grpc_lb_policy *policy, grpc_transport_op *op); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_H */ diff --git a/src/core/client_config/resolver.c b/src/core/client_config/resolver.c new file mode 100644 index 0000000000..91e42bb684 --- /dev/null +++ b/src/core/client_config/resolver.c @@ -0,0 +1,83 @@ +/* + * + * 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/client_config/resolver.h" + +void grpc_resolver_init(grpc_resolver *resolver, + const grpc_resolver_vtable *vtable) { + resolver->vtable = vtable; + gpr_ref_init(&resolver->refs, 1); +} + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +void grpc_resolver_ref(grpc_resolver *resolver, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p ref %d -> %d %s", + resolver, (int)resolver->refs.count, (int)resolver->refs.count + 1, + reason); +#else +void grpc_resolver_ref(grpc_resolver *resolver) { +#endif + gpr_ref(&resolver->refs); +} + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +void grpc_resolver_unref(grpc_resolver *resolver, const char *file, int line, + const char *reason) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "RESOLVER:%p unref %d -> %d %s", + resolver, (int)resolver->refs.count, (int)resolver->refs.count - 1, + reason); +#else +void grpc_resolver_unref(grpc_resolver *resolver) { +#endif + if (gpr_unref(&resolver->refs)) { + resolver->vtable->destroy(resolver); + } +} + +void grpc_resolver_shutdown(grpc_resolver *resolver) { + resolver->vtable->shutdown(resolver); +} + +void grpc_resolver_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len) { + resolver->vtable->channel_saw_error(resolver, failing_address, + failing_address_len); +} + +void grpc_resolver_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + resolver->vtable->next(resolver, target_config, on_complete); +} diff --git a/src/core/client_config/resolver.h b/src/core/client_config/resolver.h new file mode 100644 index 0000000000..8ad87d789b --- /dev/null +++ b/src/core/client_config/resolver.h @@ -0,0 +1,97 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_H + +#include "src/core/client_config/client_config.h" +#include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/sockaddr.h" + +typedef struct grpc_resolver grpc_resolver; +typedef struct grpc_resolver_vtable grpc_resolver_vtable; + +/** grpc_resolver provides grpc_client_config objects to grpc_channel + objects */ +struct grpc_resolver { + const grpc_resolver_vtable *vtable; + gpr_refcount refs; +}; + +struct grpc_resolver_vtable { + void (*destroy)(grpc_resolver *resolver); + void (*shutdown)(grpc_resolver *resolver); + void (*channel_saw_error)(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len); + void (*next)(grpc_resolver *resolver, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); +}; + +#ifdef GRPC_RESOLVER_REFCOUNT_DEBUG +#define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_RESOLVER_UNREF(p, r) \ + grpc_resolver_unref((p), __FILE__, __LINE__, (r)) +void grpc_resolver_ref(grpc_resolver *policy, const char *file, int line, + const char *reason); +void grpc_resolver_unref(grpc_resolver *policy, const char *file, int line, + const char *reason); +#else +#define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p)) +#define GRPC_RESOLVER_UNREF(p, r) grpc_resolver_unref((p)) +void grpc_resolver_ref(grpc_resolver *policy); +void grpc_resolver_unref(grpc_resolver *policy); +#endif + +void grpc_resolver_init(grpc_resolver *resolver, + const grpc_resolver_vtable *vtable); + +void grpc_resolver_shutdown(grpc_resolver *resolver); + +/** Notification that the channel has seen an error on some address. + Can be used as a hint that re-resolution is desirable soon. */ +void grpc_resolver_channel_saw_error(grpc_resolver *resolver, + struct sockaddr *failing_address, + int failing_address_len); + +/** Get the next client config. Called by the channel to fetch a new + configuration. Expected to set *target_config with a new configuration, + and then schedule on_complete for execution. + + If resolution is fatally broken, set *target_config to NULL and + schedule on_complete. */ +void grpc_resolver_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_H */ diff --git a/src/core/client_config/resolver_factory.c b/src/core/client_config/resolver_factory.c new file mode 100644 index 0000000000..6721977e21 --- /dev/null +++ b/src/core/client_config/resolver_factory.c @@ -0,0 +1,50 @@ +/* + * + * 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/client_config/resolver_factory.h" + +void grpc_resolver_factory_ref(grpc_resolver_factory *factory) { + factory->vtable->ref(factory); +} + +void grpc_resolver_factory_unref(grpc_resolver_factory *factory) { + factory->vtable->unref(factory); +} + +/** Create a resolver instance for a name */ +grpc_resolver *grpc_resolver_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + if (!factory) return NULL; + return factory->vtable->create_resolver(factory, uri, subchannel_factory); +} diff --git a/src/core/client_config/resolver_factory.h b/src/core/client_config/resolver_factory.h new file mode 100644 index 0000000000..c5d85499c6 --- /dev/null +++ b/src/core/client_config/resolver_factory.h @@ -0,0 +1,67 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_FACTORY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_FACTORY_H + +#include "src/core/client_config/resolver.h" +#include "src/core/client_config/subchannel_factory.h" +#include "src/core/client_config/uri_parser.h" + +typedef struct grpc_resolver_factory grpc_resolver_factory; +typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable; + +/** grpc_resolver provides grpc_client_config objects to grpc_channel + objects */ +struct grpc_resolver_factory { + const grpc_resolver_factory_vtable *vtable; +}; + +struct grpc_resolver_factory_vtable { + void (*ref)(grpc_resolver_factory *factory); + void (*unref)(grpc_resolver_factory *factory); + + grpc_resolver *(*create_resolver)( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory); +}; + +void grpc_resolver_factory_ref(grpc_resolver_factory *resolver); +void grpc_resolver_factory_unref(grpc_resolver_factory *resolver); + +/** Create a resolver instance for a name */ +grpc_resolver *grpc_resolver_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory); + +#endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_FACTORY_H */ diff --git a/src/core/client_config/resolver_registry.c b/src/core/client_config/resolver_registry.c new file mode 100644 index 0000000000..16be2da994 --- /dev/null +++ b/src/core/client_config/resolver_registry.c @@ -0,0 +1,124 @@ +/* + * + * 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/client_config/resolver_registry.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#define MAX_RESOLVERS 10 + +typedef struct { + char *scheme; + grpc_resolver_factory *factory; +} registered_resolver; + +static registered_resolver g_all_of_the_resolvers[MAX_RESOLVERS]; +static int g_number_of_resolvers = 0; + +static char *g_default_resolver_scheme; + +void grpc_resolver_registry_init(const char *default_resolver_scheme) { + g_number_of_resolvers = 0; + g_default_resolver_scheme = gpr_strdup(default_resolver_scheme); +} + +void grpc_resolver_registry_shutdown(void) { + int i; + for (i = 0; i < g_number_of_resolvers; i++) { + gpr_free(g_all_of_the_resolvers[i].scheme); + grpc_resolver_factory_unref(g_all_of_the_resolvers[i].factory); + } + gpr_free(g_default_resolver_scheme); +} + +void grpc_register_resolver_type(const char *scheme, + grpc_resolver_factory *factory) { + int i; + for (i = 0; i < g_number_of_resolvers; i++) { + GPR_ASSERT(0 != strcmp(scheme, g_all_of_the_resolvers[i].scheme)); + } + GPR_ASSERT(g_number_of_resolvers != MAX_RESOLVERS); + g_all_of_the_resolvers[g_number_of_resolvers].scheme = gpr_strdup(scheme); + grpc_resolver_factory_ref(factory); + g_all_of_the_resolvers[g_number_of_resolvers].factory = factory; + g_number_of_resolvers++; +} + +static grpc_resolver_factory *lookup_factory(grpc_uri *uri) { + int i; + + /* handling NULL uri's here simplifies grpc_resolver_create */ + if (!uri) return NULL; + + for (i = 0; i < g_number_of_resolvers; i++) { + if (0 == strcmp(uri->scheme, g_all_of_the_resolvers[i].scheme)) { + return g_all_of_the_resolvers[i].factory; + } + } + + return NULL; +} + +grpc_resolver *grpc_resolver_create( + const char *name, grpc_subchannel_factory *subchannel_factory) { + grpc_uri *uri; + char *tmp; + grpc_resolver_factory *factory = NULL; + grpc_resolver *resolver; + + uri = grpc_uri_parse(name, 1); + factory = lookup_factory(uri); + if (factory == NULL && g_default_resolver_scheme != NULL) { + grpc_uri_destroy(uri); + gpr_asprintf(&tmp, "%s%s", g_default_resolver_scheme, name); + uri = grpc_uri_parse(tmp, 1); + factory = lookup_factory(uri); + if (factory == NULL) { + grpc_uri_destroy(grpc_uri_parse(name, 0)); + grpc_uri_destroy(grpc_uri_parse(tmp, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", name, tmp); + } + gpr_free(tmp); + } else if (factory == NULL) { + grpc_uri_destroy(grpc_uri_parse(name, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s'", name); + } + resolver = + grpc_resolver_factory_create_resolver(factory, uri, subchannel_factory); + grpc_uri_destroy(uri); + return resolver; +} diff --git a/src/core/client_config/resolver_registry.h b/src/core/client_config/resolver_registry.h new file mode 100644 index 0000000000..31aa47620a --- /dev/null +++ b/src/core/client_config/resolver_registry.h @@ -0,0 +1,62 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H + +#include "src/core/client_config/resolver_factory.h" + +void grpc_resolver_registry_init(const char *default_prefix); +void grpc_resolver_registry_shutdown(void); + +/** Register a resolver type. + URI's of \a scheme will be resolved with the given resolver. + If \a priority is greater than zero, then the resolver will be eligible + to resolve names that are passed in with no scheme. Higher priority + resolvers will be tried before lower priority schemes. */ +void grpc_register_resolver_type(const char *scheme, + grpc_resolver_factory *factory); + +/** Create a resolver given \a name. + First tries to parse \a name as a URI. If this succeeds, tries + to locate a registered resolver factory based on the URI scheme. + If parsing or location fails, prefixes default_prefix from + grpc_resolver_registry_init to name, and tries again (if default_prefix + was not NULL). + If a resolver factory was found, use it to instantiate a resolver and + return it. + If a resolver factory was not found, return NULL. */ +grpc_resolver *grpc_resolver_create( + const char *name, grpc_subchannel_factory *subchannel_factory); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_REGISTRY_H */ diff --git a/src/core/client_config/resolvers/dns_resolver.c b/src/core/client_config/resolvers/dns_resolver.c new file mode 100644 index 0000000000..ac401bc4d3 --- /dev/null +++ b/src/core/client_config/resolvers/dns_resolver.c @@ -0,0 +1,246 @@ +/* + * + * 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/client_config/resolvers/dns_resolver.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/string_util.h> + +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/support/string.h" + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** refcount */ + gpr_refcount refs; + /** name to resolve */ + char *name; + /** default port to use */ + char *default_port; + /** subchannel factory */ + grpc_subchannel_factory *subchannel_factory; + /** load balancing policy factory */ + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels); + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** are we currently resolving? */ + int resolving; + /** which version of resolved_config have we published? */ + int published_version; + /** which version of resolved_config is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_iomgr_closure *next_completion; + /** target config address for next completion */ + grpc_client_config **target_config; + /** current (fully resolved) config */ + grpc_client_config *resolved_config; +} dns_resolver; + +static void dns_destroy(grpc_resolver *r); + +static void dns_start_resolving_locked(dns_resolver *r); +static void dns_maybe_finish_next_locked(dns_resolver *r); + +static void dns_shutdown(grpc_resolver *r); +static void dns_channel_saw_error(grpc_resolver *r, + struct sockaddr *failing_address, + int failing_address_len); +static void dns_next(grpc_resolver *r, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +static const grpc_resolver_vtable dns_resolver_vtable = { + dns_destroy, dns_shutdown, dns_channel_saw_error, dns_next}; + +static void dns_shutdown(grpc_resolver *resolver) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->next_completion != NULL) { + *r->target_config = NULL; + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } + gpr_mu_unlock(&r->mu); +} + +static void dns_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa, + int len) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (!r->resolving) { + dns_start_resolving_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +static void dns_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + dns_resolver *r = (dns_resolver *)resolver; + gpr_mu_lock(&r->mu); + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_config = target_config; + if (r->resolved_version == 0 && !r->resolving) { + dns_start_resolving_locked(r); + } else { + dns_maybe_finish_next_locked(r); + } + gpr_mu_unlock(&r->mu); +} + +static void dns_on_resolved(void *arg, grpc_resolved_addresses *addresses) { + dns_resolver *r = arg; + grpc_client_config *config = NULL; + grpc_subchannel **subchannels; + grpc_subchannel_args args; + grpc_lb_policy *lb_policy; + size_t i; + if (addresses) { + config = grpc_client_config_create(); + subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs); + for (i = 0; i < addresses->naddrs; i++) { + memset(&args, 0, sizeof(args)); + args.addr = (struct sockaddr *)(addresses->addrs[i].addr); + args.addr_len = addresses->addrs[i].len; + subchannels[i] = grpc_subchannel_factory_create_subchannel( + r->subchannel_factory, &args); + } + lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs); + grpc_client_config_set_lb_policy(config, lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "construction"); + grpc_resolved_addresses_destroy(addresses); + gpr_free(subchannels); + } + gpr_mu_lock(&r->mu); + GPR_ASSERT(r->resolving); + r->resolving = 0; + if (r->resolved_config) { + grpc_client_config_unref(r->resolved_config); + } + r->resolved_config = config; + r->resolved_version++; + dns_maybe_finish_next_locked(r); + gpr_mu_unlock(&r->mu); + + GRPC_RESOLVER_UNREF(&r->base, "dns-resolving"); +} + +static void dns_start_resolving_locked(dns_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "dns-resolving"); + GPR_ASSERT(!r->resolving); + r->resolving = 1; + grpc_resolve_address(r->name, r->default_port, dns_on_resolved, r); +} + +static void dns_maybe_finish_next_locked(dns_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_config = r->resolved_config; + if (r->resolved_config) { + grpc_client_config_ref(r->resolved_config); + } + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void dns_destroy(grpc_resolver *gr) { + dns_resolver *r = (dns_resolver *)gr; + gpr_mu_destroy(&r->mu); + if (r->resolved_config) { + grpc_client_config_unref(r->resolved_config); + } + grpc_subchannel_factory_unref(r->subchannel_factory); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +static grpc_resolver *dns_create( + grpc_uri *uri, const char *default_port, + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels), + grpc_subchannel_factory *subchannel_factory) { + dns_resolver *r; + const char *path = uri->path; + + if (0 != strcmp(uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported"); + return NULL; + } + + if (path[0] == '/') ++path; + + r = gpr_malloc(sizeof(dns_resolver)); + memset(r, 0, sizeof(*r)); + gpr_ref_init(&r->refs, 1); + gpr_mu_init(&r->mu); + grpc_resolver_init(&r->base, &dns_resolver_vtable); + r->name = gpr_strdup(path); + r->default_port = gpr_strdup(default_port); + r->subchannel_factory = subchannel_factory; + r->lb_policy_factory = lb_policy_factory; + grpc_subchannel_factory_ref(subchannel_factory); + return &r->base; +} + +/* + * FACTORY + */ + +static void dns_factory_ref(grpc_resolver_factory *factory) {} + +static void dns_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *dns_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + return dns_create(uri, "https", grpc_create_pick_first_lb_policy, + subchannel_factory); +} + +static const grpc_resolver_factory_vtable dns_factory_vtable = { + dns_factory_ref, dns_factory_unref, dns_factory_create_resolver}; +static grpc_resolver_factory dns_resolver_factory = {&dns_factory_vtable}; + +grpc_resolver_factory *grpc_dns_resolver_factory_create() { + return &dns_resolver_factory; +} diff --git a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.h b/src/core/client_config/resolvers/dns_resolver.h index 81c80f2a49..a3ef3161a6 100644 --- a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.h +++ b/src/core/client_config/resolvers/dns_resolver.h @@ -31,10 +31,12 @@ * */ -#import <Foundation/Foundation.h> +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H -#import "GRPCClient/GRPCMethodName.h" +#include "src/core/client_config/resolver_factory.h" -@interface GRPCMethodName (HTTP2Encoding) -- (NSString *)HTTP2Path; -@end +/** Create a dns resolver factory */ +grpc_resolver_factory *grpc_dns_resolver_factory_create(void); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H */ diff --git a/src/core/client_config/resolvers/unix_resolver_posix.c b/src/core/client_config/resolvers/unix_resolver_posix.c new file mode 100644 index 0000000000..be515d2689 --- /dev/null +++ b/src/core/client_config/resolvers/unix_resolver_posix.c @@ -0,0 +1,195 @@ +/* + * + * 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 <grpc/support/port_platform.h> +#ifdef GPR_POSIX_SOCKET + +#include "src/core/client_config/resolvers/unix_resolver_posix.h" + +#include <string.h> +#include <sys/un.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/string_util.h> + +#include "src/core/client_config/lb_policies/pick_first.h" +#include "src/core/iomgr/resolve_address.h" +#include "src/core/support/string.h" + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** refcount */ + gpr_refcount refs; + /** subchannel factory */ + grpc_subchannel_factory *subchannel_factory; + /** load balancing policy factory */ + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels); + + /** the address that we've 'resolved' */ + struct sockaddr_un addr; + int addr_len; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** have we published? */ + int published; + /** pending next completion, or NULL */ + grpc_iomgr_closure *next_completion; + /** target config address for next completion */ + grpc_client_config **target_config; +} unix_resolver; + +static void unix_destroy(grpc_resolver *r); + +static void unix_maybe_finish_next_locked(unix_resolver *r); + +static void unix_shutdown(grpc_resolver *r); +static void unix_channel_saw_error(grpc_resolver *r, + struct sockaddr *failing_address, + int failing_address_len); +static void unix_next(grpc_resolver *r, grpc_client_config **target_config, + grpc_iomgr_closure *on_complete); + +static const grpc_resolver_vtable unix_resolver_vtable = { + unix_destroy, unix_shutdown, unix_channel_saw_error, unix_next}; + +static void unix_shutdown(grpc_resolver *resolver) { + unix_resolver *r = (unix_resolver *)resolver; + gpr_mu_lock(&r->mu); + if (r->next_completion != NULL) { + *r->target_config = NULL; + /* TODO(ctiller): add delayed callback */ + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } + gpr_mu_unlock(&r->mu); +} + +static void unix_channel_saw_error(grpc_resolver *resolver, struct sockaddr *sa, + int len) {} + +static void unix_next(grpc_resolver *resolver, + grpc_client_config **target_config, + grpc_iomgr_closure *on_complete) { + unix_resolver *r = (unix_resolver *)resolver; + gpr_mu_lock(&r->mu); + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_config = target_config; + unix_maybe_finish_next_locked(r); + gpr_mu_unlock(&r->mu); +} + +static void unix_maybe_finish_next_locked(unix_resolver *r) { + grpc_client_config *cfg; + grpc_lb_policy *lb_policy; + grpc_subchannel *subchannel; + grpc_subchannel_args args; + + if (r->next_completion != NULL && !r->published) { + cfg = grpc_client_config_create(); + memset(&args, 0, sizeof(args)); + args.addr = (struct sockaddr *)&r->addr; + args.addr_len = r->addr_len; + subchannel = + grpc_subchannel_factory_create_subchannel(r->subchannel_factory, &args); + lb_policy = r->lb_policy_factory(&subchannel, 1); + grpc_client_config_set_lb_policy(cfg, lb_policy); + GRPC_LB_POLICY_UNREF(lb_policy, "unix"); + r->published = 1; + *r->target_config = cfg; + grpc_iomgr_add_callback(r->next_completion); + r->next_completion = NULL; + } +} + +static void unix_destroy(grpc_resolver *gr) { + unix_resolver *r = (unix_resolver *)gr; + gpr_mu_destroy(&r->mu); + grpc_subchannel_factory_unref(r->subchannel_factory); + gpr_free(r); +} + +static grpc_resolver *unix_create( + grpc_uri *uri, + grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels, + size_t num_subchannels), + grpc_subchannel_factory *subchannel_factory) { + unix_resolver *r; + + if (0 != strcmp(uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported"); + return NULL; + } + + r = gpr_malloc(sizeof(unix_resolver)); + memset(r, 0, sizeof(*r)); + gpr_ref_init(&r->refs, 1); + gpr_mu_init(&r->mu); + grpc_resolver_init(&r->base, &unix_resolver_vtable); + r->subchannel_factory = subchannel_factory; + r->lb_policy_factory = lb_policy_factory; + + r->addr.sun_family = AF_UNIX; + strcpy(r->addr.sun_path, uri->path); + r->addr_len = strlen(r->addr.sun_path) + sizeof(r->addr.sun_family) + 1; + + grpc_subchannel_factory_ref(subchannel_factory); + return &r->base; +} + +/* + * FACTORY + */ + +static void unix_factory_ref(grpc_resolver_factory *factory) {} + +static void unix_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *unix_factory_create_resolver( + grpc_resolver_factory *factory, grpc_uri *uri, + grpc_subchannel_factory *subchannel_factory) { + return unix_create(uri, grpc_create_pick_first_lb_policy, subchannel_factory); +} + +static const grpc_resolver_factory_vtable unix_factory_vtable = { + unix_factory_ref, unix_factory_unref, unix_factory_create_resolver}; +static grpc_resolver_factory unix_resolver_factory = {&unix_factory_vtable}; + +grpc_resolver_factory *grpc_unix_resolver_factory_create() { + return &unix_resolver_factory; +} + +#endif diff --git a/src/core/client_config/resolvers/unix_resolver_posix.h b/src/core/client_config/resolvers/unix_resolver_posix.h new file mode 100644 index 0000000000..57ace59e21 --- /dev/null +++ b/src/core/client_config/resolvers/unix_resolver_posix.h @@ -0,0 +1,44 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H + +#include <grpc/support/port_platform.h> + +#include "src/core/client_config/resolver_factory.h" + +/** Create a unix resolver factory */ +grpc_resolver_factory *grpc_unix_resolver_factory_create(void); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVERS_UNIX_RESOLVER_H */ diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c new file mode 100644 index 0000000000..6cf9062ab0 --- /dev/null +++ b/src/core/client_config/subchannel.c @@ -0,0 +1,659 @@ +/* + * + * 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/client_config/subchannel.h" + +#include <string.h> + +#include <grpc/support/alloc.h> + +#include "src/core/channel/channel_args.h" +#include "src/core/channel/connected_channel.h" +#include "src/core/iomgr/alarm.h" +#include "src/core/transport/connectivity_state.h" + +typedef struct { + /* all fields protected by subchannel->mu */ + /** refcount */ + int refs; + /** parent subchannel */ + grpc_subchannel *subchannel; +} connection; + +typedef struct { + grpc_iomgr_closure closure; + size_t version; + grpc_subchannel *subchannel; + grpc_connectivity_state connectivity_state; +} state_watcher; + +typedef struct waiting_for_connect { + struct waiting_for_connect *next; + grpc_iomgr_closure *notify; + grpc_pollset *pollset; + grpc_subchannel_call **target; + grpc_subchannel *subchannel; + grpc_iomgr_closure continuation; +} waiting_for_connect; + +struct grpc_subchannel { + grpc_connector *connector; + + /** non-transport related channel filters */ + const grpc_channel_filter **filters; + size_t num_filters; + /** channel arguments */ + grpc_channel_args *args; + /** address to connect to */ + struct sockaddr *addr; + size_t addr_len; + /** metadata context */ + grpc_mdctx *mdctx; + /** master channel - the grpc_channel instance that ultimately owns + this channel_data via its channel stack. + We occasionally use this to bump the refcount on the master channel + to keep ourselves alive through an asynchronous operation. */ + grpc_channel *master; + /** have we seen a disconnection? */ + int disconnected; + + /** set during connection */ + grpc_connect_out_args connecting_result; + + /** callback for connection finishing */ + grpc_iomgr_closure connected; + + /** pollset_set tracking who's interested in a connection + being setup */ + grpc_pollset_set pollset_set; + + /** mutex protecting remaining elements */ + gpr_mu mu; + + /** active connection */ + connection *active; + /** version number for the active connection */ + size_t active_version; + /** refcount */ + int refs; + /** are we connecting */ + int connecting; + /** things waiting for a connection */ + waiting_for_connect *waiting; + /** connectivity state tracking */ + grpc_connectivity_state_tracker state_tracker; + + /** next connect attempt time */ + gpr_timespec next_attempt; + /** amount to backoff each failure */ + gpr_timespec backoff_delta; + /** do we have an active alarm? */ + int have_alarm; + /** our alarm */ + grpc_alarm alarm; +}; + +struct grpc_subchannel_call { + connection *connection; + gpr_refcount refs; +}; + +#define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) +#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)((con) + 1)) + +static grpc_subchannel_call *create_call(connection *con); +static void connectivity_state_changed_locked(grpc_subchannel *c); +static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c); +static gpr_timespec compute_connect_deadline(grpc_subchannel *c); +static void subchannel_connected(void *subchannel, int iomgr_success); + +static void subchannel_ref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +static int subchannel_unref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; +static void connection_ref_locked(connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +static grpc_subchannel *connection_unref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; +static void subchannel_destroy(grpc_subchannel *c); + +#ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG +#define SUBCHANNEL_REF_LOCKED(p, r) \ + subchannel_ref_locked((p), __FILE__, __LINE__, (r)) +#define SUBCHANNEL_UNREF_LOCKED(p, r) \ + subchannel_unref_locked((p), __FILE__, __LINE__, (r)) +#define CONNECTION_REF_LOCKED(p, r) \ + connection_ref_locked((p), __FILE__, __LINE__, (r)) +#define CONNECTION_UNREF_LOCKED(p, r) \ + connection_unref_locked((p), __FILE__, __LINE__, (r)) +#define REF_PASS_ARGS , file, line, reason +#define REF_LOG(name, p) \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p ref %d -> %d %s", \ + (name), (p), (p)->refs, (p)->refs + 1, reason) +#define UNREF_LOG(name, p) \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p unref %d -> %d %s", \ + (name), (p), (p)->refs, (p)->refs - 1, reason) +#else +#define SUBCHANNEL_REF_LOCKED(p, r) subchannel_ref_locked((p)) +#define SUBCHANNEL_UNREF_LOCKED(p, r) subchannel_unref_locked((p)) +#define CONNECTION_REF_LOCKED(p, r) connection_ref_locked((p)) +#define CONNECTION_UNREF_LOCKED(p, r) connection_unref_locked((p)) +#define REF_PASS_ARGS +#define REF_LOG(name, p) \ + do { \ + } while (0) +#define UNREF_LOG(name, p) \ + do { \ + } while (0) +#endif + +/* + * connection implementation + */ + +static void connection_destroy(connection *c) { + GPR_ASSERT(c->refs == 0); + grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CONNECTION(c)); + gpr_free(c); +} + +static void connection_ref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + REF_LOG("CONNECTION", c); + subchannel_ref_locked(c->subchannel REF_PASS_ARGS); + ++c->refs; +} + +static grpc_subchannel *connection_unref_locked( + connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + grpc_subchannel *destroy = NULL; + UNREF_LOG("CONNECTION", c); + if (subchannel_unref_locked(c->subchannel REF_PASS_ARGS)) { + destroy = c->subchannel; + } + if (--c->refs == 0 && c->subchannel->active != c) { + connection_destroy(c); + } + return destroy; +} + +/* + * grpc_subchannel implementation + */ + +static void subchannel_ref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + REF_LOG("SUBCHANNEL", c); + ++c->refs; +} + +static int subchannel_unref_locked( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + UNREF_LOG("SUBCHANNEL", c); + return --c->refs == 0; +} + +void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_mu_lock(&c->mu); + subchannel_ref_locked(c REF_PASS_ARGS); + gpr_mu_unlock(&c->mu); +} + +void grpc_subchannel_unref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + int destroy; + gpr_mu_lock(&c->mu); + destroy = subchannel_unref_locked(c REF_PASS_ARGS); + gpr_mu_unlock(&c->mu); + if (destroy) subchannel_destroy(c); +} + +static void subchannel_destroy(grpc_subchannel *c) { + if (c->active != NULL) { + connection_destroy(c->active); + } + gpr_free(c->filters); + grpc_channel_args_destroy(c->args); + gpr_free(c->addr); + grpc_mdctx_unref(c->mdctx); + grpc_pollset_set_destroy(&c->pollset_set); + grpc_connectivity_state_destroy(&c->state_tracker); + grpc_connector_unref(c->connector); + gpr_free(c); +} + +void grpc_subchannel_add_interested_party(grpc_subchannel *c, + grpc_pollset *pollset) { + grpc_pollset_set_add_pollset(&c->pollset_set, pollset); +} + +void grpc_subchannel_del_interested_party(grpc_subchannel *c, + grpc_pollset *pollset) { + grpc_pollset_set_del_pollset(&c->pollset_set, pollset); +} + +grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, + grpc_subchannel_args *args) { + grpc_subchannel *c = gpr_malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + c->refs = 1; + c->connector = connector; + grpc_connector_ref(c->connector); + c->num_filters = args->filter_count; + c->filters = gpr_malloc(sizeof(grpc_channel_filter *) * c->num_filters); + memcpy(c->filters, args->filters, + sizeof(grpc_channel_filter *) * c->num_filters); + c->addr = gpr_malloc(args->addr_len); + memcpy(c->addr, args->addr, args->addr_len); + c->addr_len = args->addr_len; + c->args = grpc_channel_args_copy(args->args); + c->mdctx = args->mdctx; + c->master = args->master; + grpc_mdctx_ref(c->mdctx); + grpc_pollset_set_init(&c->pollset_set); + grpc_iomgr_closure_init(&c->connected, subchannel_connected, c); + grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE); + gpr_mu_init(&c->mu); + return c; +} + +static void continue_connect(grpc_subchannel *c) { + grpc_connect_in_args args; + + args.interested_parties = &c->pollset_set; + args.addr = c->addr; + args.addr_len = c->addr_len; + args.deadline = compute_connect_deadline(c); + args.channel_args = c->args; + args.metadata_context = c->mdctx; + + grpc_connector_connect(c->connector, &args, &c->connecting_result, + &c->connected); +} + +static void start_connect(grpc_subchannel *c) { + gpr_timespec now = gpr_now(); + c->next_attempt = now; + c->backoff_delta = gpr_time_from_seconds(1); + + continue_connect(c); +} + +static void continue_creating_call(void *arg, int iomgr_success) { + waiting_for_connect *w4c = arg; + grpc_subchannel_create_call(w4c->subchannel, w4c->pollset, w4c->target, + w4c->notify); + GRPC_SUBCHANNEL_UNREF(w4c->subchannel, "waiting_for_connect"); + gpr_free(w4c); +} + +void grpc_subchannel_create_call(grpc_subchannel *c, grpc_pollset *pollset, + grpc_subchannel_call **target, + grpc_iomgr_closure *notify) { + connection *con; + gpr_mu_lock(&c->mu); + if (c->active != NULL) { + con = c->active; + CONNECTION_REF_LOCKED(con, "call"); + gpr_mu_unlock(&c->mu); + + *target = create_call(con); + notify->cb(notify->cb_arg, 1); + } else { + waiting_for_connect *w4c = gpr_malloc(sizeof(*w4c)); + w4c->next = c->waiting; + w4c->notify = notify; + w4c->pollset = pollset; + w4c->target = target; + w4c->subchannel = c; + /* released when clearing w4c */ + SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect"); + grpc_iomgr_closure_init(&w4c->continuation, continue_creating_call, w4c); + c->waiting = w4c; + grpc_subchannel_add_interested_party(c, pollset); + if (!c->connecting) { + c->connecting = 1; + connectivity_state_changed_locked(c); + /* released by connection */ + SUBCHANNEL_REF_LOCKED(c, "connecting"); + gpr_mu_unlock(&c->mu); + + start_connect(c); + } else { + gpr_mu_unlock(&c->mu); + } + } +} + +grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) { + grpc_connectivity_state state; + gpr_mu_lock(&c->mu); + state = grpc_connectivity_state_check(&c->state_tracker); + gpr_mu_unlock(&c->mu); + return state; +} + +void grpc_subchannel_notify_on_state_change(grpc_subchannel *c, + grpc_connectivity_state *state, + grpc_iomgr_closure *notify) { + int do_connect = 0; + gpr_mu_lock(&c->mu); + if (grpc_connectivity_state_notify_on_state_change(&c->state_tracker, state, + notify)) { + do_connect = 1; + c->connecting = 1; + /* released by connection */ + SUBCHANNEL_REF_LOCKED(c, "connecting"); + connectivity_state_changed_locked(c); + } + gpr_mu_unlock(&c->mu); + if (do_connect) { + start_connect(c); + } +} + +void grpc_subchannel_process_transport_op(grpc_subchannel *c, + grpc_transport_op *op) { + connection *con = NULL; + grpc_subchannel *destroy; + int cancel_alarm = 0; + gpr_mu_lock(&c->mu); + if (op->disconnect) { + c->disconnected = 1; + connectivity_state_changed_locked(c); + if (c->have_alarm) { + cancel_alarm = 1; + } + } + if (c->active != NULL) { + con = c->active; + CONNECTION_REF_LOCKED(con, "transport-op"); + } + gpr_mu_unlock(&c->mu); + + if (con != NULL) { + grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_channel_element *top_elem = + grpc_channel_stack_element(channel_stack, 0); + top_elem->filter->start_transport_op(top_elem, op); + + gpr_mu_lock(&c->mu); + destroy = CONNECTION_UNREF_LOCKED(con, "transport-op"); + gpr_mu_unlock(&c->mu); + if (destroy) { + subchannel_destroy(destroy); + } + } + + if (cancel_alarm) { + grpc_alarm_cancel(&c->alarm); + } +} + +static void on_state_changed(void *p, int iomgr_success) { + state_watcher *sw = p; + grpc_subchannel *c = sw->subchannel; + gpr_mu *mu = &c->mu; + int destroy; + grpc_transport_op op; + grpc_channel_element *elem; + connection *destroy_connection = NULL; + + gpr_mu_lock(mu); + + /* if we failed or there is a version number mismatch, just leave + this closure */ + if (!iomgr_success || sw->subchannel->active_version != sw->version) { + goto done; + } + + switch (sw->connectivity_state) { + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_READY: + case GRPC_CHANNEL_IDLE: + /* all is still good: keep watching */ + memset(&op, 0, sizeof(op)); + op.connectivity_state = &sw->connectivity_state; + op.on_connectivity_state_change = &sw->closure; + elem = grpc_channel_stack_element( + CHANNEL_STACK_FROM_CONNECTION(c->active), 0); + elem->filter->start_transport_op(elem, &op); + /* early out */ + gpr_mu_unlock(mu); + return; + case GRPC_CHANNEL_FATAL_FAILURE: + case GRPC_CHANNEL_TRANSIENT_FAILURE: + /* things have gone wrong, deactivate and enter idle */ + if (sw->subchannel->active->refs == 0) { + destroy_connection = sw->subchannel->active; + } + sw->subchannel->active = NULL; + grpc_connectivity_state_set(&c->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE); + break; + } + +done: + connectivity_state_changed_locked(c); + destroy = SUBCHANNEL_UNREF_LOCKED(c, "state_watcher"); + gpr_free(sw); + gpr_mu_unlock(mu); + if (destroy) { + subchannel_destroy(c); + } + if (destroy_connection != NULL) { + connection_destroy(destroy_connection); + } +} + +static void publish_transport(grpc_subchannel *c) { + size_t channel_stack_size; + connection *con; + grpc_channel_stack *stk; + size_t num_filters; + const grpc_channel_filter **filters; + waiting_for_connect *w4c; + grpc_transport_op op; + state_watcher *sw; + connection *destroy_connection = NULL; + grpc_channel_element *elem; + + /* build final filter list */ + num_filters = c->num_filters + c->connecting_result.num_filters + 1; + filters = gpr_malloc(sizeof(*filters) * num_filters); + memcpy(filters, c->filters, sizeof(*filters) * c->num_filters); + memcpy(filters + c->num_filters, c->connecting_result.filters, + sizeof(*filters) * c->connecting_result.num_filters); + filters[num_filters - 1] = &grpc_connected_channel_filter; + + /* construct channel stack */ + channel_stack_size = grpc_channel_stack_size(filters, num_filters); + con = gpr_malloc(sizeof(connection) + channel_stack_size); + stk = (grpc_channel_stack *)(con + 1); + con->refs = 0; + con->subchannel = c; + grpc_channel_stack_init(filters, num_filters, c->master, c->args, c->mdctx, + stk); + grpc_connected_channel_bind_transport(stk, c->connecting_result.transport); + gpr_free(c->connecting_result.filters); + memset(&c->connecting_result, 0, sizeof(c->connecting_result)); + + /* initialize state watcher */ + sw = gpr_malloc(sizeof(*sw)); + grpc_iomgr_closure_init(&sw->closure, on_state_changed, sw); + sw->subchannel = c; + sw->connectivity_state = GRPC_CHANNEL_READY; + + gpr_mu_lock(&c->mu); + + if (c->disconnected) { + gpr_mu_unlock(&c->mu); + gpr_free(sw); + gpr_free(filters); + grpc_channel_stack_destroy(stk); + return; + } + + /* publish */ + if (c->active != NULL && c->active->refs == 0) { + destroy_connection = c->active; + } + c->active = con; + c->active_version++; + sw->version = c->active_version; + c->connecting = 0; + + /* watch for changes; subchannel ref for connecting is donated + to the state watcher */ + memset(&op, 0, sizeof(op)); + op.connectivity_state = &sw->connectivity_state; + op.on_connectivity_state_change = &sw->closure; + SUBCHANNEL_REF_LOCKED(c, "state_watcher"); + GPR_ASSERT(!SUBCHANNEL_UNREF_LOCKED(c, "connecting")); + elem = + grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(c->active), 0); + elem->filter->start_transport_op(elem, &op); + + /* signal completion */ + connectivity_state_changed_locked(c); + while ((w4c = c->waiting)) { + c->waiting = w4c->next; + grpc_iomgr_add_callback(&w4c->continuation); + } + + gpr_mu_unlock(&c->mu); + + gpr_free(filters); + + if (destroy_connection != NULL) { + connection_destroy(destroy_connection); + } +} + +static void on_alarm(void *arg, int iomgr_success) { + grpc_subchannel *c = arg; + gpr_mu_lock(&c->mu); + c->have_alarm = 0; + if (c->disconnected) { + iomgr_success = 0; + } + connectivity_state_changed_locked(c); + gpr_mu_unlock(&c->mu); + if (iomgr_success) { + continue_connect(c); + } else { + GRPC_SUBCHANNEL_UNREF(c, "connecting"); + } +} + +static void subchannel_connected(void *arg, int iomgr_success) { + grpc_subchannel *c = arg; + if (c->connecting_result.transport != NULL) { + publish_transport(c); + } else { + gpr_mu_lock(&c->mu); + connectivity_state_changed_locked(c); + GPR_ASSERT(!c->have_alarm); + c->have_alarm = 1; + c->next_attempt = gpr_time_add(c->next_attempt, c->backoff_delta); + c->backoff_delta = gpr_time_add(c->backoff_delta, c->backoff_delta); + grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, gpr_now()); + gpr_mu_unlock(&c->mu); + } +} + +static gpr_timespec compute_connect_deadline(grpc_subchannel *c) { + return gpr_time_add(c->next_attempt, c->backoff_delta); +} + +static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c) { + if (c->disconnected) { + return GRPC_CHANNEL_FATAL_FAILURE; + } + if (c->connecting) { + if (c->have_alarm) { + return GRPC_CHANNEL_TRANSIENT_FAILURE; + } + return GRPC_CHANNEL_CONNECTING; + } + if (c->active) { + return GRPC_CHANNEL_READY; + } + return GRPC_CHANNEL_IDLE; +} + +static void connectivity_state_changed_locked(grpc_subchannel *c) { + grpc_connectivity_state current = compute_connectivity_locked(c); + grpc_connectivity_state_set(&c->state_tracker, current); +} + +/* + * grpc_subchannel_call implementation + */ + +void grpc_subchannel_call_ref( + grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_ref(&c->refs); +} + +void grpc_subchannel_call_unref( + grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + if (gpr_unref(&c->refs)) { + gpr_mu *mu = &c->connection->subchannel->mu; + grpc_subchannel *destroy; + grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c)); + gpr_mu_lock(mu); + destroy = CONNECTION_UNREF_LOCKED(c->connection, "call"); + gpr_mu_unlock(mu); + gpr_free(c); + if (destroy != NULL) { + subchannel_destroy(destroy); + } + } +} + +void grpc_subchannel_call_process_op(grpc_subchannel_call *call, + grpc_transport_stream_op *op) { + grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); + grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); + top_elem->filter->start_transport_stream_op(top_elem, op); +} + +grpc_subchannel_call *create_call(connection *con) { + grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_subchannel_call *call = + gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size); + grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call); + call->connection = con; + gpr_ref_init(&call->refs, 1); + grpc_call_stack_init(chanstk, NULL, NULL, callstk); + return call; +} diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h new file mode 100644 index 0000000000..a23a623277 --- /dev/null +++ b/src/core/client_config/subchannel.h @@ -0,0 +1,124 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/connector.h" + +/** A (sub-)channel that knows how to connect to exactly one target + address. Provides a target for load balancing. */ +typedef struct grpc_subchannel grpc_subchannel; +typedef struct grpc_subchannel_call grpc_subchannel_call; +typedef struct grpc_subchannel_args grpc_subchannel_args; + +#ifdef GRPC_SUBCHANNEL_REFCOUNT_DEBUG +#define GRPC_SUBCHANNEL_REF(p, r) \ + grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_UNREF(p, r) \ + grpc_subchannel_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_CALL_REF(p, r) \ + grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) \ + grpc_subchannel_call_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS \ + , const char *file, int line, const char *reason +#else +#define GRPC_SUBCHANNEL_REF(p, r) grpc_subchannel_ref((p)) +#define GRPC_SUBCHANNEL_UNREF(p, r) grpc_subchannel_unref((p)) +#define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p)) +#define GRPC_SUBCHANNEL_CALL_UNREF(p, r) grpc_subchannel_call_unref((p)) +#define GRPC_SUBCHANNEL_REF_EXTRA_ARGS +#endif + +void grpc_subchannel_ref( + grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_unref( + grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_ref( + grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_call_unref( + grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); + +/** construct a call (possibly asynchronously) */ +void grpc_subchannel_create_call(grpc_subchannel *subchannel, + grpc_pollset *pollset, + grpc_subchannel_call **target, + grpc_iomgr_closure *notify); + +/** process a transport level op */ +void grpc_subchannel_process_transport_op(grpc_subchannel *subchannel, + grpc_transport_op *op); + +/** poll the current connectivity state of a channel */ +grpc_connectivity_state grpc_subchannel_check_connectivity( + grpc_subchannel *channel); + +/** call notify when the connectivity state of a channel changes from *state. + Updates *state with the new state of the channel */ +void grpc_subchannel_notify_on_state_change(grpc_subchannel *channel, + grpc_connectivity_state *state, + grpc_iomgr_closure *notify); + +void grpc_subchannel_add_interested_party(grpc_subchannel *channel, + grpc_pollset *pollset); +void grpc_subchannel_del_interested_party(grpc_subchannel *channel, + grpc_pollset *pollset); + +/** continue processing a transport op */ +void grpc_subchannel_call_process_op(grpc_subchannel_call *subchannel_call, + grpc_transport_stream_op *op); + +struct grpc_subchannel_args { + /** Channel filters for this channel - wrapped factories will likely + want to mutate this */ + const grpc_channel_filter **filters; + /** The number of filters in the above array */ + size_t filter_count; + /** Channel arguments to be supplied to the newly created channel */ + const grpc_channel_args *args; + /** Address to connect to */ + struct sockaddr *addr; + size_t addr_len; + /** metadata context to use */ + grpc_mdctx *mdctx; + /** master channel */ + grpc_channel *master; +}; + +/** create a subchannel given a connector */ +grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, + grpc_subchannel_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H */ diff --git a/src/core/client_config/subchannel_factory.c b/src/core/client_config/subchannel_factory.c new file mode 100644 index 0000000000..f71386594c --- /dev/null +++ b/src/core/client_config/subchannel_factory.c @@ -0,0 +1,46 @@ +/* + * + * 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/client_config/subchannel_factory.h" + +void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory) { + factory->vtable->ref(factory); +} +void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory) { + factory->vtable->unref(factory); +} + +grpc_subchannel *grpc_subchannel_factory_create_subchannel( + grpc_subchannel_factory *factory, grpc_subchannel_args *args) { + return factory->vtable->create_subchannel(factory, args); +} diff --git a/src/core/client_config/subchannel_factory.h b/src/core/client_config/subchannel_factory.h new file mode 100644 index 0000000000..d7eae1c964 --- /dev/null +++ b/src/core/client_config/subchannel_factory.h @@ -0,0 +1,63 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H + +#include "src/core/channel/channel_stack.h" +#include "src/core/client_config/subchannel.h" + +typedef struct grpc_subchannel_factory grpc_subchannel_factory; +typedef struct grpc_subchannel_factory_vtable grpc_subchannel_factory_vtable; + +/** Constructor for new configured channels. + Creating decorators around this type is encouraged to adapt behavior. */ +struct grpc_subchannel_factory { + const grpc_subchannel_factory_vtable *vtable; +}; + +struct grpc_subchannel_factory_vtable { + void (*ref)(grpc_subchannel_factory *factory); + void (*unref)(grpc_subchannel_factory *factory); + grpc_subchannel *(*create_subchannel)(grpc_subchannel_factory *factory, + grpc_subchannel_args *args); +}; + +void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory); +void grpc_subchannel_factory_unref(grpc_subchannel_factory *factory); + +/** Create a new grpc_subchannel */ +grpc_subchannel *grpc_subchannel_factory_create_subchannel( + grpc_subchannel_factory *factory, grpc_subchannel_args *args); + +#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */ diff --git a/src/core/client_config/uri_parser.c b/src/core/client_config/uri_parser.c new file mode 100644 index 0000000000..776a255923 --- /dev/null +++ b/src/core/client_config/uri_parser.c @@ -0,0 +1,149 @@ +/* + * + * 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/client_config/uri_parser.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +static grpc_uri *bad_uri(const char *uri_text, int pos, const char *section, + int suppress_errors) { + char *line_prefix; + int pfx_len; + + if (!suppress_errors) { + gpr_asprintf(&line_prefix, "bad uri.%s: '", section); + pfx_len = strlen(line_prefix) + pos; + gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text); + gpr_free(line_prefix); + + line_prefix = gpr_malloc(pfx_len + 1); + memset(line_prefix, ' ', pfx_len); + line_prefix[pfx_len] = 0; + gpr_log(GPR_ERROR, "%s^ here", line_prefix); + gpr_free(line_prefix); + } + + return NULL; +} + +static char *copy_fragment(const char *src, int begin, int end) { + char *out = gpr_malloc(end - begin + 1); + memcpy(out, src + begin, end - begin); + out[end - begin] = 0; + return out; +} + +grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { + grpc_uri *uri; + int scheme_begin = 0; + int scheme_end = -1; + int authority_begin = -1; + int authority_end = -1; + int path_begin = -1; + int path_end = -1; + int i; + + for (i = scheme_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == ':') { + scheme_end = i; + break; + } + if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue; + if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue; + if (i != scheme_begin) { + if (uri_text[i] >= '0' && uri_text[i] <= '9') continue; + if (uri_text[i] == '+') continue; + if (uri_text[i] == '-') continue; + if (uri_text[i] == '.') continue; + } + break; + } + if (scheme_end == -1) { + return bad_uri(uri_text, i, "scheme", suppress_errors); + } + + if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { + authority_begin = scheme_end + 3; + for (i = authority_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == '/') { + authority_end = i; + } + if (uri_text[i] == '?') { + return bad_uri(uri_text, i, "query_not_supported", suppress_errors); + } + if (uri_text[i] == '#') { + return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); + } + } + if (authority_end == -1 && uri_text[i] == 0) { + authority_end = i; + } + if (authority_end == -1) { + return bad_uri(uri_text, i, "authority", suppress_errors); + } + /* TODO(ctiller): parse the authority correctly */ + path_begin = authority_end; + } else { + path_begin = scheme_end + 1; + } + + for (i = path_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == '?') { + return bad_uri(uri_text, i, "query_not_supported", suppress_errors); + } + if (uri_text[i] == '#') { + return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors); + } + } + path_end = i; + + uri = gpr_malloc(sizeof(*uri)); + memset(uri, 0, sizeof(*uri)); + uri->scheme = copy_fragment(uri_text, scheme_begin, scheme_end); + uri->authority = copy_fragment(uri_text, authority_begin, authority_end); + uri->path = copy_fragment(uri_text, path_begin, path_end); + + return uri; +} + +void grpc_uri_destroy(grpc_uri *uri) { + if (!uri) return; + gpr_free(uri->scheme); + gpr_free(uri->authority); + gpr_free(uri->path); + gpr_free(uri); +} diff --git a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.m b/src/core/client_config/uri_parser.h index 3ad757fb29..ce4e6aecb0 100644 --- a/src/objective-c/GRPCClient/private/GRPCMethodName+HTTP2Encoding.m +++ b/src/core/client_config/uri_parser.h @@ -31,14 +31,19 @@ * */ -#import "GRPCMethodName+HTTP2Encoding.h" +#ifndef GRPC_INTERNAL_CORE_CLIENT_CONFIG_URI_PARSER_H +#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_URI_PARSER_H -@implementation GRPCMethodName (HTTP2Encoding) -- (NSString *)HTTP2Path { - if (self.package) { - return [NSString stringWithFormat:@"/%@.%@/%@", self.package, self.interface, self.method]; - } else { - return [NSString stringWithFormat:@"/%@/%@", self.interface, self.method]; - } -} -@end +typedef struct { + char *scheme; + char *authority; + char *path; +} grpc_uri; + +/** parse a uri, return NULL on failure */ +grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors); + +/** destroy a uri */ +void grpc_uri_destroy(grpc_uri *uri); + +#endif diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index 914355a408..3f5557e08e 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -198,7 +198,7 @@ static void on_connected(void *arg, grpc_endpoint *tcp) { GRPC_SECURITY_OK); grpc_setup_secure_transport(&sc->base, tcp, on_secure_transport_setup_done, req); - grpc_security_connector_unref(&sc->base); + GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli"); } else { start_write(req); } diff --git a/src/core/iomgr/alarm.h b/src/core/iomgr/alarm.h index e5262e2199..c067a0b8a3 100644 --- a/src/core/iomgr/alarm.h +++ b/src/core/iomgr/alarm.h @@ -41,9 +41,9 @@ typedef struct grpc_alarm { gpr_timespec deadline; gpr_uint32 heap_index; /* INVALID_HEAP_INDEX if not in heap */ + int triggered; struct grpc_alarm *next; struct grpc_alarm *prev; - int triggered; grpc_iomgr_cb_func cb; void *cb_arg; } grpc_alarm; diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c index 632d2a4609..6ad377ce1c 100644 --- a/src/core/iomgr/fd_posix.c +++ b/src/core/iomgr/fd_posix.c @@ -116,7 +116,7 @@ static void destroy(grpc_fd *fd) { #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, int line) { - gpr_log(GPR_DEBUG, "FD %d %p ref %d %d -> %d [%s; %s:%d]", fd->fd, fd, n, + gpr_log(GPR_DEBUG, "FD %d %p ref %d %d -> %d [%s; %s:%d]", fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); #else @@ -198,7 +198,8 @@ static void wake_all_watchers_locked(grpc_fd *fd) { } static int has_watchers(grpc_fd *fd) { - return fd->read_watcher != NULL || fd->write_watcher != NULL || fd->inactive_watcher_root.next != &fd->inactive_watcher_root; + return fd->read_watcher != NULL || fd->write_watcher != NULL || + fd->inactive_watcher_root.next != &fd->inactive_watcher_root; } void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_closure *on_done, diff --git a/src/core/iomgr/iocp_windows.h b/src/core/iomgr/iocp_windows.h index 4503fdd55e..9df6476917 100644 --- a/src/core/iomgr/iocp_windows.h +++ b/src/core/iomgr/iocp_windows.h @@ -34,7 +34,6 @@ #ifndef GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H #define GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H -#include <windows.h> #include <grpc/support/sync.h> #include "src/core/iomgr/socket_windows.h" diff --git a/src/core/iomgr/iomgr.c b/src/core/iomgr/iomgr.c index c47528aa94..c507e7c26a 100644 --- a/src/core/iomgr/iomgr.c +++ b/src/core/iomgr/iomgr.c @@ -158,7 +158,7 @@ void grpc_iomgr_shutdown(void) { "memory leaks are likely", count_objects()); for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { - gpr_log(GPR_DEBUG, "LEAKED OBJECT: %s", obj->name); + gpr_log(GPR_DEBUG, "LEAKED OBJECT: %s %p", obj->name, obj); } break; } @@ -177,8 +177,8 @@ void grpc_iomgr_shutdown(void) { } void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) { - gpr_mu_lock(&g_mu); obj->name = gpr_strdup(name); + gpr_mu_lock(&g_mu); obj->next = &g_root_object; obj->prev = obj->next->prev; obj->next->prev = obj->prev->next = obj; @@ -189,9 +189,9 @@ void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { gpr_mu_lock(&g_mu); obj->next->prev = obj->prev; obj->prev->next = obj->next; - gpr_free(obj->name); gpr_cv_signal(&g_rcv); gpr_mu_unlock(&g_mu); + gpr_free(obj->name); } void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, @@ -201,9 +201,21 @@ void grpc_iomgr_closure_init(grpc_iomgr_closure *closure, grpc_iomgr_cb_func cb, closure->next = NULL; } +static void assert_not_scheduled_locked(grpc_iomgr_closure *closure) { +#ifndef NDEBUG + grpc_iomgr_closure *c; + + for (c = g_cbs_head; c; c = c->next) { + GPR_ASSERT(c != closure); + } +#endif +} + void grpc_iomgr_add_delayed_callback(grpc_iomgr_closure *closure, int success) { closure->success = success; + GPR_ASSERT(closure->cb); gpr_mu_lock(&g_mu); + assert_not_scheduled_locked(closure); closure->next = NULL; if (!g_cbs_tail) { g_cbs_head = g_cbs_tail = closure; diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 15ed8e75e6..12496440de 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -249,7 +249,8 @@ static void basic_do_promote(void *args, int success) { pollset->in_flight_cbs--; if (pollset->shutting_down) { /* We don't care about this pollset anymore. */ - if (pollset->in_flight_cbs == 0 && pollset->counter == 0) { + if (pollset->in_flight_cbs == 0 && pollset->counter == 0 && !pollset->called_shutdown) { + pollset->called_shutdown = 1; do_shutdown_cb = 1; } } else if (grpc_fd_is_orphaned(fd)) { diff --git a/src/core/iomgr/pollset_set_posix.c b/src/core/iomgr/pollset_set_posix.c index 005e938398..5ff7df1dcd 100644 --- a/src/core/iomgr/pollset_set_posix.c +++ b/src/core/iomgr/pollset_set_posix.c @@ -114,7 +114,7 @@ void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) { if (pollset_set->fds[i] == fd) { pollset_set->fd_count--; GPR_SWAP(grpc_fd *, pollset_set->fds[i], - pollset_set->fds[pollset_set->pollset_count]); + pollset_set->fds[pollset_set->fd_count]); GRPC_FD_UNREF(fd, "pollset_set"); break; } diff --git a/src/core/iomgr/pollset_windows.h b/src/core/iomgr/pollset_windows.h index 57a2907926..c9b8d3f374 100644 --- a/src/core/iomgr/pollset_windows.h +++ b/src/core/iomgr/pollset_windows.h @@ -34,7 +34,6 @@ #ifndef GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H #define GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H -#include <windows.h> #include <grpc/support/sync.h> #include "src/core/iomgr/socket_windows.h" diff --git a/src/core/iomgr/sockaddr_win32.h b/src/core/iomgr/sockaddr_win32.h index c0385ea614..be55db805a 100644 --- a/src/core/iomgr/sockaddr_win32.h +++ b/src/core/iomgr/sockaddr_win32.h @@ -34,8 +34,8 @@ #ifndef GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H #define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H -#include <ws2tcpip.h> #include <winsock2.h> +#include <ws2tcpip.h> #include <mswsock.h> #ifdef __MINGW32__ diff --git a/src/core/iomgr/socket_windows.h b/src/core/iomgr/socket_windows.h index 7080919af0..346fde8edd 100644 --- a/src/core/iomgr/socket_windows.h +++ b/src/core/iomgr/socket_windows.h @@ -34,7 +34,8 @@ #ifndef GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H #define GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H -#include <windows.h> +#include <grpc/support/port_platform.h> +#include <winsock2.h> #include <grpc/support/sync.h> #include <grpc/support/atm.h> diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c index bbf7711588..d981aaf028 100644 --- a/src/core/iomgr/tcp_client_posix.c +++ b/src/core/iomgr/tcp_client_posix.c @@ -63,6 +63,7 @@ typedef struct { grpc_alarm alarm; int refs; grpc_iomgr_closure write_closure; + grpc_pollset_set *interested_parties; } async_connect; static int prepare_socket(const struct sockaddr *addr, int fd) { @@ -152,6 +153,7 @@ static void on_writable(void *acp, int success) { goto finish; } } else { + grpc_pollset_set_del_fd(ac->interested_parties, ac->fd); ep = grpc_tcp_create(ac->fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE); goto finish; } @@ -165,6 +167,7 @@ static void on_writable(void *acp, int success) { finish: gpr_mu_lock(&ac->mu); if (!ep) { + grpc_pollset_set_del_fd(ac->interested_parties, ac->fd); grpc_fd_orphan(ac->fd, NULL, "tcp_client_orphan"); } done = (--ac->refs == 0); @@ -240,6 +243,7 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), ac->cb = cb; ac->cb_arg = arg; ac->fd = fdobj; + ac->interested_parties = interested_parties; gpr_mu_init(&ac->mu); ac->refs = 2; ac->write_closure.cb = on_writable; diff --git a/src/core/iomgr/tcp_posix.c b/src/core/iomgr/tcp_posix.c index 9ad089af66..b6d6efc9fb 100644 --- a/src/core/iomgr/tcp_posix.c +++ b/src/core/iomgr/tcp_posix.c @@ -313,9 +313,7 @@ static void call_read_cb(grpc_tcp *tcp, gpr_slice *slices, size_t nslices, size_t i; gpr_log(GPR_DEBUG, "read: status=%d", status); for (i = 0; i < nslices; i++) { - char *dump = - gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]), - GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT); + char *dump = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "READ: %s", dump); gpr_free(dump); } @@ -540,9 +538,7 @@ static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep, size_t i; for (i = 0; i < nslices; i++) { - char *data = - gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]), - GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT); + char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data); gpr_free(data); } diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c index e9bd45db68..f8d18d9b17 100644 --- a/src/core/security/client_auth_filter.c +++ b/src/core/security/client_auth_filter.c @@ -58,7 +58,7 @@ typedef struct { so that work can progress when this call wants work to progress */ grpc_pollset *pollset; - grpc_transport_op op; + grpc_transport_stream_op op; size_t op_md_idx; int sent_initial_metadata; grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; @@ -77,7 +77,7 @@ typedef struct { static void bubble_up_error(grpc_call_element *elem, const char *error_msg) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; - grpc_transport_op_add_cancellation( + grpc_transport_stream_op_add_cancellation( &calld->op, GRPC_STATUS_UNAUTHENTICATED, grpc_mdstr_from_string(chand->md_ctx, error_msg)); grpc_call_next_op(elem, &calld->op); @@ -90,7 +90,7 @@ static void on_credentials_metadata(void *user_data, grpc_call_element *elem = (grpc_call_element *)user_data; call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; - grpc_transport_op *op = &calld->op; + grpc_transport_stream_op *op = &calld->op; grpc_metadata_batch *mdb; size_t i; if (status != GRPC_CREDENTIALS_OK) { @@ -131,7 +131,7 @@ static char *build_service_url(const char *url_scheme, call_data *calld) { } static void send_security_metadata(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; grpc_client_security_context *ctx = @@ -193,7 +193,7 @@ static void on_host_checked(void *user_data, grpc_security_status status) { op contains type and call direction information, in addition to the data that is being sent or received. */ static void auth_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; @@ -253,17 +253,10 @@ static void auth_start_transport_op(grpc_call_element *elem, grpc_call_next_op(elem, op); } -/* Called on special channel events, such as disconnection or new incoming - calls on the server */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - grpc_channel_next_op(elem, op); -} - /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; calld->creds = NULL; calld->host = NULL; @@ -287,7 +280,7 @@ static void destroy_call_elem(grpc_call_element *elem) { } /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { @@ -298,14 +291,14 @@ static void init_channel_elem(grpc_channel_element *elem, /* The first and the last filters tend to be implemented differently to handle the case that there's no 'next' filter to call on the up or down path */ - GPR_ASSERT(!is_first); GPR_ASSERT(!is_last); GPR_ASSERT(sc != NULL); /* initialize members */ GPR_ASSERT(sc->is_client_side); chand->security_connector = - (grpc_channel_security_connector *)grpc_security_connector_ref(sc); + (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF( + sc, "client_auth_filter"); chand->md_ctx = metadata_context; chand->authority_string = grpc_mdstr_from_string(chand->md_ctx, ":authority"); chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path"); @@ -318,7 +311,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *chand = elem->channel_data; grpc_channel_security_connector *ctx = chand->security_connector; - if (ctx != NULL) grpc_security_connector_unref(&ctx->base); + if (ctx != NULL) + GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter"); if (chand->authority_string != NULL) { grpc_mdstr_unref(chand->authority_string); } @@ -334,6 +328,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } const grpc_channel_filter grpc_client_auth_filter = { - auth_start_transport_op, channel_op, sizeof(call_data), + auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), init_call_elem, destroy_call_elem, sizeof(channel_data), init_channel_elem, destroy_channel_elem, "client-auth"}; diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index cf663faf2d..e79e9ce351 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -225,7 +225,7 @@ static grpc_security_status ssl_create_security_connector( arg.type = GRPC_ARG_STRING; arg.key = GRPC_ARG_HTTP2_SCHEME; arg.value.string = "https"; - *new_args = grpc_channel_args_copy_and_add(args, &arg); + *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); return status; } diff --git a/src/core/security/secure_endpoint.c b/src/core/security/secure_endpoint.c index 73496d1153..3548198046 100644 --- a/src/core/security/secure_endpoint.c +++ b/src/core/security/secure_endpoint.c @@ -101,9 +101,7 @@ static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices, if (grpc_trace_secure_endpoint) { size_t i; for (i = 0; i < nslices; i++) { - char *data = - gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]), - GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT); + char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); gpr_free(data); } @@ -235,9 +233,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep, if (grpc_trace_secure_endpoint) { for (i = 0; i < nslices; i++) { - char *data = - gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]), - GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT); + char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); gpr_free(data); } diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c index 1b39ab141e..731b382f09 100644 --- a/src/core/security/secure_transport_setup.c +++ b/src/core/security/secure_transport_setup.c @@ -74,7 +74,7 @@ static void secure_transport_setup_done(grpc_secure_transport_setup *s, if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); gpr_slice_buffer_destroy(&s->left_overs); - grpc_security_connector_unref(s->connector); + GRPC_SECURITY_CONNECTOR_UNREF(s->connector, "secure_transport_setup"); gpr_free(s); } @@ -234,8 +234,9 @@ static void on_handshake_data_received_from_peer( gpr_slice_split_tail(&slices[i], consumed_slice_size)); gpr_slice_unref(slices[i]); /* split_tail above increments refcount. */ } - gpr_slice_buffer_addn(&s->left_overs, &slices[i + 1], - num_left_overs - (size_t)has_left_overs_in_current_slice); + gpr_slice_buffer_addn( + &s->left_overs, &slices[i + 1], + num_left_overs - (size_t)has_left_overs_in_current_slice); check_peer(s); } @@ -275,7 +276,8 @@ void grpc_setup_secure_transport(grpc_security_connector *connector, secure_transport_setup_done(s, 0); return; } - s->connector = grpc_security_connector_ref(connector); + s->connector = + GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); s->endpoint = nonsecure_endpoint; diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index 34cb0395a2..f6e423eb27 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -84,12 +84,12 @@ static const char *ssl_cipher_suites(void) { /* -- Common methods. -- */ /* Returns the first property with that name. */ -const tsi_peer_property *tsi_peer_get_property_by_name( - const tsi_peer *peer, const char *name) { +const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, + const char *name) { size_t i; if (peer == NULL) return NULL; for (i = 0; i < peer->property_count; i++) { - const tsi_peer_property* property = &peer->properties[i]; + const tsi_peer_property *property = &peer->properties[i]; if (name == NULL && property->name == NULL) { return property; } @@ -124,24 +124,44 @@ grpc_security_status grpc_channel_security_connector_check_call_host( return sc->check_call_host(sc, host, cb, user_data); } -void grpc_security_connector_unref(grpc_security_connector *sc) { - if (sc == NULL) return; - if (gpr_unref(&sc->refcount)) sc->vtable->destroy(sc); -} - +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *sc, const char *file, int line, + const char *reason) { + if (sc == NULL) return NULL; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p ref %d -> %d %s", sc, + (int)sc->refcount.count, (int)sc->refcount.count + 1, reason); +#else grpc_security_connector *grpc_security_connector_ref( grpc_security_connector *sc) { if (sc == NULL) return NULL; +#endif gpr_ref(&sc->refcount); return sc; } +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +void grpc_security_connector_unref(grpc_security_connector *sc, + const char *file, int line, + const char *reason) { + if (sc == NULL) return; + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p unref %d -> %d %s", sc, + (int)sc->refcount.count, (int)sc->refcount.count - 1, reason); +#else +void grpc_security_connector_unref(grpc_security_connector *sc) { + if (sc == NULL) return; +#endif + if (gpr_unref(&sc->refcount)) sc->vtable->destroy(sc); +} + static void connector_pointer_arg_destroy(void *p) { - grpc_security_connector_unref(p); + GRPC_SECURITY_CONNECTOR_UNREF(p, "connector_pointer_arg"); } static void *connector_pointer_arg_copy(void *p) { - return grpc_security_connector_ref(p); + return GRPC_SECURITY_CONNECTOR_REF(p, "connector_pointer_arg"); } grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) { @@ -580,7 +600,8 @@ grpc_security_status grpc_ssl_channel_security_connector_create( config->pem_private_key, config->pem_private_key_size, config->pem_cert_chain, config->pem_cert_chain_size, pem_root_certs, pem_root_certs_size, ssl_cipher_suites(), alpn_protocol_strings, - alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, &c->handshaker_factory); + alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, + &c->handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", tsi_result_to_string(result)); @@ -633,8 +654,8 @@ grpc_security_status grpc_ssl_server_security_connector_create( (const unsigned char **)config->pem_cert_chains, config->pem_cert_chains_sizes, config->num_key_cert_pairs, config->pem_root_certs, config->pem_root_certs_size, ssl_cipher_suites(), - alpn_protocol_strings, alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, - &c->handshaker_factory); + alpn_protocol_strings, alpn_protocol_string_lengths, + (uint16_t)num_alpn_protocols, &c->handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", tsi_result_to_string(result)); diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h index ee3057b43b..a4c723f026 100644 --- a/src/core/security/security_connector.h +++ b/src/core/security/security_connector.h @@ -80,12 +80,25 @@ struct grpc_security_connector { grpc_auth_context *auth_context; /* Populated after the peer is checked. */ }; -/* Increments the refcount. */ +/* Refcounting. */ +#ifdef GRPC_SECURITY_CONNECTOR_REFCOUNT_DEBUG +#define GRPC_SECURITY_CONNECTOR_REF(p, r) \ + grpc_security_connector_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) \ + grpc_security_connector_unref((p), __FILE__, __LINE__, (r)) grpc_security_connector *grpc_security_connector_ref( - grpc_security_connector *sc); - -/* Decrements the refcount and destroys the object if it reaches 0. */ -void grpc_security_connector_unref(grpc_security_connector *sc); + grpc_security_connector *policy, const char *file, int line, + const char *reason); +void grpc_security_connector_unref(grpc_security_connector *policy, + const char *file, int line, + const char *reason); +#else +#define GRPC_SECURITY_CONNECTOR_REF(p, r) grpc_security_connector_ref((p)) +#define GRPC_SECURITY_CONNECTOR_UNREF(p, r) grpc_security_connector_unref((p)) +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *policy); +void grpc_security_connector_unref(grpc_security_connector *policy); +#endif /* Handshake creation. */ grpc_security_status grpc_security_connector_create_handshaker( @@ -172,9 +185,9 @@ typedef struct { specific error code otherwise. */ grpc_security_status grpc_ssl_channel_security_connector_create( - grpc_credentials *request_metadata_creds, - const grpc_ssl_config *config, const char *target_name, - const char *overridden_target_name, grpc_channel_security_connector **sc); + grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, + const char *target_name, const char *overridden_target_name, + grpc_channel_security_connector **sc); /* Gets the default ssl roots. */ size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs); @@ -200,8 +213,8 @@ grpc_security_status grpc_ssl_server_security_connector_create( const grpc_ssl_server_config *config, grpc_security_connector **sc); /* Util. */ -const tsi_peer_property *tsi_peer_get_property_by_name( - const tsi_peer *peer, const char *name); +const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, + const char *name); /* Exposed for testing only. */ grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer); diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c index b8639287a5..10eef6d237 100644 --- a/src/core/security/server_auth_filter.c +++ b/src/core/security/server_auth_filter.c @@ -51,24 +51,17 @@ typedef struct channel_data { op contains type and call direction information, in addition to the data that is being sent or received. */ static void auth_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { + grpc_transport_stream_op *op) { /* TODO(jboeuf): Get the metadata and get a new context from it. */ /* pass control down the stack */ grpc_call_next_op(elem, op); } -/* Called on special channel events, such as disconnection or new incoming - calls on the server */ -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - grpc_channel_next_op(elem, op); -} - /* Constructor for call_data */ static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; @@ -95,11 +88,10 @@ static void init_call_elem(grpc_call_element *elem, } /* Destructor for call_data */ -static void destroy_call_elem(grpc_call_element *elem) { -} +static void destroy_call_elem(grpc_call_element *elem) {} /* Constructor for channel_data */ -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { grpc_security_connector *sc = grpc_find_security_connector_in_args(args); @@ -115,17 +107,19 @@ static void init_channel_elem(grpc_channel_element *elem, /* initialize members */ GPR_ASSERT(!sc->is_client_side); - chand->security_connector = grpc_security_connector_ref(sc); + chand->security_connector = + GRPC_SECURITY_CONNECTOR_REF(sc, "server_auth_filter"); } /* Destructor for channel data */ static void destroy_channel_elem(grpc_channel_element *elem) { /* grab pointers to our data from the channel element */ channel_data *chand = elem->channel_data; - grpc_security_connector_unref(chand->security_connector); + GRPC_SECURITY_CONNECTOR_UNREF(chand->security_connector, + "server_auth_filter"); } const grpc_channel_filter grpc_server_auth_filter = { - auth_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "server-auth"}; + auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), + init_call_elem, destroy_call_elem, sizeof(channel_data), + init_channel_elem, destroy_channel_elem, "server-auth"}; diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 53afa1caad..8a7ada07af 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -70,38 +70,39 @@ static void state_unref(grpc_server_secure_state *state) { gpr_mu_lock(&state->mu); gpr_mu_unlock(&state->mu); /* clean up */ - grpc_security_connector_unref(state->sc); + GRPC_SECURITY_CONNECTOR_UNREF(state->sc, "server"); gpr_free(state); } } -static grpc_transport_setup_result setup_transport(void *statep, - grpc_transport *transport, - grpc_mdctx *mdctx) { +static void setup_transport(void *statep, grpc_transport *transport, + grpc_mdctx *mdctx) { static grpc_channel_filter const *extra_filters[] = { &grpc_server_auth_filter, &grpc_http_server_filter}; grpc_server_secure_state *state = statep; - grpc_transport_setup_result result; grpc_arg connector_arg = grpc_security_connector_to_arg(state->sc); grpc_channel_args *args_copy = grpc_channel_args_copy_and_add( - grpc_server_get_channel_args(state->server), &connector_arg); - result = grpc_server_setup_transport(state->server, transport, extra_filters, - GPR_ARRAY_SIZE(extra_filters), mdctx, - args_copy); + grpc_server_get_channel_args(state->server), &connector_arg, 1); + grpc_server_setup_transport(state->server, transport, extra_filters, + GPR_ARRAY_SIZE(extra_filters), mdctx, args_copy); grpc_channel_args_destroy(args_copy); - return result; } static void on_secure_transport_setup_done(void *statep, grpc_security_status status, grpc_endpoint *secure_endpoint) { grpc_server_secure_state *state = statep; + grpc_transport *transport; + grpc_mdctx *mdctx; if (status == GRPC_SECURITY_OK) { gpr_mu_lock(&state->mu); if (!state->is_shutdown) { - grpc_create_chttp2_transport( - setup_transport, state, grpc_server_get_channel_args(state->server), - secure_endpoint, NULL, 0, grpc_mdctx_create(), 0); + mdctx = grpc_mdctx_create(); + transport = grpc_create_chttp2_transport( + grpc_server_get_channel_args(state->server), secure_endpoint, mdctx, + 0); + setup_transport(state, transport, mdctx); + grpc_chttp2_transport_start_reading(transport, NULL, 0); } else { /* We need to consume this here, because the server may already have gone * away. */ @@ -220,7 +221,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, /* Error path: cleanup and return */ error: if (sc) { - grpc_security_connector_unref(sc); + GRPC_SECURITY_CONNECTOR_UNREF(sc, "server"); } if (resolved) { grpc_resolved_addresses_destroy(resolved); diff --git a/src/core/support/cpu_windows.c b/src/core/support/cpu_windows.c index 107a7b85f0..ce32eb0a9d 100644 --- a/src/core/support/cpu_windows.c +++ b/src/core/support/cpu_windows.c @@ -34,7 +34,6 @@ #include <grpc/support/port_platform.h> #ifdef GPR_WIN32 -#include <windows.h> #include <grpc/support/log.h> unsigned gpr_cpu_num_cores(void) { diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c index 48349d2c83..7937466b79 100644 --- a/src/core/support/log_linux.c +++ b/src/core/support/log_linux.c @@ -43,7 +43,9 @@ #ifdef GPR_LINUX +#include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/string_util.h> #include <grpc/support/time.h> #include <stdio.h> #include <stdarg.h> @@ -71,6 +73,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, void gpr_default_log(gpr_log_func_args *args) { char *final_slash; + char *prefix; const char *display_file; char time_buffer[64]; gpr_timespec now = gpr_now(); @@ -89,10 +92,12 @@ void gpr_default_log(gpr_log_func_args *args) { strcpy(time_buffer, "error:strftime"); } - fprintf(stderr, "%s%s.%09d %7ld %s:%d] %s\n", + gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]", gpr_log_severity_string(args->severity), time_buffer, - (int)(now.tv_nsec), gettid(), display_file, args->line, - args->message); + (int)(now.tv_nsec), gettid(), display_file, args->line); + + fprintf(stderr, "%-60s %s\n", prefix, args->message); + gpr_free(prefix); } #endif diff --git a/src/core/support/slice.c b/src/core/support/slice.c index a2d62fc1e5..e4196a48c6 100644 --- a/src/core/support/slice.c +++ b/src/core/support/slice.c @@ -325,3 +325,10 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) { if (d != 0) return d; return memcmp(GPR_SLICE_START_PTR(a), b, b_length); } + +char *gpr_slice_to_cstring(gpr_slice slice) { + char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1); + memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)); + result[GPR_SLICE_LENGTH(slice)] = '\0'; + return result; +} diff --git a/src/core/support/string.c b/src/core/support/string.c index 6a80ccc841..09598da946 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -61,14 +61,14 @@ typedef struct { size_t capacity; size_t length; char *data; -} hexout; +} dump_out; -static hexout hexout_create(void) { - hexout r = {0, 0, NULL}; +static dump_out dump_out_create(void) { + dump_out r = {0, 0, NULL}; return r; } -static void hexout_append(hexout *out, char c) { +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); @@ -76,34 +76,55 @@ static void hexout_append(hexout *out, char c) { out->data[out->length++] = c; } -char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags) { +static void hexdump(dump_out *out, const char *buf, size_t len) { static const char hex[16] = "0123456789abcdef"; - hexout out = hexout_create(); const gpr_uint8 *const beg = (const gpr_uint8 *)buf; const gpr_uint8 *const end = beg + len; const gpr_uint8 *cur; for (cur = beg; cur != end; ++cur) { - if (cur != beg) hexout_append(&out, ' '); - hexout_append(&out, hex[*cur >> 4]); - hexout_append(&out, hex[*cur & 0xf]); + if (cur != beg) dump_out_append(out, ' '); + dump_out_append(out, hex[*cur >> 4]); + dump_out_append(out, hex[*cur & 0xf]); } +} - if (flags & GPR_HEXDUMP_PLAINTEXT) { - if (len) hexout_append(&out, ' '); - hexout_append(&out, '\''); - for (cur = beg; cur != end; ++cur) { - hexout_append(&out, isprint(*cur) ? *(char*)cur : '.'); - } - hexout_append(&out, '\''); +static void asciidump(dump_out *out, const char *buf, size_t len) { + const gpr_uint8 *const beg = (const gpr_uint8 *)buf; + const gpr_uint8 *const end = beg + len; + const gpr_uint8 *cur; + int out_was_empty = (out->length == 0); + if (!out_was_empty) { + dump_out_append(out, ' '); + dump_out_append(out, '\''); } + for (cur = beg; cur != end; ++cur) { + dump_out_append(out, isprint(*cur) ? *(char *)cur : '.'); + } + if (!out_was_empty) { + dump_out_append(out, '\''); + } +} - hexout_append(&out, 0); - +char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags) { + dump_out out = dump_out_create(); + if (flags & GPR_DUMP_HEX) { + hexdump(&out, buf, len); + } + if (flags & GPR_DUMP_ASCII) { + asciidump(&out, buf, len); + } + dump_out_append(&out, 0); return out.data; } +char *gpr_dump_slice(gpr_slice s, gpr_uint32 flags) { + return gpr_dump((const char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s), + flags); +} + + int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) { gpr_uint32 out = 0; gpr_uint32 new; diff --git a/src/core/support/string.h b/src/core/support/string.h index 31e9fcb5e9..d950d908d6 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -37,6 +37,7 @@ #include <stddef.h> #include <grpc/support/port_platform.h> +#include <grpc/support/slice.h> #ifdef __cplusplus extern "C" { @@ -44,12 +45,16 @@ extern "C" { /* String utility functions */ -/* flag to include plaintext after a hexdump */ -#define GPR_HEXDUMP_PLAINTEXT 0x00000001 +/* Flags for gpr_dump function. */ +#define GPR_DUMP_HEX 0x00000001 +#define GPR_DUMP_ASCII 0x00000002 -/* Converts array buf, of length len, into a hexadecimal dump. Result should - be freed with gpr_free() */ -char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags); +/* Converts array buf, of length len, into a C string according to the flags. + Result should be freed with gpr_free() */ +char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags); + +/* Calls gpr_dump on a slice. */ +char *gpr_dump_slice(gpr_slice slice, gpr_uint32 flags); /* Parses an array of bytes into an integer (base 10). Returns 1 on success, 0 on failure. */ diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c index 6d1d6337a9..27b9f3637a 100644 --- a/src/core/support/string_win32.c +++ b/src/core/support/string_win32.c @@ -37,7 +37,6 @@ #ifdef GPR_WIN32 -#include <windows.h> #include <stdio.h> #include <stdarg.h> #include <string.h> diff --git a/src/core/support/string_win32.h b/src/core/support/string_win32.h index 0bc3247d9d..1260aa55c1 100644 --- a/src/core/support/string_win32.h +++ b/src/core/support/string_win32.h @@ -38,8 +38,6 @@ #ifdef GPR_WIN32 -#include <windows.h> - /* These allocate new strings using gpr_malloc to convert from and to utf-8. */ LPTSTR gpr_char_to_tchar(LPCSTR input); LPSTR gpr_tchar_to_char(LPCTSTR input); diff --git a/src/core/support/sync_win32.c b/src/core/support/sync_win32.c index cc31d9b052..72f39f8d46 100644 --- a/src/core/support/sync_win32.c +++ b/src/core/support/sync_win32.c @@ -37,9 +37,6 @@ #ifdef GPR_WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#include <windows.h> #include <grpc/support/log.h> #include <grpc/support/sync.h> #include <grpc/support/time.h> diff --git a/src/core/support/thd_win32.c b/src/core/support/thd_win32.c index 3cc798293a..4fa3907444 100644 --- a/src/core/support/thd_win32.c +++ b/src/core/support/thd_win32.c @@ -37,7 +37,6 @@ #ifdef GPR_WIN32 -#include <windows.h> #include <string.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c index f4443b5c2d..9db267c01b 100644 --- a/src/core/support/time_win32.c +++ b/src/core/support/time_win32.c @@ -39,7 +39,6 @@ #include <grpc/support/time.h> #include <sys/timeb.h> -#include <windows.h> gpr_timespec gpr_now(void) { gpr_timespec now_tv; diff --git a/src/core/surface/byte_buffer.c b/src/core/surface/byte_buffer.c index 4817e00454..a930949f2d 100644 --- a/src/core/surface/byte_buffer.c +++ b/src/core/surface/byte_buffer.c @@ -55,6 +55,20 @@ grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( return bb; } +grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( + grpc_byte_buffer_reader *reader) { + grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer)); + gpr_slice slice; + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = GRPC_COMPRESS_NONE; + gpr_slice_buffer_init(&bb->data.raw.slice_buffer); + + while (grpc_byte_buffer_reader_next(reader, &slice)) { + gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slice); + } + return bb; +} + grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { switch (bb->type) { case GRPC_BB_RAW: diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 181617fff8..a28a542c8d 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -76,14 +76,14 @@ typedef struct { typedef struct { /* Overall status of the operation: starts OK, may degrade to non-OK */ - int success; - /* Completion function to call at the end of the operation */ - grpc_ioreq_completion_func on_complete; - void *user_data; + gpr_uint8 success; /* a bit mask of which request ops are needed (1u << opid) */ gpr_uint16 need_mask; /* a bit mask of which request ops are now completed */ gpr_uint16 complete_mask; + /* Completion function to call at the end of the operation */ + grpc_ioreq_completion_func on_complete; + void *user_data; } reqinfo_master; /* Status data for a request can come from several sources; this @@ -262,8 +262,8 @@ struct grpc_call { static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline); static void call_on_done_recv(void *call, int success); static void call_on_done_send(void *call, int success); -static int fill_send_ops(grpc_call *call, grpc_transport_op *op); -static void execute_op(grpc_call *call, grpc_transport_op *op); +static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op); +static void execute_op(grpc_call *call, grpc_transport_stream_op *op); static void recv_metadata(grpc_call *call, grpc_metadata_batch *metadata); static void finish_read_ops(grpc_call *call); static grpc_call_error cancel_with_status(grpc_call *c, grpc_status_code status, @@ -279,8 +279,8 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq, size_t add_initial_metadata_count, gpr_timespec send_deadline) { size_t i; - grpc_transport_op initial_op; - grpc_transport_op *initial_op_ptr = NULL; + grpc_transport_stream_op initial_op; + grpc_transport_stream_op *initial_op_ptr = NULL; grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel); grpc_call *call = gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); @@ -298,8 +298,6 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq, if (call->is_client) { call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE; call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE; - call->context[GRPC_CONTEXT_TRACING].value = grpc_census_context_create(); - call->context[GRPC_CONTEXT_TRACING].destroy = grpc_census_context_destroy; } GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT); for (i = 0; i < add_initial_metadata_count; i++) { @@ -464,12 +462,11 @@ static int need_more_data(grpc_call *call) { (is_op_live(call, GRPC_IOREQ_RECV_CLOSE) && grpc_bbq_empty(&call->incoming_queue)) || (call->write_state == WRITE_STATE_INITIAL && !call->is_client) || - (call->cancel_with_status != GRPC_STATUS_OK) || - call->destroy_called; + (call->cancel_with_status != GRPC_STATUS_OK) || call->destroy_called; } static void unlock(grpc_call *call) { - grpc_transport_op op; + grpc_transport_stream_op op; completed_request completed_requests[GRPC_IOREQ_OP_COUNT]; int completing_requests = 0; int start_op = 0; @@ -888,7 +885,7 @@ static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer, } } -static int fill_send_ops(grpc_call *call, grpc_transport_op *op) { +static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) { grpc_ioreq_data data; gpr_uint32 flags; grpc_metadata_batch mdb; @@ -1144,7 +1141,7 @@ static void finished_loose_op_allocated(void *alloc, int success) { gpr_free(args); } -static void execute_op(grpc_call *call, grpc_transport_op *op) { +static void execute_op(grpc_call *call, grpc_transport_stream_op *op) { grpc_call_element *elem; GPR_ASSERT(op->on_consumed == NULL); @@ -1155,14 +1152,15 @@ static void execute_op(grpc_call *call, grpc_transport_op *op) { } else { finished_loose_op_allocated_args *args = gpr_malloc(sizeof(*args)); args->call = call; - grpc_iomgr_closure_init(&args->closure, finished_loose_op_allocated, args); + grpc_iomgr_closure_init(&args->closure, finished_loose_op_allocated, + args); op->on_consumed = &args->closure; } } elem = CALL_ELEM_FROM_CALL(call, 0); op->context = call->context; - elem->filter->start_transport_op(elem, op); + elem->filter->start_transport_stream_op(elem, op); } grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { @@ -1229,13 +1227,13 @@ static gpr_uint32 decode_compression(grpc_mdelem *md) { } else { gpr_uint32 parsed_clevel_bytes; if (gpr_parse_bytes_to_uint32(grpc_mdstr_as_c_string(md->value), - GPR_SLICE_LENGTH(md->value->slice), - &parsed_clevel_bytes)) { + GPR_SLICE_LENGTH(md->value->slice), + &parsed_clevel_bytes)) { /* the following cast is safe, as a gpr_uint32 should be able to hold all * possible values of the grpc_compression_level enum */ - clevel = (grpc_compression_level) parsed_clevel_bytes; + clevel = (grpc_compression_level)parsed_clevel_bytes; } else { - clevel = GRPC_COMPRESS_LEVEL_NONE; /* could not parse, no compression */ + clevel = GRPC_COMPRESS_LEVEL_NONE; /* could not parse, no compression */ } grpc_mdelem_set_user_data(md, destroy_compression, (void *)(gpr_intptr)(clevel + COMPRESS_OFFSET)); @@ -1258,7 +1256,8 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { set_status_code(call, STATUS_FROM_WIRE, decode_status(md)); } else if (key == grpc_channel_get_message_string(call->channel)) { set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value)); - } else if (key == grpc_channel_get_compresssion_level_string(call->channel)) { + } else if (key == + grpc_channel_get_compresssion_level_string(call->channel)) { set_decode_compression_level(call, decode_compression(md)); } else { dest = &call->buffered_metadata[is_trailing]; diff --git a/src/core/surface/call.h b/src/core/surface/call.h index fb3662b50d..3b6f9c942e 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -78,8 +78,8 @@ typedef union { typedef struct { grpc_ioreq_op op; - grpc_ioreq_data data; gpr_uint32 flags; /**< A copy of the write flags from grpc_op */ + grpc_ioreq_data data; } grpc_ioreq; typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success, diff --git a/src/core/surface/call_log_batch.c b/src/core/surface/call_log_batch.c index 55663298c9..997046d954 100644 --- a/src/core/surface/call_log_batch.c +++ b/src/core/surface/call_log_batch.c @@ -46,8 +46,8 @@ static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) { gpr_strvec_add(b, gpr_strdup(md[i].key)); gpr_strvec_add(b, gpr_strdup(" value=")); - gpr_strvec_add(b, gpr_hexdump(md[i].value, md[i].value_length, - GPR_HEXDUMP_PLAINTEXT)); + gpr_strvec_add(b, gpr_dump(md[i].value, md[i].value_length, + GPR_DUMP_HEX | GPR_DUMP_ASCII)); } } diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index a3c4dcebc1..f8151c121c 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -39,7 +39,6 @@ #include "src/core/iomgr/iomgr.h" #include "src/core/support/string.h" #include "src/core/surface/call.h" -#include "src/core/surface/client.h" #include "src/core/surface/init.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -94,9 +93,8 @@ grpc_channel *grpc_channel_create_from_filters( grpc_channel *channel = gpr_malloc(size); GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); channel->is_client = is_client; - /* decremented by grpc_channel_destroy, and grpc_client_channel_closed if - * is_client */ - gpr_ref_init(&channel->refs, 1 + is_client); + /* decremented by grpc_channel_destroy */ + gpr_ref_init(&channel->refs, 1); channel->metadata_context = mdctx; channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status"); channel->grpc_compression_level_string = @@ -111,8 +109,6 @@ grpc_channel *grpc_channel_create_from_filters( } channel->path_string = grpc_mdstr_from_string(mdctx, ":path"); channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority"); - grpc_channel_stack_init(filters, num_filters, args, channel->metadata_context, - CHANNEL_STACK_FROM_CHANNEL(channel)); gpr_mu_init(&channel->registered_call_mu); channel->registered_calls = NULL; @@ -133,6 +129,10 @@ grpc_channel *grpc_channel_create_from_filters( } } + grpc_channel_stack_init(filters, num_filters, channel, args, + channel->metadata_context, + CHANNEL_STACK_FROM_CHANNEL(channel)); + return channel; } @@ -239,28 +239,16 @@ void grpc_channel_internal_unref(grpc_channel *channel) { } void grpc_channel_destroy(grpc_channel *channel) { - grpc_channel_op op; + grpc_transport_op op; grpc_channel_element *elem; - + memset(&op, 0, sizeof(op)); + op.disconnect = 1; elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); - - op.type = GRPC_CHANNEL_GOAWAY; - op.dir = GRPC_CALL_DOWN; - op.data.goaway.status = GRPC_STATUS_OK; - op.data.goaway.message = gpr_slice_from_copied_string("Client disconnect"); - elem->filter->channel_op(elem, NULL, &op); - - op.type = GRPC_CHANNEL_DISCONNECT; - op.dir = GRPC_CALL_DOWN; - elem->filter->channel_op(elem, NULL, &op); + elem->filter->start_transport_op(elem, &op); GRPC_CHANNEL_INTERNAL_UNREF(channel, "channel"); } -void grpc_client_channel_closed(grpc_channel_element *elem) { - GRPC_CHANNEL_INTERNAL_UNREF(CHANNEL_FROM_TOP_ELEM(elem), "closed"); -} - grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { return CHANNEL_STACK_FROM_CHANNEL(channel); } @@ -277,7 +265,6 @@ grpc_mdstr *grpc_channel_get_compresssion_level_string(grpc_channel *channel) { return channel->grpc_compression_level_string; } - grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) { if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) { return grpc_mdelem_ref(channel->grpc_status_elem[i]); diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h index 3c04676b43..71f8a55731 100644 --- a/src/core/surface/channel.h +++ b/src/core/surface/channel.h @@ -35,6 +35,7 @@ #define GRPC_INTERNAL_CORE_SURFACE_CHANNEL_H #include "src/core/channel/channel_stack.h" +#include "src/core/client_config/subchannel_factory.h" grpc_channel *grpc_channel_create_from_filters( const grpc_channel_filter **filters, size_t count, @@ -57,8 +58,6 @@ grpc_mdstr *grpc_channel_get_compresssion_level_string(grpc_channel *channel); grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel); gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel); -void grpc_client_channel_closed(grpc_channel_element *elem); - #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG void grpc_channel_internal_ref(grpc_channel *channel, const char *reason); void grpc_channel_internal_unref(grpc_channel *channel, const char *reason); diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c index d069a04a9a..e205f0a9f8 100644 --- a/src/core/surface/channel_create.c +++ b/src/core/surface/channel_create.c @@ -31,159 +31,120 @@ * */ -#include "src/core/iomgr/sockaddr.h" - #include <grpc/grpc.h> #include <stdlib.h> #include <string.h> -#include "src/core/channel/census_filter.h" +#include <grpc/support/alloc.h> + #include "src/core/channel/channel_args.h" #include "src/core/channel/client_channel.h" -#include "src/core/channel/client_setup.h" -#include "src/core/channel/connected_channel.h" #include "src/core/channel/http_client_filter.h" -#include "src/core/iomgr/endpoint.h" -#include "src/core/iomgr/resolve_address.h" +#include "src/core/client_config/resolver_registry.h" #include "src/core/iomgr/tcp_client.h" #include "src/core/surface/channel.h" -#include "src/core/surface/client.h" -#include "src/core/support/string.h" #include "src/core/transport/chttp2_transport.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include <grpc/support/sync.h> -#include <grpc/support/useful.h> - -typedef struct setup setup; -/* A single setup request (started via initiate) */ typedef struct { - grpc_client_setup_request *cs_request; - setup *setup; - /* Resolved addresses, or null if resolution not yet completed */ - grpc_resolved_addresses *resolved; - /* which address in resolved should we pick for the next connection attempt */ - size_t resolved_index; -} request; - -/* Global setup logic (may be running many simultaneous setup requests, but - with only one 'active' */ -struct setup { - const char *target; - grpc_transport_setup_callback setup_callback; - void *setup_user_data; -}; - -static int maybe_try_next_resolved(request *r); - -static void done(request *r, int was_successful) { - grpc_client_setup_request_finish(r->cs_request, was_successful); - if (r->resolved) { - grpc_resolved_addresses_destroy(r->resolved); - } - gpr_free(r); + grpc_connector base; + gpr_refcount refs; + + grpc_iomgr_closure *notify; + grpc_connect_in_args args; + grpc_connect_out_args *result; +} connector; + +static void connector_ref(grpc_connector *con) { + connector *c = (connector *)con; + gpr_ref(&c->refs); } -/* connection callback: tcp is either valid, or null on error */ -static void on_connect(void *rp, grpc_endpoint *tcp) { - request *r = rp; - - if (!grpc_client_setup_request_should_continue(r->cs_request, "on_connect")) { - if (tcp) { - grpc_endpoint_shutdown(tcp); - grpc_endpoint_destroy(tcp); - } - done(r, 0); - return; +static void connector_unref(grpc_connector *con) { + connector *c = (connector *)con; + if (gpr_unref(&c->refs)) { + gpr_free(c); } +} - if (!tcp) { - if (!maybe_try_next_resolved(r)) { - done(r, 0); - return; - } else { - return; - } - } else if (grpc_client_setup_cb_begin(r->cs_request, "on_connect")) { - grpc_create_chttp2_transport( - r->setup->setup_callback, r->setup->setup_user_data, - grpc_client_setup_get_channel_args(r->cs_request), tcp, NULL, 0, - grpc_client_setup_get_mdctx(r->cs_request), 1); - grpc_client_setup_cb_end(r->cs_request, "on_connect"); - done(r, 1); - return; +static void connected(void *arg, grpc_endpoint *tcp) { + connector *c = arg; + grpc_iomgr_closure *notify; + if (tcp != NULL) { + c->result->transport = grpc_create_chttp2_transport( + c->args.channel_args, tcp, c->args.metadata_context, 1); + grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + GPR_ASSERT(c->result->transport); + c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *)); + c->result->filters[0] = &grpc_http_client_filter; + c->result->num_filters = 1; } else { - done(r, 0); + memset(c->result, 0, sizeof(*c->result)); } + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); } -/* attempt to connect to the next available resolved address */ -static int maybe_try_next_resolved(request *r) { - grpc_resolved_address *addr; - if (!r->resolved) return 0; - if (r->resolved_index == r->resolved->naddrs) return 0; - addr = &r->resolved->addrs[r->resolved_index++]; - grpc_tcp_client_connect( - on_connect, r, grpc_client_setup_get_interested_parties(r->cs_request), - (struct sockaddr *)&addr->addr, addr->len, - grpc_client_setup_request_deadline(r->cs_request)); - return 1; +static void connector_connect(grpc_connector *con, + const grpc_connect_in_args *args, + grpc_connect_out_args *result, + grpc_iomgr_closure *notify) { + connector *c = (connector *)con; + GPR_ASSERT(c->notify == NULL); + GPR_ASSERT(notify->cb); + c->notify = notify; + c->args = *args; + c->result = result; + grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, + args->addr_len, args->deadline); } -/* callback for when our target address has been resolved */ -static void on_resolved(void *rp, grpc_resolved_addresses *resolved) { - request *r = rp; - - /* if we're not still the active request, abort */ - if (!grpc_client_setup_request_should_continue(r->cs_request, - "on_resolved")) { - if (resolved) { - grpc_resolved_addresses_destroy(resolved); - } - done(r, 0); - return; - } +static const grpc_connector_vtable connector_vtable = { + connector_ref, connector_unref, connector_connect}; - if (!resolved) { - done(r, 0); - return; - } else { - r->resolved = resolved; - r->resolved_index = 0; - if (!maybe_try_next_resolved(r)) { - done(r, 0); - } - } +typedef struct { + grpc_subchannel_factory base; + gpr_refcount refs; + grpc_mdctx *mdctx; + grpc_channel_args *merge_args; +} subchannel_factory; + +static void subchannel_factory_ref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + gpr_ref(&f->refs); } -static void initiate_setup(void *sp, grpc_client_setup_request *cs_request) { - request *r = gpr_malloc(sizeof(request)); - r->setup = sp; - r->cs_request = cs_request; - r->resolved = NULL; - r->resolved_index = 0; - /* TODO(klempner): Make grpc_resolve_address respect deadline */ - grpc_resolve_address(r->setup->target, "http", on_resolved, r); +static void subchannel_factory_unref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + if (gpr_unref(&f->refs)) { + grpc_channel_args_destroy(f->merge_args); + grpc_mdctx_unref(f->mdctx); + gpr_free(f); + } } -static void done_setup(void *sp) { - setup *s = sp; - gpr_free((void *)s->target); - gpr_free(s); +static grpc_subchannel *subchannel_factory_create_subchannel( + grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + subchannel_factory *f = (subchannel_factory *)scf; + connector *c = gpr_malloc(sizeof(*c)); + grpc_channel_args *final_args = + grpc_channel_args_merge(args->args, f->merge_args); + grpc_subchannel *s; + memset(c, 0, sizeof(*c)); + c->base.vtable = &connector_vtable; + gpr_ref_init(&c->refs, 1); + args->mdctx = f->mdctx; + args->args = final_args; + s = grpc_subchannel_create(&c->base, args); + grpc_connector_unref(&c->base); + grpc_channel_args_destroy(final_args); + return s; } -static grpc_transport_setup_result complete_setup(void *channel_stack, - grpc_transport *transport, - grpc_mdctx *mdctx) { - static grpc_channel_filter const *extra_filters[] = { - &grpc_http_client_filter}; - return grpc_client_channel_transport_setup_complete( - channel_stack, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters), - mdctx); -} +static const grpc_subchannel_factory_vtable subchannel_factory_vtable = { + subchannel_factory_ref, subchannel_factory_unref, + subchannel_factory_create_subchannel}; /* Create a client channel: Asynchronously: - resolve target @@ -191,28 +152,36 @@ static grpc_transport_setup_result complete_setup(void *channel_stack, - perform handshakes */ grpc_channel *grpc_channel_create(const char *target, const grpc_channel_args *args) { - setup *s = gpr_malloc(sizeof(setup)); - grpc_mdctx *mdctx = grpc_mdctx_create(); grpc_channel *channel = NULL; #define MAX_FILTERS 3 const grpc_channel_filter *filters[MAX_FILTERS]; + grpc_resolver *resolver; + subchannel_factory *f; + grpc_mdctx *mdctx = grpc_mdctx_create(); int n = 0; - filters[n++] = &grpc_client_surface_filter; /* TODO(census) if (grpc_channel_args_is_census_enabled(args)) { filters[n++] = &grpc_client_census_filter; } */ filters[n++] = &grpc_client_channel_filter; GPR_ASSERT(n <= MAX_FILTERS); - channel = grpc_channel_create_from_filters(filters, n, args, mdctx, 1); - s->target = gpr_strdup(target); - s->setup_callback = complete_setup; - s->setup_user_data = grpc_channel_get_channel_stack(channel); + f = gpr_malloc(sizeof(*f)); + f->base.vtable = &subchannel_factory_vtable; + gpr_ref_init(&f->refs, 1); + grpc_mdctx_ref(mdctx); + f->mdctx = mdctx; + f->merge_args = grpc_channel_args_copy(args); + resolver = grpc_resolver_create(target, &f->base); + if (!resolver) { + return NULL; + } - grpc_client_setup_create_and_attach(grpc_channel_get_channel_stack(channel), - args, mdctx, initiate_setup, done_setup, - s); + channel = grpc_channel_create_from_filters(filters, n, args, mdctx, 1); + grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), + resolver); + GRPC_RESOLVER_UNREF(resolver, "create"); + grpc_subchannel_factory_unref(&f->base); return channel; } diff --git a/src/core/surface/client.c b/src/core/surface/client.c deleted file mode 100644 index 8ac4dd1e0e..0000000000 --- a/src/core/surface/client.c +++ /dev/null @@ -1,89 +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. - * - */ - -#include "src/core/surface/client.h" - -#include "src/core/surface/call.h" -#include "src/core/surface/channel.h" -#include "src/core/support/string.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -typedef struct { void *unused; } call_data; - -typedef struct { void *unused; } channel_data; - -static void client_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - grpc_call_next_op(elem, op); -} - -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - switch (op->type) { - case GRPC_ACCEPT_CALL: - gpr_log(GPR_ERROR, "Client cannot accept new calls"); - break; - case GRPC_TRANSPORT_CLOSED: - grpc_client_channel_closed(elem); - break; - case GRPC_TRANSPORT_GOAWAY: - gpr_slice_unref(op->data.goaway.message); - break; - default: - GPR_ASSERT(op->dir == GRPC_CALL_DOWN); - grpc_channel_next_op(elem, op); - } -} - -static void init_call_elem(grpc_call_element *elem, - const void *transport_server_data, - grpc_transport_op *initial_op) {} - -static void destroy_call_elem(grpc_call_element *elem) {} - -static void init_channel_elem(grpc_channel_element *elem, - const grpc_channel_args *args, grpc_mdctx *mdctx, - int is_first, int is_last) { - GPR_ASSERT(is_first); - GPR_ASSERT(!is_last); -} - -static void destroy_channel_elem(grpc_channel_element *elem) {} - -const grpc_channel_filter grpc_client_surface_filter = { - client_start_transport_op, channel_op, sizeof(call_data), init_call_elem, - destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, "client", -}; diff --git a/src/core/surface/init.c b/src/core/surface/init.c index ca61a38a35..3847ded28c 100644 --- a/src/core/surface/init.c +++ b/src/core/surface/init.c @@ -31,9 +31,13 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/census.h> #include <grpc/grpc.h> #include "src/core/channel/channel_stack.h" +#include "src/core/client_config/resolver_registry.h" +#include "src/core/client_config/resolvers/dns_resolver.h" #include "src/core/debug/trace.h" #include "src/core/iomgr/iomgr.h" #include "src/core/profiling/timers.h" @@ -42,6 +46,10 @@ #include "src/core/surface/surface_trace.h" #include "src/core/transport/chttp2_transport.h" +#ifdef GPR_POSIX_SOCKET +#include "src/core/client_config/resolvers/unix_resolver_posix.h" +#endif + static gpr_once g_basic_init = GPR_ONCE_INIT; static gpr_mu g_init_mu; static int g_initializations; @@ -56,6 +64,11 @@ void grpc_init(void) { gpr_mu_lock(&g_init_mu); if (++g_initializations == 1) { + grpc_resolver_registry_init("dns:///"); + grpc_register_resolver_type("dns", grpc_dns_resolver_factory_create()); +#ifdef GPR_POSIX_SOCKET + grpc_register_resolver_type("unix", grpc_unix_resolver_factory_create()); +#endif grpc_register_tracer("channel", &grpc_trace_channel); grpc_register_tracer("surface", &grpc_surface_trace); grpc_register_tracer("http", &grpc_http_trace); @@ -79,6 +92,7 @@ void grpc_shutdown(void) { census_shutdown(); grpc_timers_global_destroy(); grpc_tracer_shutdown(); + grpc_resolver_registry_shutdown(); } gpr_mu_unlock(&g_init_mu); } diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 85e1ab5554..3dd56fe5a9 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -49,16 +49,16 @@ typedef struct { typedef struct { grpc_mdctx *mdctx; } channel_data; -static void lame_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { +static void lame_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - if (op->send_ops) { + if (op->send_ops != NULL) { grpc_stream_ops_unref_owned_objects(op->send_ops->ops, op->send_ops->nops); op->on_done_send->cb(op->on_done_send->cb_arg, 0); } - if (op->recv_ops) { + if (op->recv_ops != NULL) { char tmp[GPR_LTOA_MIN_BUFSIZE]; grpc_metadata_batch mdb; gpr_ltoa(GRPC_STATUS_UNKNOWN, tmp); @@ -77,36 +77,35 @@ static void lame_start_transport_op(grpc_call_element *elem, *op->recv_state = GRPC_STREAM_CLOSED; op->on_done_recv->cb(op->on_done_recv->cb_arg, 1); } - if (op->on_consumed) { + if (op->on_consumed != NULL) { op->on_consumed->cb(op->on_consumed->cb_arg, 0); } } -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - switch (op->type) { - case GRPC_CHANNEL_GOAWAY: - gpr_slice_unref(op->data.goaway.message); - break; - case GRPC_CHANNEL_DISCONNECT: - grpc_client_channel_closed(elem); - break; - default: - break; +static void lame_start_transport_op(grpc_channel_element *elem, + grpc_transport_op *op) { + if (op->on_connectivity_state_change) { + GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE); + *op->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE; + op->on_connectivity_state_change->cb( + op->on_connectivity_state_change->cb_arg, 1); + } + if (op->on_consumed != NULL) { + op->on_consumed->cb(op->on_consumed->cb_arg, 1); } } static void init_call_elem(grpc_call_element *elem, const void *transport_server_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { if (initial_op) { - grpc_transport_op_finish_with_failure(initial_op); + grpc_transport_stream_op_finish_with_failure(initial_op); } } static void destroy_call_elem(grpc_call_element *elem) {} -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *mdctx, int is_first, int is_last) { channel_data *chand = elem->channel_data; @@ -118,9 +117,15 @@ static void init_channel_elem(grpc_channel_element *elem, static void destroy_channel_elem(grpc_channel_element *elem) {} static const grpc_channel_filter lame_filter = { - lame_start_transport_op, channel_op, sizeof(call_data), - init_call_elem, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, "lame-client", + lame_start_transport_stream_op, + lame_start_transport_op, + sizeof(call_data), + init_call_elem, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + "lame-client", }; grpc_channel *grpc_lame_client_channel_create(void) { diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index fae3e4e90a..34ee3f8400 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -31,177 +31,147 @@ * */ -#include "src/core/iomgr/sockaddr.h" - #include <grpc/grpc.h> #include <stdlib.h> #include <string.h> -#include "src/core/channel/census_filter.h" +#include <grpc/support/alloc.h> + #include "src/core/channel/channel_args.h" #include "src/core/channel/client_channel.h" -#include "src/core/channel/client_setup.h" -#include "src/core/channel/connected_channel.h" #include "src/core/channel/http_client_filter.h" -#include "src/core/iomgr/resolve_address.h" +#include "src/core/client_config/resolver_registry.h" #include "src/core/iomgr/tcp_client.h" #include "src/core/security/auth_filters.h" #include "src/core/security/credentials.h" #include "src/core/security/secure_transport_setup.h" -#include "src/core/support/string.h" #include "src/core/surface/channel.h" -#include "src/core/surface/client.h" #include "src/core/transport/chttp2_transport.h" -#include <grpc/grpc_security.h> -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include <grpc/support/sync.h> -#include <grpc/support/useful.h> #include "src/core/tsi/transport_security_interface.h" -typedef struct setup setup; - -/* A single setup request (started via initiate) */ typedef struct { - grpc_client_setup_request *cs_request; - setup *setup; - /* Resolved addresses, or null if resolution not yet completed. */ - grpc_resolved_addresses *resolved; - /* which address in resolved should we pick for the next connection attempt */ - size_t resolved_index; -} request; + grpc_connector base; + gpr_refcount refs; -struct setup { grpc_channel_security_connector *security_connector; - const char *target; - grpc_transport_setup_callback setup_callback; - void *setup_user_data; -}; -static int maybe_try_next_resolved(request *r); + grpc_iomgr_closure *notify; + grpc_connect_in_args args; + grpc_connect_out_args *result; +} connector; -static void done(request *r, int was_successful) { - grpc_client_setup_request_finish(r->cs_request, was_successful); - if (r->resolved) { - grpc_resolved_addresses_destroy(r->resolved); +static void connector_ref(grpc_connector *con) { + connector *c = (connector *)con; + gpr_ref(&c->refs); +} + +static void connector_unref(grpc_connector *con) { + connector *c = (connector *)con; + if (gpr_unref(&c->refs)) { + gpr_free(c); } - gpr_free(r); } -static void on_secure_transport_setup_done(void *rp, +static void on_secure_transport_setup_done(void *arg, grpc_security_status status, grpc_endpoint *secure_endpoint) { - request *r = rp; + connector *c = arg; + grpc_iomgr_closure *notify; if (status != GRPC_SECURITY_OK) { gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); - done(r, 0); - } else if (grpc_client_setup_cb_begin(r->cs_request, - "on_secure_transport_setup_done")) { - grpc_create_chttp2_transport( - r->setup->setup_callback, r->setup->setup_user_data, - grpc_client_setup_get_channel_args(r->cs_request), secure_endpoint, - NULL, 0, grpc_client_setup_get_mdctx(r->cs_request), 1); - grpc_client_setup_cb_end(r->cs_request, "on_secure_transport_setup_done"); - done(r, 1); + memset(c->result, 0, sizeof(*c->result)); } else { - done(r, 0); + c->result->transport = grpc_create_chttp2_transport( + c->args.channel_args, secure_endpoint, c->args.metadata_context, 1); + grpc_chttp2_transport_start_reading(c->result->transport, NULL, 0); + c->result->filters = gpr_malloc(sizeof(grpc_channel_filter *) * 2); + c->result->filters[0] = &grpc_client_auth_filter; + c->result->filters[1] = &grpc_http_client_filter; + c->result->num_filters = 2; } + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); } -/* connection callback: tcp is either valid, or null on error */ -static void on_connect(void *rp, grpc_endpoint *tcp) { - request *r = rp; - - if (!grpc_client_setup_request_should_continue(r->cs_request, - "on_connect.secure")) { - if (tcp) { - grpc_endpoint_shutdown(tcp); - grpc_endpoint_destroy(tcp); - } - done(r, 0); - return; - } - - if (!tcp) { - if (!maybe_try_next_resolved(r)) { - done(r, 0); - return; - } else { - return; - } +static void connected(void *arg, grpc_endpoint *tcp) { + connector *c = arg; + grpc_iomgr_closure *notify; + if (tcp != NULL) { + grpc_setup_secure_transport(&c->security_connector->base, tcp, + on_secure_transport_setup_done, c); } else { - grpc_setup_secure_transport(&r->setup->security_connector->base, tcp, - on_secure_transport_setup_done, r); + memset(c->result, 0, sizeof(*c->result)); + notify = c->notify; + c->notify = NULL; + grpc_iomgr_add_callback(notify); } } -/* attempt to connect to the next available resolved address */ -static int maybe_try_next_resolved(request *r) { - grpc_resolved_address *addr; - if (!r->resolved) return 0; - if (r->resolved_index == r->resolved->naddrs) return 0; - addr = &r->resolved->addrs[r->resolved_index++]; - grpc_tcp_client_connect( - on_connect, r, grpc_client_setup_get_interested_parties(r->cs_request), - (struct sockaddr *)&addr->addr, addr->len, - grpc_client_setup_request_deadline(r->cs_request)); - return 1; +static void connector_connect(grpc_connector *con, + const grpc_connect_in_args *args, + grpc_connect_out_args *result, + grpc_iomgr_closure *notify) { + connector *c = (connector *)con; + GPR_ASSERT(c->notify == NULL); + GPR_ASSERT(notify->cb); + c->notify = notify; + c->args = *args; + c->result = result; + grpc_tcp_client_connect(connected, c, args->interested_parties, args->addr, + args->addr_len, args->deadline); } -/* callback for when our target address has been resolved */ -static void on_resolved(void *rp, grpc_resolved_addresses *resolved) { - request *r = rp; +static const grpc_connector_vtable connector_vtable = { + connector_ref, connector_unref, connector_connect}; - /* if we're not still the active request, abort */ - if (!grpc_client_setup_request_should_continue(r->cs_request, - "on_resolved.secure")) { - if (resolved) { - grpc_resolved_addresses_destroy(resolved); - } - done(r, 0); - return; - } +typedef struct { + grpc_subchannel_factory base; + gpr_refcount refs; + grpc_mdctx *mdctx; + grpc_channel_args *merge_args; + grpc_channel_security_connector *security_connector; +} subchannel_factory; - if (!resolved) { - done(r, 0); - return; - } else { - r->resolved = resolved; - r->resolved_index = 0; - if (!maybe_try_next_resolved(r)) { - done(r, 0); - } - } +static void subchannel_factory_ref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + gpr_ref(&f->refs); } -static void initiate_setup(void *sp, grpc_client_setup_request *cs_request) { - request *r = gpr_malloc(sizeof(request)); - r->setup = sp; - r->cs_request = cs_request; - r->resolved = NULL; - r->resolved_index = 0; - /* TODO(klempner): Make grpc_resolve_address respect deadline */ - grpc_resolve_address(r->setup->target, "https", on_resolved, r); +static void subchannel_factory_unref(grpc_subchannel_factory *scf) { + subchannel_factory *f = (subchannel_factory *)scf; + if (gpr_unref(&f->refs)) { + GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, + "subchannel_factory"); + grpc_channel_args_destroy(f->merge_args); + grpc_mdctx_unref(f->mdctx); + gpr_free(f); + } } -static void done_setup(void *sp) { - setup *s = sp; - gpr_free((void *)s->target); - grpc_security_connector_unref(&s->security_connector->base); - gpr_free(s); +static grpc_subchannel *subchannel_factory_create_subchannel( + grpc_subchannel_factory *scf, grpc_subchannel_args *args) { + subchannel_factory *f = (subchannel_factory *)scf; + connector *c = gpr_malloc(sizeof(*c)); + grpc_channel_args *final_args = + grpc_channel_args_merge(args->args, f->merge_args); + grpc_subchannel *s; + memset(c, 0, sizeof(*c)); + c->base.vtable = &connector_vtable; + c->security_connector = f->security_connector; + gpr_ref_init(&c->refs, 1); + args->mdctx = f->mdctx; + args->args = final_args; + s = grpc_subchannel_create(&c->base, args); + grpc_connector_unref(&c->base); + grpc_channel_args_destroy(final_args); + return s; } -static grpc_transport_setup_result complete_setup(void *channel_stack, - grpc_transport *transport, - grpc_mdctx *mdctx) { - static grpc_channel_filter const *extra_filters[] = { - &grpc_client_auth_filter, &grpc_http_client_filter}; - return grpc_client_channel_transport_setup_complete( - channel_stack, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters), - mdctx); -} +static const grpc_subchannel_factory_vtable subchannel_factory_vtable = { + subchannel_factory_ref, subchannel_factory_unref, + subchannel_factory_create_subchannel}; /* Create a secure client channel: Asynchronously: - resolve target @@ -210,13 +180,14 @@ static grpc_transport_setup_result complete_setup(void *channel_stack, grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, const char *target, const grpc_channel_args *args) { - setup *s; grpc_channel *channel; grpc_arg connector_arg; grpc_channel_args *args_copy; grpc_channel_args *new_args_from_connector; grpc_channel_security_connector *connector; grpc_mdctx *mdctx; + grpc_resolver *resolver; + subchannel_factory *f; #define MAX_FILTERS 3 const grpc_channel_filter *filters[MAX_FILTERS]; int n = 0; @@ -233,30 +204,41 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, } mdctx = grpc_mdctx_create(); - s = gpr_malloc(sizeof(setup)); connector_arg = grpc_security_connector_to_arg(&connector->base); args_copy = grpc_channel_args_copy_and_add( new_args_from_connector != NULL ? new_args_from_connector : args, - &connector_arg); - filters[n++] = &grpc_client_surface_filter; + &connector_arg, 1); /* TODO(census) if (grpc_channel_args_is_census_enabled(args)) { filters[n++] = &grpc_client_census_filter; } */ filters[n++] = &grpc_client_channel_filter; GPR_ASSERT(n <= MAX_FILTERS); + + f = gpr_malloc(sizeof(*f)); + f->base.vtable = &subchannel_factory_vtable; + gpr_ref_init(&f->refs, 1); + grpc_mdctx_ref(mdctx); + f->mdctx = mdctx; + GRPC_SECURITY_CONNECTOR_REF(&connector->base, "subchannel_factory"); + f->security_connector = connector; + f->merge_args = grpc_channel_args_copy(args_copy); + resolver = grpc_resolver_create(target, &f->base); + if (!resolver) { + return NULL; + } + channel = grpc_channel_create_from_filters(filters, n, args_copy, mdctx, 1); + grpc_client_channel_set_resolver(grpc_channel_get_channel_stack(channel), + resolver); + GRPC_RESOLVER_UNREF(resolver, "create"); + grpc_subchannel_factory_unref(&f->base); + GRPC_SECURITY_CONNECTOR_UNREF(&connector->base, "channel_create"); + grpc_channel_args_destroy(args_copy); if (new_args_from_connector != NULL) { grpc_channel_args_destroy(new_args_from_connector); } - s->target = gpr_strdup(target); - s->setup_callback = complete_setup; - s->setup_user_data = grpc_channel_get_channel_stack(channel); - s->security_connector = connector; - grpc_client_setup_create_and_attach(grpc_channel_get_channel_stack(channel), - args, mdctx, initiate_setup, done_setup, - s); return channel; } diff --git a/src/core/surface/server.c b/src/core/surface/server.c index 13ec5bee94..341ca2942c 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -51,7 +51,7 @@ #include <grpc/support/string_util.h> #include <grpc/support/useful.h> -typedef enum { PENDING_START, ALL_CALLS, CALL_LIST_COUNT } call_list; +typedef enum { PENDING_START, CALL_LIST_COUNT } call_list; typedef struct listener { void *arg; @@ -114,7 +114,7 @@ typedef struct channel_registered_method { struct channel_data { grpc_server *server; - size_t num_calls; + grpc_connectivity_state connectivity_state; grpc_channel *channel; grpc_mdstr *path_key; grpc_mdstr *authority_key; @@ -125,6 +125,7 @@ struct channel_data { gpr_uint32 registered_method_slots; gpr_uint32 registered_method_max_probes; grpc_iomgr_closure finish_destroy_channel_closure; + grpc_iomgr_closure channel_connectivity_changed; }; typedef struct shutdown_tag { @@ -149,7 +150,7 @@ struct grpc_server { before mu_call. This is currently used in shutdown processing (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ gpr_mu mu_global; /* mutex for server and channel state */ - gpr_mu mu_call; /* mutex for call-specific state */ + gpr_mu mu_call; /* mutex for call-specific state */ registered_method *registered_methods; requested_call_array requested_calls; @@ -200,18 +201,83 @@ struct call_data { call_link links[CALL_LIST_COUNT]; }; +typedef struct { + grpc_channel **channels; + size_t num_channels; +} channel_broadcaster; + #define SERVER_FROM_CALL_ELEM(elem) \ (((channel_data *)(elem)->channel_data)->server) static void begin_call(grpc_server *server, call_data *calld, requested_call *rc); static void fail_call(grpc_server *server, requested_call *rc); -static void shutdown_channel(channel_data *chand, int send_goaway, - int send_disconnect); /* Before calling maybe_finish_shutdown, we must hold mu_global and not hold mu_call */ static void maybe_finish_shutdown(grpc_server *server); +/* channel broadcaster */ + +/* assumes server locked */ +static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { + channel_data *c; + size_t count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + count++; + } + cb->num_channels = count; + cb->channels = gpr_malloc(sizeof(*cb->channels) * cb->num_channels); + count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + cb->channels[count++] = c->channel; + GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast"); + } +} + +struct shutdown_cleanup_args { + grpc_iomgr_closure closure; + gpr_slice slice; +}; + +static void shutdown_cleanup(void *arg, int iomgr_status_ignored) { + struct shutdown_cleanup_args *a = arg; + gpr_slice_unref(a->slice); + gpr_free(a); +} + +static void send_shutdown(grpc_channel *channel, int send_goaway, + int send_disconnect) { + grpc_transport_op op; + struct shutdown_cleanup_args *sc; + grpc_channel_element *elem; + + memset(&op, 0, sizeof(op)); + op.send_goaway = send_goaway; + sc = gpr_malloc(sizeof(*sc)); + sc->slice = gpr_slice_from_copied_string("Server shutdown"); + op.goaway_message = &sc->slice; + op.goaway_status = GRPC_STATUS_OK; + op.disconnect = send_disconnect; + grpc_iomgr_closure_init(&sc->closure, shutdown_cleanup, sc); + op.on_consumed = &sc->closure; + + elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + elem->filter->start_transport_op(elem, &op); +} + +static void channel_broadcaster_shutdown(channel_broadcaster *cb, + int send_goaway, int force_disconnect) { + size_t i; + + for (i = 0; i < cb->num_channels; i++) { + send_shutdown(cb->channels[i], send_goaway, force_disconnect); + GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast"); + } + gpr_free(cb->channels); +} + +/* call list */ + static int call_list_join(call_data **root, call_data *call, call_list list) { GPR_ASSERT(!call->root[list]); call->root[list] = root; @@ -416,15 +482,6 @@ static void maybe_finish_shutdown(grpc_server *server) { return; } - gpr_mu_lock(&server->mu_call); - if (server->lists[ALL_CALLS] != NULL) { - gpr_log(GPR_DEBUG, - "Waiting for all calls to finish before destroying server"); - gpr_mu_unlock(&server->mu_call); - return; - } - gpr_mu_unlock(&server->mu_call); - if (server->root_channel_data.next != &server->root_channel_data) { gpr_log(GPR_DEBUG, "Waiting for all channels to close before destroying server"); @@ -456,19 +513,10 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { return md; } -static void decrement_call_count(channel_data *chand) { - chand->num_calls--; - if (0 == chand->num_calls && chand->server->shutdown) { - shutdown_channel(chand, 0, 1); - } - maybe_finish_shutdown(chand->server); -} - static void server_on_recv(void *ptr, int success) { grpc_call_element *elem = ptr; call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; - int remove_res; if (success && !calld->got_initial_metadata) { size_t i; @@ -513,20 +561,15 @@ static void server_on_recv(void *ptr, int success) { grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); grpc_iomgr_add_callback(&calld->kill_zombie_closure); } - remove_res = call_list_remove(calld, ALL_CALLS); gpr_mu_unlock(&chand->server->mu_call); - gpr_mu_lock(&chand->server->mu_global); - if (remove_res) { - decrement_call_count(chand); - } - gpr_mu_unlock(&chand->server->mu_global); break; } calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success); } -static void server_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { +static void server_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { call_data *calld = elem->call_data; if (op->recv_ops) { @@ -538,92 +581,43 @@ static void server_mutate_op(grpc_call_element *elem, grpc_transport_op *op) { } } -static void server_start_transport_op(grpc_call_element *elem, - grpc_transport_op *op) { +static void server_start_transport_stream_op(grpc_call_element *elem, + grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); server_mutate_op(elem, op); grpc_call_next_op(elem, op); } -static void channel_op(grpc_channel_element *elem, - grpc_channel_element *from_elem, grpc_channel_op *op) { - channel_data *chand = elem->channel_data; - grpc_server *server = chand->server; - - switch (op->type) { - case GRPC_ACCEPT_CALL: - /* create a call */ - grpc_call_create(chand->channel, NULL, - op->data.accept_call.transport_server_data, NULL, 0, - gpr_inf_future); - break; - case GRPC_TRANSPORT_CLOSED: - /* if the transport is closed for a server channel, we destroy the - channel */ - gpr_mu_lock(&server->mu_global); - server_ref(server); - destroy_channel(chand); - gpr_mu_unlock(&server->mu_global); - server_unref(server); - break; - case GRPC_TRANSPORT_GOAWAY: - gpr_slice_unref(op->data.goaway.message); - break; - default: - GPR_ASSERT(op->dir == GRPC_CALL_DOWN); - grpc_channel_next_op(elem, op); - break; - } +static void accept_stream(void *cd, grpc_transport *transport, + const void *transport_server_data) { + channel_data *chand = cd; + /* create a call */ + grpc_call_create(chand->channel, NULL, transport_server_data, NULL, 0, + gpr_inf_future); } -typedef struct { - channel_data *chand; - int send_goaway; - int send_disconnect; - grpc_iomgr_closure finish_shutdown_channel_closure; -} shutdown_channel_args; - -static void finish_shutdown_channel(void *p, int success) { - shutdown_channel_args *sca = p; - grpc_channel_op op; - - if (sca->send_goaway) { - op.type = GRPC_CHANNEL_GOAWAY; - op.dir = GRPC_CALL_DOWN; - op.data.goaway.status = GRPC_STATUS_OK; - op.data.goaway.message = gpr_slice_from_copied_string("Server shutdown"); - channel_op(grpc_channel_stack_element( - grpc_channel_get_channel_stack(sca->chand->channel), 0), - NULL, &op); - } - if (sca->send_disconnect) { - op.type = GRPC_CHANNEL_DISCONNECT; - op.dir = GRPC_CALL_DOWN; - channel_op(grpc_channel_stack_element( - grpc_channel_get_channel_stack(sca->chand->channel), 0), - NULL, &op); +static void channel_connectivity_changed(void *cd, int iomgr_status_ignored) { + channel_data *chand = cd; + grpc_server *server = chand->server; + if (chand->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) { + grpc_transport_op op; + memset(&op, 0, sizeof(op)); + op.on_connectivity_state_change = &chand->channel_connectivity_changed, + op.connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(grpc_channel_stack_element( + grpc_channel_get_channel_stack(chand->channel), 0), + &op); + } else { + gpr_mu_lock(&server->mu_global); + destroy_channel(chand); + gpr_mu_unlock(&server->mu_global); + GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "connectivity"); } - GRPC_CHANNEL_INTERNAL_UNREF(sca->chand->channel, "shutdown"); - - gpr_free(sca); -} - -static void shutdown_channel(channel_data *chand, int send_goaway, - int send_disconnect) { - shutdown_channel_args *sca; - GRPC_CHANNEL_INTERNAL_REF(chand->channel, "shutdown"); - sca = gpr_malloc(sizeof(shutdown_channel_args)); - sca->chand = chand; - sca->send_goaway = send_goaway; - sca->send_disconnect = send_disconnect; - sca->finish_shutdown_channel_closure.cb = finish_shutdown_channel; - sca->finish_shutdown_channel_closure.cb_arg = sca; - grpc_iomgr_add_callback(&sca->finish_shutdown_channel_closure); } static void init_call_elem(grpc_call_element *elem, const void *server_transport_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; memset(calld, 0, sizeof(call_data)); @@ -632,14 +626,6 @@ static void init_call_elem(grpc_call_element *elem, grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem); - gpr_mu_lock(&chand->server->mu_call); - call_list_join(&chand->server->lists[ALL_CALLS], calld, ALL_CALLS); - gpr_mu_unlock(&chand->server->mu_call); - - gpr_mu_lock(&chand->server->mu_global); - chand->num_calls++; - gpr_mu_unlock(&chand->server->mu_global); - server_ref(chand->server); if (initial_op) server_mutate_op(elem, initial_op); @@ -648,19 +634,13 @@ static void init_call_elem(grpc_call_element *elem, static void destroy_call_elem(grpc_call_element *elem) { channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; - int removed[CALL_LIST_COUNT]; size_t i; gpr_mu_lock(&chand->server->mu_call); for (i = 0; i < CALL_LIST_COUNT; i++) { - removed[i] = call_list_remove(elem->call_data, i); + call_list_remove(elem->call_data, i); } gpr_mu_unlock(&chand->server->mu_call); - if (removed[ALL_CALLS]) { - gpr_mu_lock(&chand->server->mu_global); - decrement_call_count(chand); - gpr_mu_unlock(&chand->server->mu_global); - } if (calld->host) { grpc_mdstr_unref(calld->host); @@ -672,7 +652,7 @@ static void destroy_call_elem(grpc_call_element *elem) { server_unref(chand->server); } -static void init_channel_elem(grpc_channel_element *elem, +static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master, const grpc_channel_args *args, grpc_mdctx *metadata_context, int is_first, int is_last) { @@ -680,12 +660,14 @@ static void init_channel_elem(grpc_channel_element *elem, GPR_ASSERT(is_first); GPR_ASSERT(!is_last); chand->server = NULL; - chand->num_calls = 0; chand->channel = NULL; chand->path_key = grpc_mdstr_from_string(metadata_context, ":path"); chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority"); chand->next = chand->prev = chand; chand->registered_methods = NULL; + chand->connectivity_state = GRPC_CHANNEL_IDLE; + grpc_iomgr_closure_init(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand); } static void destroy_channel_elem(grpc_channel_element *elem) { @@ -716,8 +698,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) { } static const grpc_channel_filter server_surface_filter = { - server_start_transport_op, - channel_op, + server_start_transport_stream_op, + grpc_channel_next_op, sizeof(call_data), init_call_elem, destroy_call_elem, @@ -831,10 +813,10 @@ void grpc_server_start(grpc_server *server) { } } -grpc_transport_setup_result grpc_server_setup_transport( - grpc_server *s, grpc_transport *transport, - grpc_channel_filter const **extra_filters, size_t num_extra_filters, - grpc_mdctx *mdctx, const grpc_channel_args *args) { +void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport, + grpc_channel_filter const **extra_filters, + size_t num_extra_filters, grpc_mdctx *mdctx, + const grpc_channel_args *args) { size_t num_filters = s->channel_filter_count + num_extra_filters + 1; grpc_channel_filter const **filters = gpr_malloc(sizeof(grpc_channel_filter *) * num_filters); @@ -851,7 +833,7 @@ grpc_transport_setup_result grpc_server_setup_transport( gpr_uint32 slots; gpr_uint32 probes; gpr_uint32 max_probes = 0; - grpc_transport_setup_result result; + grpc_transport_op op; for (i = 0; i < s->channel_filter_count; i++) { filters[i] = s->channel_filters[i]; @@ -862,7 +844,9 @@ grpc_transport_setup_result grpc_server_setup_transport( filters[i] = &grpc_connected_channel_filter; for (i = 0; i < s->cq_count; i++) { - grpc_transport_add_to_pollset(transport, grpc_cq_pollset(s->cqs[i])); + memset(&op, 0, sizeof(op)); + op.bind_pollset = grpc_cq_pollset(s->cqs[i]); + grpc_transport_perform_op(transport, &op); } channel = @@ -903,8 +887,8 @@ grpc_transport_setup_result grpc_server_setup_transport( chand->registered_method_max_probes = max_probes; } - result = grpc_connected_channel_bind_transport( - grpc_channel_get_channel_stack(channel), transport); + grpc_connected_channel_bind_transport(grpc_channel_get_channel_stack(channel), + transport); gpr_mu_lock(&s->mu_global); chand->next = &s->root_channel_data; @@ -914,17 +898,23 @@ grpc_transport_setup_result grpc_server_setup_transport( gpr_free(filters); - return result; + GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); + memset(&op, 0, sizeof(op)); + op.set_accept_stream = accept_stream; + op.set_accept_stream_user_data = chand; + op.on_connectivity_state_change = &chand->channel_connectivity_changed; + op.connectivity_state = &chand->connectivity_state; + grpc_transport_perform_op(transport, &op); } void grpc_server_shutdown_and_notify(grpc_server *server, grpc_completion_queue *cq, void *tag) { listener *l; requested_call_array requested_calls; - channel_data *c; size_t i; registered_method *rm; shutdown_tag *sdt; + channel_broadcaster broadcaster; /* lock, and gather up some stuff to do */ gpr_mu_lock(&server->mu_global); @@ -940,10 +930,7 @@ void grpc_server_shutdown_and_notify(grpc_server *server, return; } - for (c = server->root_channel_data.next; c != &server->root_channel_data; - c = c->next) { - shutdown_channel(c, 1, c->num_calls == 0); - } + channel_broadcaster_init(server, &broadcaster); /* collect all unregistered then registered calls */ gpr_mu_lock(&server->mu_call); @@ -981,6 +968,8 @@ void grpc_server_shutdown_and_notify(grpc_server *server, for (l = server->listeners; l; l = l->next) { l->destroy(server, l->arg); } + + channel_broadcaster_shutdown(&broadcaster, 1, 0); } void grpc_server_listener_destroy_done(void *s) { @@ -992,47 +981,13 @@ void grpc_server_listener_destroy_done(void *s) { } void grpc_server_cancel_all_calls(grpc_server *server) { - call_data *calld; - grpc_call **calls; - size_t call_count; - size_t call_capacity; - int is_first = 1; - size_t i; - - gpr_mu_lock(&server->mu_call); - - GPR_ASSERT(server->shutdown); - - if (!server->lists[ALL_CALLS]) { - gpr_mu_unlock(&server->mu_call); - return; - } + channel_broadcaster broadcaster; - call_capacity = 8; - call_count = 0; - calls = gpr_malloc(sizeof(grpc_call *) * call_capacity); - - for (calld = server->lists[ALL_CALLS]; - calld != server->lists[ALL_CALLS] || is_first; - calld = calld->links[ALL_CALLS].next) { - if (call_count == call_capacity) { - call_capacity *= 2; - calls = gpr_realloc(calls, sizeof(grpc_call *) * call_capacity); - } - calls[call_count++] = calld->call; - GRPC_CALL_INTERNAL_REF(calld->call, "cancel_all"); - is_first = 0; - } - - gpr_mu_unlock(&server->mu_call); - - for (i = 0; i < call_count; i++) { - grpc_call_cancel_with_status(calls[i], GRPC_STATUS_UNAVAILABLE, - "Unavailable"); - GRPC_CALL_INTERNAL_UNREF(calls[i], "cancel_all", 1); - } + gpr_mu_lock(&server->mu_global); + channel_broadcaster_init(server, &broadcaster); + gpr_mu_unlock(&server->mu_global); - gpr_free(calls); + channel_broadcaster_shutdown(&broadcaster, 0, 1); } void grpc_server_destroy(grpc_server *server) { @@ -1181,6 +1136,8 @@ static void begin_call(grpc_server *server, call_data *calld, calld->cq_new = rc->cq_for_notification; switch (rc->type) { case BATCH_CALL: + GPR_ASSERT(calld->host != NULL); + GPR_ASSERT(calld->path != NULL); cpstr(&rc->data.batch.details->host, &rc->data.batch.details->host_capacity, calld->host); cpstr(&rc->data.batch.details->method, diff --git a/src/core/surface/server.h b/src/core/surface/server.h index 91a1a2a7f6..2899c6dea3 100644 --- a/src/core/surface/server.h +++ b/src/core/surface/server.h @@ -55,10 +55,10 @@ void grpc_server_listener_destroy_done(void *server); /* Setup a transport - creates a channel stack, binds the transport to the server */ -grpc_transport_setup_result grpc_server_setup_transport( - grpc_server *server, grpc_transport *transport, - grpc_channel_filter const **extra_filters, size_t num_extra_filters, - grpc_mdctx *mdctx, const grpc_channel_args *args); +void grpc_server_setup_transport(grpc_server *server, grpc_transport *transport, + grpc_channel_filter const **extra_filters, + size_t num_extra_filters, grpc_mdctx *mdctx, + const grpc_channel_args *args); const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server); diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c index 7e49a531df..78c53466b3 100644 --- a/src/core/surface/server_chttp2.c +++ b/src/core/surface/server_chttp2.c @@ -42,14 +42,13 @@ #include <grpc/support/log.h> #include <grpc/support/useful.h> -static grpc_transport_setup_result setup_transport(void *server, - grpc_transport *transport, - grpc_mdctx *mdctx) { +static void setup_transport(void *server, grpc_transport *transport, + grpc_mdctx *mdctx) { static grpc_channel_filter const *extra_filters[] = { &grpc_http_server_filter}; - return grpc_server_setup_transport(server, transport, extra_filters, - GPR_ARRAY_SIZE(extra_filters), mdctx, - grpc_server_get_channel_args(server)); + grpc_server_setup_transport(server, transport, extra_filters, + GPR_ARRAY_SIZE(extra_filters), mdctx, + grpc_server_get_channel_args(server)); } static void new_transport(void *server, grpc_endpoint *tcp) { @@ -60,9 +59,11 @@ static void new_transport(void *server, grpc_endpoint *tcp) { * (as in server_secure_chttp2.c) needs to add synchronization to avoid this * case. */ - grpc_create_chttp2_transport(setup_transport, server, - grpc_server_get_channel_args(server), tcp, NULL, - 0, grpc_mdctx_create(), 0); + grpc_mdctx *mdctx = grpc_mdctx_create(); + grpc_transport *transport = grpc_create_chttp2_transport( + grpc_server_get_channel_args(server), tcp, mdctx, 0); + setup_transport(server, transport, mdctx); + grpc_chttp2_transport_start_reading(transport, NULL, 0); } /* Server callback: start listening on our ports */ diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index b8ab664db5..c729e0f22f 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -1329,12 +1329,9 @@ static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, /* PUBLIC INTERFACE */ static void on_header_not_set(void *user_data, grpc_mdelem *md) { - char *keyhex = - gpr_hexdump(grpc_mdstr_as_c_string(md->key), - GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT); + char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); char *valuehex = - gpr_hexdump(grpc_mdstr_as_c_string(md->value), - GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT); + gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex, valuehex); gpr_free(keyhex); diff --git a/src/core/transport/chttp2/incoming_metadata.c b/src/core/transport/chttp2/incoming_metadata.c index a4b7174329..68e0912b9c 100644 --- a/src/core/transport/chttp2/incoming_metadata.c +++ b/src/core/transport/chttp2/incoming_metadata.c @@ -124,6 +124,7 @@ void grpc_incoming_metadata_buffer_move_to_referencing_sopb( sopb->ops[i].data.metadata.list.tail = (void *)(delta + (gpr_intptr)sopb->ops[i].data.metadata.list.tail); } + src->count = 0; } void grpc_chttp2_incoming_metadata_buffer_postprocess_sopb_and_begin_live_op( diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h index 02c94744ee..bdd4b432eb 100644 --- a/src/core/transport/chttp2/internal.h +++ b/src/core/transport/chttp2/internal.h @@ -34,7 +34,6 @@ #ifndef GRPC_INTERNAL_CORE_CHTTP2_INTERNAL_H #define GRPC_INTERNAL_CORE_CHTTP2_INTERNAL_H -#include "src/core/transport/transport_impl.h" #include "src/core/iomgr/endpoint.h" #include "src/core/transport/chttp2/frame.h" #include "src/core/transport/chttp2/frame_data.h" @@ -47,6 +46,8 @@ #include "src/core/transport/chttp2/incoming_metadata.h" #include "src/core/transport/chttp2/stream_encoder.h" #include "src/core/transport/chttp2/stream_map.h" +#include "src/core/transport/connectivity_state.h" +#include "src/core/transport/transport_impl.h" typedef struct grpc_chttp2_transport grpc_chttp2_transport; typedef struct grpc_chttp2_stream grpc_chttp2_stream; @@ -62,6 +63,7 @@ typedef enum { GRPC_CHTTP2_LIST_WRITABLE_WINDOW_UPDATE, GRPC_CHTTP2_LIST_PARSING_SEEN, GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_PARSING, + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING, GRPC_CHTTP2_LIST_INCOMING_WINDOW_UPDATED, /** streams that are waiting to start because there are too many concurrent streams on the connection */ @@ -134,12 +136,6 @@ typedef struct { grpc_chttp2_stream *prev; } grpc_chttp2_stream_link; -typedef enum { - GRPC_CHTTP2_ERROR_STATE_NONE, - GRPC_CHTTP2_ERROR_STATE_SEEN, - GRPC_CHTTP2_ERROR_STATE_NOTIFIED -} grpc_chttp2_error_state; - /* We keep several sets of connection wide parameters */ typedef enum { /* The settings our peer has asked for (and we have acked) */ @@ -165,7 +161,8 @@ typedef struct { /** data to write next write */ gpr_slice_buffer qbuf; /** queued callbacks */ - grpc_iomgr_closure *pending_closures; + grpc_iomgr_closure *pending_closures_head; + grpc_iomgr_closure *pending_closures_tail; /** window available for us to send to peer */ gpr_uint32 outgoing_window; @@ -174,6 +171,11 @@ typedef struct { /** how much window would we like to have for incoming_window */ gpr_uint32 connection_window_target; + /** have we seen a goaway */ + gpr_uint8 seen_goaway; + /** have we sent a goaway */ + gpr_uint8 sent_goaway; + /** is this transport a client? */ gpr_uint8 is_client; /** are the local settings dirty and need to be sent? */ @@ -185,10 +187,6 @@ typedef struct { /** settings values */ gpr_uint32 settings[GRPC_NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS]; - /** has there been a connection level error, and have we notified - anyone about it? */ - grpc_chttp2_error_state error_state; - /** what is the next stream id to be allocated by this peer? copied to next_stream_id in parsing when parsing commences */ gpr_uint32 next_stream_id; @@ -204,13 +202,6 @@ typedef struct { /** concurrent stream count: updated when not parsing, so this is a strict over-estimation on the client */ gpr_uint32 concurrent_stream_count; - - /** is there a goaway available? (boolean) */ - grpc_chttp2_error_state goaway_state; - /** what is the debug text of the goaway? */ - gpr_slice goaway_text; - /** what is the status code of the goaway? */ - grpc_status_code goaway_error; } grpc_chttp2_transport_global; typedef struct { @@ -343,14 +334,13 @@ struct grpc_chttp2_transport { grpc_chttp2_stream **accepting_stream; struct { - /** is a thread currently performing channel callbacks */ - gpr_uint8 executing; - /** transport channel-level callback */ - const grpc_transport_callbacks *cb; - /** user data for cb calls */ - void *cb_user_data; - /** closure for notifying transport closure */ - grpc_iomgr_closure notify_closed; + /* accept stream callback */ + void (*accept_stream)(void *user_data, grpc_transport *transport, + const void *server_data); + void *accept_stream_user_data; + + /** connectivity tracking */ + grpc_connectivity_state_tracker state_tracker; } channel_callback; }; @@ -539,6 +529,13 @@ int grpc_chttp2_list_pop_closed_waiting_for_parsing( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global **stream_global); +void grpc_chttp2_list_add_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + void grpc_chttp2_list_add_read_write_state_changed( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global); @@ -562,8 +559,10 @@ void grpc_chttp2_add_incoming_goaway( void grpc_chttp2_register_stream(grpc_chttp2_transport *t, grpc_chttp2_stream *s); -void grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s); +/* returns 1 if this is the last stream, 0 otherwise */ +int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) GRPC_MUST_USE_RESULT; +int grpc_chttp2_has_streams(grpc_chttp2_transport *t); void grpc_chttp2_for_all_streams( grpc_chttp2_transport_global *transport_global, void *user_data, void (*cb)(grpc_chttp2_transport_global *transport_global, void *user_data, diff --git a/src/core/transport/chttp2/stream_lists.c b/src/core/transport/chttp2/stream_lists.c index c6ba12fca8..4fea058c19 100644 --- a/src/core/transport/chttp2/stream_lists.c +++ b/src/core/transport/chttp2/stream_lists.c @@ -222,7 +222,9 @@ int grpc_chttp2_list_pop_writable_window_update_stream( void grpc_chttp2_list_remove_writable_window_update_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global) { - stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global), STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE_WINDOW_UPDATE); + stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_WRITABLE_WINDOW_UPDATE); } void grpc_chttp2_list_add_parsing_seen_stream( @@ -282,6 +284,24 @@ int grpc_chttp2_list_pop_closed_waiting_for_parsing( return r; } +void grpc_chttp2_list_add_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING); +} + +int grpc_chttp2_list_pop_cancelled_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_CANCELLED_WAITING_FOR_WRITING); + *stream_global = &stream->global; + return r; +} + void grpc_chttp2_list_add_incoming_window_updated( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global) { @@ -334,9 +354,14 @@ void grpc_chttp2_register_stream(grpc_chttp2_transport *t, stream_list_add_tail(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); } -void grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, +int grpc_chttp2_unregister_stream(grpc_chttp2_transport *t, grpc_chttp2_stream *s) { - stream_list_remove(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); + return stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS); +} + +int grpc_chttp2_has_streams(grpc_chttp2_transport *t) { + return !stream_list_empty(t, GRPC_CHTTP2_LIST_ALL_STREAMS); } void grpc_chttp2_for_all_streams( diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c index fdcc300099..a78654334e 100644 --- a/src/core/transport/chttp2/writing.c +++ b/src/core/transport/chttp2/writing.c @@ -97,12 +97,8 @@ int grpc_chttp2_unlocking_check_writes( grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing); } - /* we should either exhaust window or have no ops left, but not both */ - if (stream_global->outgoing_sopb->nops == 0) { - stream_global->outgoing_sopb = NULL; - grpc_chttp2_schedule_closure(transport_global, - stream_global->send_done_closure, 1); - } else if (stream_global->outgoing_window > 0) { + if (stream_global->outgoing_window > 0 && + stream_global->outgoing_sopb->nops != 0) { grpc_chttp2_list_add_writable_stream(transport_global, stream_global); } } @@ -201,6 +197,11 @@ void grpc_chttp2_cleanup_writing( while (grpc_chttp2_list_pop_written_stream( transport_global, transport_writing, &stream_global, &stream_writing)) { + if (stream_global->outgoing_sopb->nops == 0) { + stream_global->outgoing_sopb = NULL; + grpc_chttp2_schedule_closure(transport_global, + stream_global->send_done_closure, 1); + } if (stream_writing->send_closed != GRPC_DONT_SEND_CLOSED) { stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; if (!transport_global->is_client) { diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 7ecc49ed60..47d0c548cb 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -37,18 +37,19 @@ #include <stdio.h> #include <string.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/slice_buffer.h> +#include <grpc/support/string_util.h> +#include <grpc/support/useful.h> + #include "src/core/profiling/timers.h" #include "src/core/support/string.h" #include "src/core/transport/chttp2/http2_errors.h" +#include "src/core/transport/chttp2/internal.h" #include "src/core/transport/chttp2/status_conversion.h" #include "src/core/transport/chttp2/timeout_encoding.h" -#include "src/core/transport/chttp2/internal.h" #include "src/core/transport/transport_impl.h" -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/slice_buffer.h> -#include <grpc/support/string_util.h> -#include <grpc/support/useful.h> #define DEFAULT_WINDOW 65535 #define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024) @@ -79,13 +80,11 @@ static const grpc_transport_vtable vtable; static void lock(grpc_chttp2_transport *t); static void unlock(grpc_chttp2_transport *t); -static void unlock_check_channel_callbacks(grpc_chttp2_transport *t); static void unlock_check_read_write_state(grpc_chttp2_transport *t); /* forward declarations of various callbacks that we'll build closures around */ static void writing_action(void *t, int iomgr_success_ignored); static void reading_action(void *t, int iomgr_success_ignored); -static void notify_closed(void *t, int iomgr_success_ignored); /** Set a transport level setting, and push it to our peer */ static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, @@ -99,9 +98,9 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices, static void drop_connection(grpc_chttp2_transport *t); /** Perform a transport_op */ -static void perform_op_locked(grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, - grpc_transport_op *op); +static void perform_stream_op_locked( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op); /** Cancel a stream: coming from the transport API */ static void cancel_from_api(grpc_chttp2_transport_global *transport_global, @@ -116,6 +115,10 @@ static void add_to_pollset_locked(grpc_chttp2_transport *t, static void maybe_start_some_streams( grpc_chttp2_transport_global *transport_global); +static void connectivity_state_set( + grpc_chttp2_transport_global *transport_global, + grpc_connectivity_state state); + /* * CONSTRUCTION/DESTRUCTION/REFCOUNTING */ @@ -148,6 +151,7 @@ static void destruct_transport(grpc_chttp2_transport *t) { grpc_chttp2_stream_map_destroy(&t->parsing_stream_map); grpc_chttp2_stream_map_destroy(&t->new_stream_map); + grpc_connectivity_state_destroy(&t->channel_callback.state_tracker); gpr_mu_unlock(&t->mu); gpr_mu_destroy(&t->mu); @@ -196,13 +200,11 @@ static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } #endif static void init_transport(grpc_chttp2_transport *t, - grpc_transport_setup_callback setup, void *arg, const grpc_channel_args *channel_args, - grpc_endpoint *ep, gpr_slice *slices, size_t nslices, - grpc_mdctx *mdctx, int is_client) { + grpc_endpoint *ep, grpc_mdctx *mdctx, + int is_client) { size_t i; int j; - grpc_transport_setup_result sr; GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); @@ -217,7 +219,6 @@ static void init_transport(grpc_chttp2_transport *t, grpc_mdctx_ref(mdctx); t->metadata_context = mdctx; t->endpoint_reading = 1; - t->global.error_state = GRPC_CHTTP2_ERROR_STATE_NONE; t->global.next_stream_id = is_client ? 1 : 2; t->global.is_client = is_client; t->global.outgoing_window = DEFAULT_WINDOW; @@ -231,6 +232,8 @@ static void init_transport(grpc_chttp2_transport *t, t->parsing.deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; t->writing.is_client = is_client; + grpc_connectivity_state_init(&t->channel_callback.state_tracker, + GRPC_CHANNEL_READY); gpr_slice_buffer_init(&t->global.qbuf); @@ -243,7 +246,6 @@ static void init_transport(grpc_chttp2_transport *t, grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser); grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context); - grpc_iomgr_closure_init(&t->channel_callback.notify_closed, notify_closed, t); if (is_client) { gpr_slice_buffer_add( &t->global.qbuf, @@ -309,24 +311,6 @@ static void init_transport(grpc_chttp2_transport *t, } } } - - gpr_mu_lock(&t->mu); - t->channel_callback.executing = 1; - REF_TRANSPORT(t, "init"); /* matches unref at end of this function */ - gpr_mu_unlock(&t->mu); - - sr = setup(arg, &t->base, t->metadata_context); - - lock(t); - t->channel_callback.cb = sr.callbacks; - t->channel_callback.cb_user_data = sr.user_data; - t->channel_callback.executing = 0; - unlock(t); - - REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ - recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); - - UNREF_TRANSPORT(t, "init"); } static void destroy_transport(grpc_transport *gt) { @@ -343,31 +327,16 @@ static void destroy_transport(grpc_transport *gt) { static void close_transport_locked(grpc_chttp2_transport *t) { if (!t->closed) { t->closed = 1; + connectivity_state_set(&t->global, GRPC_CHANNEL_FATAL_FAILURE); if (t->ep) { grpc_endpoint_shutdown(t->ep); } } } -static void close_transport(grpc_transport *gt) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - gpr_mu_lock(&t->mu); - close_transport_locked(t); - gpr_mu_unlock(&t->mu); -} - -static void goaway(grpc_transport *gt, grpc_status_code status, - gpr_slice debug_data) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - lock(t); - grpc_chttp2_goaway_append(t->global.last_incoming_stream_id, - grpc_chttp2_grpc_status_to_http2_error(status), - debug_data, &t->global.qbuf); - unlock(t); -} - static int init_stream(grpc_transport *gt, grpc_stream *gs, - const void *server_data, grpc_transport_op *initial_op) { + const void *server_data, + grpc_transport_stream_op *initial_op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; @@ -397,7 +366,7 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs, s->global.in_stream_map = 1; } - if (initial_op) perform_op_locked(&t->global, &s->global, initial_op); + if (initial_op) perform_stream_op_locked(&t->global, &s->global, initial_op); unlock(t); return 0; @@ -413,7 +382,9 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) { GPR_ASSERT(s->global.published_state == GRPC_STREAM_CLOSED || s->global.id == 0); GPR_ASSERT(!s->global.in_stream_map); - grpc_chttp2_unregister_stream(t, s); + if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { + close_transport_locked(t); + } if (!t->parsing_active && s->global.id) { GPR_ASSERT(grpc_chttp2_stream_map_find(&t->parsing_stream_map, s->global.id) == NULL); @@ -455,8 +426,8 @@ grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( grpc_chttp2_transport *t = TRANSPORT_FROM_PARSING(transport_parsing); GPR_ASSERT(t->accepting_stream == NULL); t->accepting_stream = &accepting; - t->channel_callback.cb->accept_stream(t->channel_callback.cb_user_data, - &t->base, (void *)(gpr_uintptr)id); + t->channel_callback.accept_stream(t->channel_callback.accept_stream_user_data, + &t->base, (void *)(gpr_uintptr)id); t->accepting_stream = NULL; return &accepting->parsing; } @@ -477,17 +448,16 @@ static void unlock(grpc_chttp2_transport *t) { grpc_iomgr_closure *run_closures; unlock_check_read_write_state(t); - if (!t->writing_active && t->global.error_state == GRPC_CHTTP2_ERROR_STATE_NONE && + if (!t->writing_active && !t->closed && grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { t->writing_active = 1; REF_TRANSPORT(t, "writing"); grpc_chttp2_schedule_closure(&t->global, &t->writing_action, 1); } - /* unlock_check_parser(t); */ - unlock_check_channel_callbacks(t); - run_closures = t->global.pending_closures; - t->global.pending_closures = NULL; + run_closures = t->global.pending_closures_head; + t->global.pending_closures_head = NULL; + t->global.pending_closures_tail = NULL; gpr_mu_unlock(&t->mu); @@ -553,15 +523,12 @@ static void writing_action(void *gt, int iomgr_success_ignored) { void grpc_chttp2_add_incoming_goaway( grpc_chttp2_transport_global *transport_global, gpr_uint32 goaway_error, gpr_slice goaway_text) { - char *msg = gpr_hexdump((char*)GPR_SLICE_START_PTR(goaway_text), GPR_SLICE_LENGTH(goaway_text), GPR_HEXDUMP_PLAINTEXT); + char *msg = gpr_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg); gpr_free(msg); - if (transport_global->goaway_state == GRPC_CHTTP2_ERROR_STATE_NONE) { - transport_global->goaway_state = GRPC_CHTTP2_ERROR_STATE_SEEN; - transport_global->goaway_text = goaway_text; - transport_global->goaway_error = goaway_error; - } else { - gpr_slice_unref(goaway_text); - } + gpr_slice_unref(goaway_text); + transport_global->seen_goaway = 1; + connectivity_state_set(transport_global, GRPC_CHANNEL_FATAL_FAILURE); } static void maybe_start_some_streams( @@ -586,9 +553,7 @@ static void maybe_start_some_streams( transport_global->next_stream_id += 2; if (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID) { - grpc_chttp2_add_incoming_goaway( - transport_global, GRPC_CHTTP2_NO_ERROR, - gpr_slice_from_copied_string("Exceeded sequence number limit")); + connectivity_state_set(transport_global, GRPC_CHANNEL_TRANSIENT_FAILURE); } stream_global->outgoing_window = @@ -614,9 +579,9 @@ static void maybe_start_some_streams( } } -static void perform_op_locked(grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, - grpc_transport_op *op) { +static void perform_stream_op_locked( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { if (op->cancel_with_status != GRPC_STATUS_OK) { cancel_from_api(transport_global, stream_global, op->cancel_with_status); } @@ -673,21 +638,19 @@ static void perform_op_locked(grpc_chttp2_transport_global *transport_global, } } -static void perform_op(grpc_transport *gt, grpc_stream *gs, - grpc_transport_op *op) { +static void perform_stream_op(grpc_transport *gt, grpc_stream *gs, + grpc_transport_stream_op *op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; lock(t); - perform_op_locked(&t->global, &s->global, op); + perform_stream_op_locked(&t->global, &s->global, op); unlock(t); } -static void send_ping(grpc_transport *gt, grpc_iomgr_closure *on_recv) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; +static void send_ping_locked(grpc_chttp2_transport *t, + grpc_iomgr_closure *on_recv) { grpc_chttp2_outstanding_ping *p = gpr_malloc(sizeof(*p)); - - lock(t); p->next = &t->global.pings; p->prev = p->next->prev; p->prev->next = p->next->prev = p; @@ -701,6 +664,52 @@ static void send_ping(grpc_transport *gt, grpc_iomgr_closure *on_recv) { p->id[7] = t->global.ping_counter & 0xff; p->on_recv = on_recv; gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); +} + +static void perform_transport_op(grpc_transport *gt, grpc_transport_op *op) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + + lock(t); + + if (op->on_consumed) { + grpc_chttp2_schedule_closure(&t->global, op->on_consumed, 1); + } + + if (op->on_connectivity_state_change) { + grpc_connectivity_state_notify_on_state_change( + &t->channel_callback.state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + } + + if (op->send_goaway) { + t->global.sent_goaway = 1; + grpc_chttp2_goaway_append( + t->global.last_incoming_stream_id, + grpc_chttp2_grpc_status_to_http2_error(op->goaway_status), + gpr_slice_ref(*op->goaway_message), &t->global.qbuf); + if (!grpc_chttp2_has_streams(t)) { + close_transport_locked(t); + } + } + + if (op->set_accept_stream != NULL) { + t->channel_callback.accept_stream = op->set_accept_stream; + t->channel_callback.accept_stream_user_data = + op->set_accept_stream_user_data; + } + + if (op->bind_pollset) { + add_to_pollset_locked(t, op->bind_pollset); + } + + if (op->send_ping) { + send_ping_locked(t, op->send_ping); + } + + if (op->disconnect) { + close_transport_locked(t); + } + unlock(t); } @@ -729,10 +738,12 @@ static void remove_stream(grpc_chttp2_transport *t, gpr_uint32 id) { t->parsing.incoming_stream = NULL; grpc_chttp2_parsing_become_skip_parser(&t->parsing); } + if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { + close_transport_locked(t); + } - new_stream_count = - grpc_chttp2_stream_map_size(&t->parsing_stream_map) + - grpc_chttp2_stream_map_size(&t->new_stream_map); + new_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map) + + grpc_chttp2_stream_map_size(&t->new_stream_map); if (new_stream_count != t->global.concurrent_stream_count) { t->global.concurrent_stream_count = new_stream_count; maybe_start_some_streams(&t->global); @@ -759,20 +770,35 @@ static void unlock_check_read_write_state(grpc_chttp2_transport *t) { } } + if (!t->writing_active) { + while (grpc_chttp2_list_pop_cancelled_waiting_for_writing(transport_global, + &stream_global)) { + grpc_chttp2_list_add_read_write_state_changed(transport_global, + stream_global); + } + } + while (grpc_chttp2_list_pop_read_write_state_changed(transport_global, &stream_global)) { if (stream_global->cancelled) { - stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; - stream_global->read_closed = 1; - if (!stream_global->published_cancelled) { - char buffer[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(stream_global->cancelled_status, buffer); - grpc_chttp2_incoming_metadata_buffer_add(&stream_global->incoming_metadata, - grpc_mdelem_from_strings(t->metadata_context, "grpc-status", buffer)); - grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( - &stream_global->incoming_metadata, - &stream_global->incoming_sopb); - stream_global->published_cancelled = 1; + if (t->writing_active && + stream_global->write_state != GRPC_WRITE_STATE_SENT_CLOSE) { + grpc_chttp2_list_add_cancelled_waiting_for_writing(transport_global, + stream_global); + } else { + stream_global->write_state = GRPC_WRITE_STATE_SENT_CLOSE; + stream_global->read_closed = 1; + if (!stream_global->published_cancelled) { + char buffer[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(stream_global->cancelled_status, buffer); + grpc_chttp2_incoming_metadata_buffer_add( + &stream_global->incoming_metadata, + grpc_mdelem_from_strings(t->metadata_context, "grpc-status", + buffer)); + grpc_chttp2_incoming_metadata_buffer_place_metadata_batch_into( + &stream_global->incoming_metadata, &stream_global->incoming_sopb); + stream_global->published_cancelled = 1; + } } } if (stream_global->write_state == GRPC_WRITE_STATE_SENT_CLOSE && @@ -820,10 +846,10 @@ static void cancel_from_api(grpc_chttp2_transport_global *transport_global, stream_global->cancelled = 1; stream_global->cancelled_status = status; if (stream_global->id != 0) { - gpr_slice_buffer_add(&transport_global->qbuf, - grpc_chttp2_rst_stream_create( - stream_global->id, - grpc_chttp2_grpc_status_to_http2_error(status))); + gpr_slice_buffer_add( + &transport_global->qbuf, + grpc_chttp2_rst_stream_create( + stream_global->id, grpc_chttp2_grpc_status_to_http2_error(status))); } grpc_chttp2_list_add_read_write_state_changed(transport_global, stream_global); @@ -840,9 +866,6 @@ static void end_all_the_calls(grpc_chttp2_transport *t) { } static void drop_connection(grpc_chttp2_transport *t) { - if (t->global.error_state == GRPC_CHTTP2_ERROR_STATE_NONE) { - t->global.error_state = GRPC_CHTTP2_ERROR_STATE_SEEN; - } close_transport_locked(t); end_all_the_calls(t); } @@ -892,7 +915,7 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices, lock(t); i = 0; GPR_ASSERT(!t->parsing_active); - if (t->global.error_state == GRPC_CHTTP2_ERROR_STATE_NONE) { + if (!t->closed) { t->parsing_active = 1; /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, @@ -910,7 +933,8 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices, /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); - t->global.concurrent_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map); + t->global.concurrent_stream_count = + grpc_chttp2_stream_map_size(&t->parsing_stream_map); if (t->parsing.initial_window_update != 0) { grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, update_global_window, t); @@ -944,76 +968,33 @@ static void reading_action(void *pt, int iomgr_success_ignored) { * CALLBACK LOOP */ -typedef struct { - grpc_chttp2_transport *t; - gpr_uint32 error; - gpr_slice text; - grpc_iomgr_closure closure; -} notify_goaways_args; - -static void notify_goaways(void *p, int iomgr_success_ignored) { - notify_goaways_args *a = p; - grpc_chttp2_transport *t = a->t; - - t->channel_callback.cb->goaway(t->channel_callback.cb_user_data, &t->base, - a->error, a->text); - - gpr_free(a); - - lock(t); - t->channel_callback.executing = 0; - unlock(t); - - UNREF_TRANSPORT(t, "notify_goaways"); +static void schedule_closure_for_connectivity(void *a, + grpc_iomgr_closure *closure) { + grpc_chttp2_schedule_closure(a, closure, 1); } -static void notify_closed(void *gt, int iomgr_success_ignored) { - grpc_chttp2_transport *t = gt; - t->channel_callback.cb->closed(t->channel_callback.cb_user_data, &t->base); - - lock(t); - t->channel_callback.executing = 0; - unlock(t); - - UNREF_TRANSPORT(t, "notify_closed"); -} - -static void unlock_check_channel_callbacks(grpc_chttp2_transport *t) { - if (t->channel_callback.executing) { - return; - } - if (t->global.goaway_state != GRPC_CHTTP2_ERROR_STATE_NONE) { - if (t->global.goaway_state == GRPC_CHTTP2_ERROR_STATE_SEEN && - t->global.error_state != GRPC_CHTTP2_ERROR_STATE_NOTIFIED) { - notify_goaways_args *a = gpr_malloc(sizeof(*a)); - a->t = t; - a->error = t->global.goaway_error; - a->text = t->global.goaway_text; - t->global.goaway_state = GRPC_CHTTP2_ERROR_STATE_NOTIFIED; - t->channel_callback.executing = 1; - grpc_iomgr_closure_init(&a->closure, notify_goaways, a); - REF_TRANSPORT(t, "notify_goaways"); - grpc_chttp2_schedule_closure(&t->global, &a->closure, 1); - return; - } else if (t->global.goaway_state != GRPC_CHTTP2_ERROR_STATE_NOTIFIED) { - return; - } - } - if (t->global.error_state == GRPC_CHTTP2_ERROR_STATE_SEEN) { - t->global.error_state = GRPC_CHTTP2_ERROR_STATE_NOTIFIED; - t->channel_callback.executing = 1; - REF_TRANSPORT(t, "notify_closed"); - grpc_chttp2_schedule_closure(&t->global, &t->channel_callback.notify_closed, - 1); - } +static void connectivity_state_set( + grpc_chttp2_transport_global *transport_global, + grpc_connectivity_state state) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_DEBUG, "set connectivity_state=%d", state)); + grpc_connectivity_state_set_with_scheduler( + &TRANSPORT_FROM_GLOBAL(transport_global)->channel_callback.state_tracker, + state, schedule_closure_for_connectivity, transport_global); } void grpc_chttp2_schedule_closure( grpc_chttp2_transport_global *transport_global, grpc_iomgr_closure *closure, int success) { closure->success = success; - closure->next = transport_global->pending_closures; - transport_global->pending_closures = closure; + if (transport_global->pending_closures_tail == NULL) { + transport_global->pending_closures_head = + transport_global->pending_closures_tail = closure; + } else { + transport_global->pending_closures_tail->next = closure; + transport_global->pending_closures_tail = closure; + } + closure->next = NULL; } /* @@ -1027,13 +1008,6 @@ static void add_to_pollset_locked(grpc_chttp2_transport *t, } } -static void add_to_pollset(grpc_transport *gt, grpc_pollset *pollset) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - lock(t); - add_to_pollset_locked(t, pollset); - unlock(t); -} - /* * TRACING */ @@ -1069,23 +1043,21 @@ void grpc_chttp2_flowctl_trace(const char *file, int line, const char *reason, * INTEGRATION GLUE */ -static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), - init_stream, - perform_op, - add_to_pollset, - destroy_stream, - goaway, - close_transport, - send_ping, - destroy_transport}; - -void grpc_create_chttp2_transport(grpc_transport_setup_callback setup, - void *arg, - const grpc_channel_args *channel_args, - grpc_endpoint *ep, gpr_slice *slices, - size_t nslices, grpc_mdctx *mdctx, - int is_client) { +static const grpc_transport_vtable vtable = { + sizeof(grpc_chttp2_stream), init_stream, perform_stream_op, + perform_transport_op, destroy_stream, destroy_transport}; + +grpc_transport *grpc_create_chttp2_transport( + const grpc_channel_args *channel_args, grpc_endpoint *ep, grpc_mdctx *mdctx, + int is_client) { grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport)); - init_transport(t, setup, arg, channel_args, ep, slices, nslices, mdctx, - is_client); + init_transport(t, channel_args, ep, mdctx, is_client); + return &t->base; +} + +void grpc_chttp2_transport_start_reading(grpc_transport *transport, + gpr_slice *slices, size_t nslices) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; + REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ + recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); } diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h index 18e19f03af..fa0d6e4151 100644 --- a/src/core/transport/chttp2_transport.h +++ b/src/core/transport/chttp2_transport.h @@ -40,11 +40,11 @@ extern int grpc_http_trace; extern int grpc_flowctl_trace; -void grpc_create_chttp2_transport(grpc_transport_setup_callback setup, - void *arg, - const grpc_channel_args *channel_args, - grpc_endpoint *ep, gpr_slice *slices, - size_t nslices, grpc_mdctx *metadata_context, - int is_client); +grpc_transport *grpc_create_chttp2_transport( + const grpc_channel_args *channel_args, grpc_endpoint *ep, + grpc_mdctx *metadata_context, int is_client); + +void grpc_chttp2_transport_start_reading(grpc_transport *transport, + gpr_slice *slices, size_t nslices); #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/src/core/transport/connectivity_state.c b/src/core/transport/connectivity_state.c new file mode 100644 index 0000000000..1091ceae44 --- /dev/null +++ b/src/core/transport/connectivity_state.c @@ -0,0 +1,112 @@ +/* + * + * 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/transport/connectivity_state.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state init_state) { + tracker->current_state = init_state; + tracker->watchers = NULL; +} + +void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker) { + grpc_connectivity_state_watcher *w; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (GRPC_CHANNEL_FATAL_FAILURE != *w->current) { + *w->current = GRPC_CHANNEL_FATAL_FAILURE; + grpc_iomgr_add_callback(w->notify); + } else { + grpc_iomgr_add_delayed_callback(w->notify, 0); + } + gpr_free(w); + } +} + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker *tracker) { + return tracker->current_state; +} + +int grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, + grpc_iomgr_closure *notify) { + if (tracker->current_state != *current) { + *current = tracker->current_state; + grpc_iomgr_add_callback(notify); + } else { + grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w)); + w->current = current; + w->notify = notify; + w->next = tracker->watchers; + tracker->watchers = w; + } + return tracker->current_state == GRPC_CHANNEL_IDLE; +} + +void grpc_connectivity_state_set_with_scheduler( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, + void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg) { + grpc_connectivity_state_watcher *new = NULL; + grpc_connectivity_state_watcher *w; + if (tracker->current_state == state) { + return; + } + tracker->current_state = state; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (state != *w->current) { + *w->current = state; + scheduler(arg, w->notify); + gpr_free(w); + } else { + w->next = new; + new = w; + } + } + tracker->watchers = new; +} + +static void default_scheduler(void *ignored, grpc_iomgr_closure *closure) { + grpc_iomgr_add_callback(closure); +} + +void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state) { + grpc_connectivity_state_set_with_scheduler(tracker, state, default_scheduler, + NULL); +} diff --git a/src/core/transport/connectivity_state.h b/src/core/transport/connectivity_state.h new file mode 100644 index 0000000000..bbdcbcb069 --- /dev/null +++ b/src/core/transport/connectivity_state.h @@ -0,0 +1,74 @@ +/* + * + * 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 GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H +#define GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H + +#include <grpc/grpc.h> +#include "src/core/iomgr/iomgr.h" + +typedef struct grpc_connectivity_state_watcher { + /** we keep watchers in a linked list */ + struct grpc_connectivity_state_watcher *next; + /** closure to notify on change */ + grpc_iomgr_closure *notify; + /** the current state as believed by the watcher */ + grpc_connectivity_state *current; +} grpc_connectivity_state_watcher; + +typedef struct { + /** current connectivity state */ + grpc_connectivity_state current_state; + /** all our watchers */ + grpc_connectivity_state_watcher *watchers; +} grpc_connectivity_state_tracker; + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state init_state); +void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker *tracker); + +void grpc_connectivity_state_set(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state); +void grpc_connectivity_state_set_with_scheduler( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state state, + void (*scheduler)(void *arg, grpc_iomgr_closure *closure), void *arg); + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker *tracker); + +/** Return 1 if the channel should start connecting, 0 otherwise */ +int grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, + grpc_iomgr_closure *notify); + +#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/src/core/transport/stream_op.h b/src/core/transport/stream_op.h index 842fc932b9..964d39d14f 100644 --- a/src/core/transport/stream_op.h +++ b/src/core/transport/stream_op.h @@ -41,7 +41,7 @@ #include "src/core/transport/metadata.h" /* this many stream ops are inlined into a sopb before allocating */ -#define GRPC_SOPB_INLINE_ELEMENTS 16 +#define GRPC_SOPB_INLINE_ELEMENTS 4 /* Operations that can be performed on a stream. Used by grpc_stream_op. */ diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c index 39d7b701f2..fe565944ed 100644 --- a/src/core/transport/transport.c +++ b/src/core/transport/transport.c @@ -38,34 +38,26 @@ size_t grpc_transport_stream_size(grpc_transport *transport) { return transport->vtable->sizeof_stream; } -void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status, - gpr_slice message) { - transport->vtable->goaway(transport, status, message); -} - -void grpc_transport_close(grpc_transport *transport) { - transport->vtable->close(transport); -} - void grpc_transport_destroy(grpc_transport *transport) { transport->vtable->destroy(transport); } int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, const void *server_data, - grpc_transport_op *initial_op) { + grpc_transport_stream_op *initial_op) { return transport->vtable->init_stream(transport, stream, server_data, initial_op); } -void grpc_transport_perform_op(grpc_transport *transport, grpc_stream *stream, - grpc_transport_op *op) { - transport->vtable->perform_op(transport, stream, op); +void grpc_transport_perform_stream_op(grpc_transport *transport, + grpc_stream *stream, + grpc_transport_stream_op *op) { + transport->vtable->perform_stream_op(transport, stream, op); } -void grpc_transport_add_to_pollset(grpc_transport *transport, - grpc_pollset *pollset) { - transport->vtable->add_to_pollset(transport, pollset); +void grpc_transport_perform_op(grpc_transport *transport, + grpc_transport_op *op) { + transport->vtable->perform_op(transport, op); } void grpc_transport_destroy_stream(grpc_transport *transport, @@ -73,29 +65,8 @@ void grpc_transport_destroy_stream(grpc_transport *transport, transport->vtable->destroy_stream(transport, stream); } -void grpc_transport_ping(grpc_transport *transport, grpc_iomgr_closure *cb) { - transport->vtable->ping(transport, cb); -} - -void grpc_transport_setup_cancel(grpc_transport_setup *setup) { - setup->vtable->cancel(setup); -} - -void grpc_transport_setup_initiate(grpc_transport_setup *setup) { - setup->vtable->initiate(setup); -} - -void grpc_transport_setup_add_interested_party(grpc_transport_setup *setup, - grpc_pollset *pollset) { - setup->vtable->add_interested_party(setup, pollset); -} - -void grpc_transport_setup_del_interested_party(grpc_transport_setup *setup, - grpc_pollset *pollset) { - setup->vtable->del_interested_party(setup, pollset); -} - -void grpc_transport_op_finish_with_failure(grpc_transport_op *op) { +void grpc_transport_stream_op_finish_with_failure( + grpc_transport_stream_op *op) { if (op->send_ops) { op->on_done_send->cb(op->on_done_send->cb_arg, 0); } @@ -107,9 +78,9 @@ void grpc_transport_op_finish_with_failure(grpc_transport_op *op) { } } -void grpc_transport_op_add_cancellation(grpc_transport_op *op, - grpc_status_code status, - grpc_mdstr *message) { +void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, + grpc_status_code status, + grpc_mdstr *message) { if (op->cancel_with_status == GRPC_STATUS_OK) { op->cancel_with_status = status; } diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h index a2c41c47af..1429737721 100644 --- a/src/core/transport/transport.h +++ b/src/core/transport/transport.h @@ -43,7 +43,6 @@ /* forward declarations */ typedef struct grpc_transport grpc_transport; -typedef struct grpc_transport_callbacks grpc_transport_callbacks; /* grpc_stream doesn't actually exist. It's used as a typesafe opaque pointer for whatever data the transport wants to track @@ -62,8 +61,9 @@ typedef enum grpc_stream_state { GRPC_STREAM_CLOSED } grpc_stream_state; -/* Transport op: a set of operations to perform on a transport */ -typedef struct grpc_transport_op { +/* Transport stream op: a set of operations to perform on a transport + against a single stream */ +typedef struct grpc_transport_stream_op { grpc_iomgr_closure *on_consumed; grpc_stream_op_buffer *send_ops; @@ -80,32 +80,34 @@ typedef struct grpc_transport_op { /* Indexes correspond to grpc_context_index enum values */ grpc_call_context_element *context; -} grpc_transport_op; +} grpc_transport_stream_op; -/* Callbacks made from the transport to the upper layers of grpc. */ -struct grpc_transport_callbacks { - /* Initialize a new stream on behalf of the transport. - Must result in a call to - grpc_transport_init_stream(transport, ..., request) in the same call - stack. - Must not result in any other calls to the transport. - - Arguments: - user_data - the transport user data set at transport creation time - transport - the grpc_transport instance making this call - request - request parameters for this stream (owned by the caller) - server_data - opaque transport dependent argument that should be passed - to grpc_transport_init_stream - */ - void (*accept_stream)(void *user_data, grpc_transport *transport, - const void *server_data); - - void (*goaway)(void *user_data, grpc_transport *transport, - grpc_status_code status, gpr_slice debug); - - /* The transport has been closed */ - void (*closed)(void *user_data, grpc_transport *transport); -}; +/** Transport op: a set of operations to perform on a transport as a whole */ +typedef struct grpc_transport_op { + /** called when processing of this op is done */ + grpc_iomgr_closure *on_consumed; + /** connectivity monitoring */ + grpc_iomgr_closure *on_connectivity_state_change; + grpc_connectivity_state *connectivity_state; + /** should the transport be disconnected */ + int disconnect; + /** should we send a goaway? + after a goaway is sent, once there are no more active calls on + the transport, the transport should disconnect */ + int send_goaway; + /** what should the goaway contain? */ + grpc_status_code goaway_status; + gpr_slice *goaway_message; + /** set the callback for accepting new streams; + this is a permanent callback, unlike the other one-shot closures */ + void (*set_accept_stream)(void *user_data, grpc_transport *transport, + const void *server_data); + void *set_accept_stream_user_data; + /** add this transport to a pollset */ + grpc_pollset *bind_pollset; + /** send a ping, call this back if not NULL */ + grpc_iomgr_closure *send_ping; +} grpc_transport_op; /* Returns the amount of memory required to store a grpc_stream for this transport */ @@ -122,7 +124,7 @@ size_t grpc_transport_stream_size(grpc_transport *transport); supplied from the accept_stream callback function */ int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, const void *server_data, - grpc_transport_op *initial_op); + grpc_transport_stream_op *initial_op); /* Destroy transport data for a stream. @@ -137,17 +139,13 @@ int grpc_transport_init_stream(grpc_transport *transport, grpc_stream *stream, void grpc_transport_destroy_stream(grpc_transport *transport, grpc_stream *stream); -void grpc_transport_op_finish_with_failure(grpc_transport_op *op); - -void grpc_transport_op_add_cancellation(grpc_transport_op *op, - grpc_status_code status, - grpc_mdstr *message); +void grpc_transport_stream_op_finish_with_failure(grpc_transport_stream_op *op); -/* TODO(ctiller): remove this */ -void grpc_transport_add_to_pollset(grpc_transport *transport, - grpc_pollset *pollset); +void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op, + grpc_status_code status, + grpc_mdstr *message); -char *grpc_transport_op_string(grpc_transport_op *op); +char *grpc_transport_stream_op_string(grpc_transport_stream_op *op); /* Send a batch of operations on a transport @@ -157,8 +155,12 @@ char *grpc_transport_op_string(grpc_transport_op *op); transport - the transport on which to initiate the stream stream - the stream on which to send the operations. This must be non-NULL and previously initialized by the same transport. - op - a grpc_transport_op specifying the op to perform */ -void grpc_transport_perform_op(grpc_transport *transport, grpc_stream *stream, + op - a grpc_transport_stream_op specifying the op to perform */ +void grpc_transport_perform_stream_op(grpc_transport *transport, + grpc_stream *stream, + grpc_transport_stream_op *op); + +void grpc_transport_perform_op(grpc_transport *transport, grpc_transport_op *op); /* Send a ping on a transport @@ -176,52 +178,4 @@ void grpc_transport_close(grpc_transport *transport); /* Destroy the transport */ void grpc_transport_destroy(grpc_transport *transport); -/* Return type for grpc_transport_setup_callback */ -typedef struct grpc_transport_setup_result { - void *user_data; - const grpc_transport_callbacks *callbacks; -} grpc_transport_setup_result; - -/* Given a transport, return callbacks for that transport. Used to finalize - setup as a transport is being created */ -typedef grpc_transport_setup_result (*grpc_transport_setup_callback)( - void *setup_arg, grpc_transport *transport, grpc_mdctx *mdctx); - -typedef struct grpc_transport_setup grpc_transport_setup; -typedef struct grpc_transport_setup_vtable grpc_transport_setup_vtable; - -struct grpc_transport_setup_vtable { - void (*initiate)(grpc_transport_setup *setup); - void (*add_interested_party)(grpc_transport_setup *setup, - grpc_pollset *pollset); - void (*del_interested_party)(grpc_transport_setup *setup, - grpc_pollset *pollset); - void (*cancel)(grpc_transport_setup *setup); -}; - -/* Transport setup is an asynchronous utility interface for client channels to - establish connections. It's transport agnostic. */ -struct grpc_transport_setup { - const grpc_transport_setup_vtable *vtable; -}; - -/* Initiate transport setup: e.g. for TCP+DNS trigger a resolve of the name - given at transport construction time, create the tcp connection, perform - handshakes, and call some grpc_transport_setup_result function provided at - setup construction time. - This *may* be implemented as a no-op if the setup process monitors something - continuously. */ -void grpc_transport_setup_initiate(grpc_transport_setup *setup); - -void grpc_transport_setup_add_interested_party(grpc_transport_setup *setup, - grpc_pollset *pollset); -void grpc_transport_setup_del_interested_party(grpc_transport_setup *setup, - grpc_pollset *pollset); - -/* Cancel transport setup. After this returns, no new transports should be - created, and all pending transport setup callbacks should be completed. - After this call completes, setup should be considered invalid (this can be - used as a destruction call by setup). */ -void grpc_transport_setup_cancel(grpc_transport_setup *setup); - #endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H */ diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h index c51951b7a7..515721dfb6 100644 --- a/src/core/transport/transport_impl.h +++ b/src/core/transport/transport_impl.h @@ -43,28 +43,19 @@ typedef struct grpc_transport_vtable { /* implementation of grpc_transport_init_stream */ int (*init_stream)(grpc_transport *self, grpc_stream *stream, - const void *server_data, grpc_transport_op *initial_op); + const void *server_data, + grpc_transport_stream_op *initial_op); - /* implementation of grpc_transport_send_batch */ - void (*perform_op)(grpc_transport *self, grpc_stream *stream, - grpc_transport_op *op); + /* implementation of grpc_transport_perform_stream_op */ + void (*perform_stream_op)(grpc_transport *self, grpc_stream *stream, + grpc_transport_stream_op *op); - /* implementation of grpc_transport_add_to_pollset */ - void (*add_to_pollset)(grpc_transport *self, grpc_pollset *pollset); + /* implementation of grpc_transport_perform_op */ + void (*perform_op)(grpc_transport *self, grpc_transport_op *op); /* implementation of grpc_transport_destroy_stream */ void (*destroy_stream)(grpc_transport *self, grpc_stream *stream); - /* implementation of grpc_transport_goaway */ - void (*goaway)(grpc_transport *self, grpc_status_code status, - gpr_slice debug_data); - - /* implementation of grpc_transport_close */ - void (*close)(grpc_transport *self); - - /* implementation of grpc_transport_ping */ - void (*ping)(grpc_transport *self, grpc_iomgr_closure *cb); - /* implementation of grpc_transport_destroy */ void (*destroy)(grpc_transport *self); } grpc_transport_vtable; diff --git a/src/core/transport/transport_op_string.c b/src/core/transport/transport_op_string.c index a408b75790..0da396a320 100644 --- a/src/core/transport/transport_op_string.c +++ b/src/core/transport/transport_op_string.c @@ -47,14 +47,12 @@ static void put_metadata(gpr_strvec *b, grpc_mdelem *md) { gpr_strvec_add(b, gpr_strdup("key=")); - gpr_strvec_add( - b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->key->slice), - GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT)); + gpr_strvec_add(b, + gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII)); gpr_strvec_add(b, gpr_strdup(" value=")); - gpr_strvec_add(b, gpr_hexdump((char *)GPR_SLICE_START_PTR(md->value->slice), - GPR_SLICE_LENGTH(md->value->slice), - GPR_HEXDUMP_PLAINTEXT)); + gpr_strvec_add( + b, gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII)); } static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { @@ -107,7 +105,7 @@ char *grpc_sopb_string(grpc_stream_op_buffer *sopb) { return out; } -char *grpc_transport_op_string(grpc_transport_op *op) { +char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) { char *tmp; char *out; int first = 1; @@ -146,6 +144,13 @@ char *grpc_transport_op_string(grpc_transport_op *op) { gpr_strvec_add(&b, tmp); } + if (op->on_consumed != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = 0; + gpr_asprintf(&tmp, "ON_CONSUMED:%p", op->on_consumed); + gpr_strvec_add(&b, tmp); + } + out = gpr_strvec_flatten(&b, NULL); gpr_strvec_destroy(&b); @@ -153,8 +158,8 @@ char *grpc_transport_op_string(grpc_transport_op *op) { } void grpc_call_log_op(char *file, int line, gpr_log_severity severity, - grpc_call_element *elem, grpc_transport_op *op) { - char *str = grpc_transport_op_string(op); + grpc_call_element *elem, grpc_transport_stream_op *op) { + char *str = grpc_transport_stream_op_string(op); gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); gpr_free(str); } diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc index 72593f877e..5bc6f6fd91 100644 --- a/src/cpp/client/channel.cc +++ b/src/cpp/client/channel.cc @@ -39,6 +39,7 @@ #include <grpc/support/log.h> #include <grpc/support/slice.h> +#include "src/core/census/grpc_context.h" #include "src/core/profiling/timers.h" #include <grpc++/channel_arguments.h> #include <grpc++/client_context.h> @@ -68,6 +69,7 @@ Call Channel::CreateCall(const RpcMethod& method, ClientContext* context, ? target_.c_str() : context->authority().c_str(), context->raw_deadline()); + grpc_census_call_set_context(c_call, context->get_census_context()); GRPC_TIMER_MARK(GRPC_PTAG_CPP_CALL_CREATED, c_call); context->set_call(c_call, shared_from_this()); return Call(c_call, this, cq); diff --git a/src/node/binding.gyp b/src/node/binding.gyp index 83f72fabca..6ba233388a 100644 --- a/src/node/binding.gyp +++ b/src/node/binding.gyp @@ -10,20 +10,54 @@ '-pthread', '-pedantic', '-g', - '-zdefs' + '-zdefs', '-Werror' ], 'ldflags': [ '-g' ], - 'link_settings': { - 'libraries': [ - '-lpthread', - '-lgrpc', - '-lgpr' - ] - }, "conditions": [ + ['OS != "win"', { + 'variables': { + 'pkg_config_grpc': '<!(pkg-config --exists grpc >/dev/null 2>&1 && echo true || echo false)' + }, + 'conditions': [ + ['pkg_config_grpc == "true"', { + 'link_settings': { + 'libraries': [ + '<!@(pkg-config --libs-only-l --static grpc)' + ] + }, + 'cflags': [ + '<!@(pkg-config --cflags grpc)' + ], + 'libraries': [ + '<!@(pkg-config --libs-only-L --static grpc)' + ], + 'ldflags': [ + '<!@(pkg-config --libs-only-other --static grpc)' + ] + }, { + 'link_settings': { + 'libraries': [ + '-lpthread', + '-lgrpc', + '-lgpr' + ], + }, + 'conditions':[ + ['OS != "mac"', { + 'link_settings': { + 'libraries': [ + '-lrt' + ] + } + }] + ] + } + ] + ] + }], ['OS == "mac"', { 'xcode_settings': { 'MACOSX_DEPLOYMENT_TARGET': '10.9', @@ -32,13 +66,6 @@ '-stdlib=libc++' ] } - }], - ['OS != "mac"', { - 'link_settings': { - 'libraries': [ - '-lrt' - ] - } }] ], "target_name": "grpc", diff --git a/src/node/package.json b/src/node/package.json index 7d4a493af4..6b545705e1 100644 --- a/src/node/package.json +++ b/src/node/package.json @@ -27,7 +27,7 @@ "bindings": "^1.2.0", "lodash": "^3.9.3", "nan": "^1.5.0", - "protobufjs": "dcodeIO/ProtoBuf.js" + "protobufjs": "^4.0.0" }, "devDependencies": { "async": "^0.9.0", diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 33aae10747..cba53fa2f6 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -48,8 +48,6 @@ #import <Foundation/Foundation.h> #import <RxLibrary/GRXWriter.h> -@class GRPCMethodName; - // Key used in |NSError|'s |userInfo| dictionary to store the response metadata sent by the server. extern id const kGRPCStatusMetadataKey; @@ -90,7 +88,7 @@ extern id const kGRPCStatusMetadataKey; // the specific remote method called). // To finish a call right away, invoke cancel. - (instancetype)initWithHost:(NSString *)host - method:(GRPCMethodName *)method + path:(NSString *)path requestsWriter:(id<GRXWriter>)requestsWriter NS_DESIGNATED_INITIALIZER; // Finishes the request side of this call, notifies the server that the RPC diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 77eebeff76..4ac4e4d37f 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -36,11 +36,9 @@ #include <grpc/grpc.h> #include <grpc/support/time.h> -#import "GRPCMethodName.h" #import "private/GRPCChannel.h" #import "private/GRPCCompletionQueue.h" #import "private/GRPCDelegateWrapper.h" -#import "private/GRPCMethodName+HTTP2Encoding.h" #import "private/GRPCWrappedCall.h" #import "private/NSData+GRPC.h" #import "private/NSDictionary+GRPC.h" @@ -90,14 +88,14 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; @synthesize state = _state; - (instancetype)init { - return [self initWithHost:nil method:nil requestsWriter:nil]; + return [self initWithHost:nil path:nil requestsWriter:nil]; } // Designated initializer - (instancetype)initWithHost:(NSString *)host - method:(GRPCMethodName *)method + path:(NSString *)path requestsWriter:(id<GRXWriter>)requestWriter { - if (!host || !method) { + if (!host || !path) { [NSException raise:NSInvalidArgumentException format:@"Neither host nor method can be nil."]; } if (requestWriter.state != GRXWriterStateNotStarted) { @@ -114,7 +112,7 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey"; _channel = [GRPCChannel channelToHost:host]; _wrappedCall = [[GRPCWrappedCall alloc] initWithChannel:_channel - method:method.HTTP2Path + path:path host:host]; // Serial queue to invoke the non-reentrant methods of the grpc_call object. diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m index 36f4c0aa5e..af4326332f 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -60,7 +60,7 @@ } - (instancetype)initWithHost:(NSString *)host { - if (![host containsString:@"://"]) { + if (![host rangeOfString:@"://"].length) { // No scheme provided; assume https. host = [@"https://" stringByAppendingString:host]; } diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index c08aefc6a8..18f8bb5531 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -84,7 +84,7 @@ @interface GRPCWrappedCall : NSObject - (instancetype)initWithChannel:(GRPCChannel *)channel - method:(NSString *)method + path:(NSString *)path host:(NSString *)host NS_DESIGNATED_INITIALIZER; - (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler; diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index d94b25091e..45f10f5d63 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -225,13 +225,13 @@ } - (instancetype)init { - return [self initWithChannel:nil method:nil host:nil]; + return [self initWithChannel:nil path:nil host:nil]; } - (instancetype)initWithChannel:(GRPCChannel *)channel - method:(NSString *)method + path:(NSString *)path host:(NSString *)host { - if (!channel || !method || !host) { + if (!channel || !path || !host) { [NSException raise:NSInvalidArgumentException format:@"channel, method, and host cannot be nil."]; } @@ -247,7 +247,7 @@ return nil; } _call = grpc_channel_create_call(channel.unmanagedChannel, _queue.unmanagedQueue, - method.UTF8String, host.UTF8String, gpr_inf_future); + path.UTF8String, host.UTF8String, gpr_inf_future); if (_call == NULL) { return nil; } diff --git a/src/objective-c/GRPCClient/GRPCMethodName.h b/src/objective-c/ProtoRPC/ProtoMethod.h index fe153dd478..8f554a0483 100644 --- a/src/objective-c/GRPCClient/GRPCMethodName.h +++ b/src/objective-c/ProtoRPC/ProtoMethod.h @@ -33,17 +33,16 @@ #import <Foundation/Foundation.h> -// See the README file for an introduction to this library. - -// A fully-qualified gRPC method name. Full qualification is needed because a gRPC endpoint can -// implement multiple interfaces. -// TODO(jcanizales): Move to ProtoRPC package. -// TODO(jcanizales): Rename interface -> service. -@interface GRPCMethodName : NSObject +// A fully-qualified proto service method name. Full qualification is needed because a gRPC endpoint +// can implement multiple services. +@interface ProtoMethod : NSObject @property(nonatomic, readonly) NSString *package; -@property(nonatomic, readonly) NSString *interface; +@property(nonatomic, readonly) NSString *service; @property(nonatomic, readonly) NSString *method; + +@property(nonatomic, readonly) NSString *HTTPPath; + - (instancetype)initWithPackage:(NSString *)package - interface:(NSString *)interface + service:(NSString *)service method:(NSString *)method; @end diff --git a/src/objective-c/GRPCClient/GRPCMethodName.m b/src/objective-c/ProtoRPC/ProtoMethod.m index 96724073a5..1113b4fbaa 100644 --- a/src/objective-c/GRPCClient/GRPCMethodName.m +++ b/src/objective-c/ProtoRPC/ProtoMethod.m @@ -31,17 +31,25 @@ * */ -#import "GRPCMethodName.h" +#import "ProtoMethod.h" -@implementation GRPCMethodName +@implementation ProtoMethod - (instancetype)initWithPackage:(NSString *)package - interface:(NSString *)interface + service:(NSString *)service method:(NSString *)method { if ((self = [super init])) { _package = [package copy]; - _interface = [interface copy]; + _service = [service copy]; _method = [method copy]; } return self; } + +- (NSString *)HTTPPath { + if (_package) { + return [NSString stringWithFormat:@"/%@.%@/%@", _package, _service, _method]; + } else { + return [NSString stringWithFormat:@"/%@/%@", _service, _method]; + } +} @end diff --git a/src/objective-c/ProtoRPC/ProtoRPC.h b/src/objective-c/ProtoRPC/ProtoRPC.h index a383310619..fcc0a507fe 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.h +++ b/src/objective-c/ProtoRPC/ProtoRPC.h @@ -34,10 +34,12 @@ #import <Foundation/Foundation.h> #import <GRPCClient/GRPCCall.h> +#import "ProtoMethod.h" + @interface ProtoRPC : GRPCCall - (instancetype)initWithHost:(NSString *)host - method:(GRPCMethodName *)method + method:(ProtoMethod *)method requestsWriter:(id<GRXWriter>)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id<GRXWriteable>)responsesWriteable NS_DESIGNATED_INITIALIZER; diff --git a/src/objective-c/ProtoRPC/ProtoRPC.m b/src/objective-c/ProtoRPC/ProtoRPC.m index 4da646d7b4..fe3ccf0541 100644 --- a/src/objective-c/ProtoRPC/ProtoRPC.m +++ b/src/objective-c/ProtoRPC/ProtoRPC.m @@ -42,19 +42,20 @@ id<GRXWriteable> _responseWriteable; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" - (instancetype)initWithHost:(NSString *)host - method:(GRPCMethodName *)method + path:(NSString *)path requestsWriter:(id<GRXWriter>)requestsWriter { - return [self initWithHost:host - method:method - requestsWriter:requestsWriter - responseClass:nil - responsesWriteable:nil]; + [NSException raise:NSInvalidArgumentException + format:@"Please use ProtoRPC's designated initializer instead."]; + return nil; } +#pragma clang diagnostic pop // Designated initializer - (instancetype)initWithHost:(NSString *)host - method:(GRPCMethodName *)method + method:(ProtoMethod *)method requestsWriter:(id<GRXWriter>)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id<GRXWriteable>)responsesWriteable { @@ -70,7 +71,7 @@ // sending GPBMessages. return [proto data]; }]; - if ((self = [super initWithHost:host method:method requestsWriter:bytesWriter])) { + if ((self = [super initWithHost:host path:method.HTTPPath requestsWriter:bytesWriter])) { // A writeable that parses the proto messages received. _responseWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { [responsesWriteable writeValue:[responseClass parseFromData:value error:NULL]]; diff --git a/src/objective-c/ProtoRPC/ProtoService.m b/src/objective-c/ProtoRPC/ProtoService.m index 47bdb5dc6e..d7c5b6a850 100644 --- a/src/objective-c/ProtoRPC/ProtoService.m +++ b/src/objective-c/ProtoRPC/ProtoService.m @@ -33,10 +33,10 @@ #import "ProtoService.h" -#import <GRPCClient/GRPCMethodName.h> #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter.h> +#import "ProtoMethod.h" #import "ProtoRPC.h" @implementation ProtoService { @@ -69,9 +69,9 @@ requestsWriter:(id<GRXWriter>)requestsWriter responseClass:(Class)responseClass responsesWriteable:(id<GRXWriteable>)responsesWriteable { - GRPCMethodName *methodName = [[GRPCMethodName alloc] initWithPackage:_packageName - interface:_serviceName - method:method]; + ProtoMethod *methodName = [[ProtoMethod alloc] initWithPackage:_packageName + service:_serviceName + method:method]; return [[ProtoRPC alloc] initWithHost:_host method:methodName requestsWriter:requestsWriter diff --git a/src/objective-c/tests/GRPCClientTests.m b/src/objective-c/tests/GRPCClientTests.m index e421127ea1..f9c2d5d8d6 100644 --- a/src/objective-c/tests/GRPCClientTests.m +++ b/src/objective-c/tests/GRPCClientTests.m @@ -35,7 +35,7 @@ #import <XCTest/XCTest.h> #import <GRPCClient/GRPCCall.h> -#import <GRPCClient/GRPCMethodName.h> +#import <ProtoRPC/ProtoMethod.h> #import <RemoteTest/Messages.pbobjc.h> #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter+Immediate.h> @@ -47,9 +47,9 @@ static NSString * const kHostAddress = @"grpc-test.sandbox.google.com"; static NSString * const kPackage = @"grpc.testing"; static NSString * const kService = @"TestService"; -static GRPCMethodName *kInexistentMethod; -static GRPCMethodName *kEmptyCallMethod; -static GRPCMethodName *kUnaryCallMethod; +static ProtoMethod *kInexistentMethod; +static ProtoMethod *kEmptyCallMethod; +static ProtoMethod *kUnaryCallMethod; @interface GRPCClientTests : XCTestCase @end @@ -58,22 +58,22 @@ static GRPCMethodName *kUnaryCallMethod; - (void)setUp { // This method isn't implemented by the remote server. - kInexistentMethod = [[GRPCMethodName alloc] initWithPackage:kPackage - interface:kService - method:@"Inexistent"]; - kEmptyCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage - interface:kService - method:@"EmptyCall"]; - kUnaryCallMethod = [[GRPCMethodName alloc] initWithPackage:kPackage - interface:kService - method:@"UnaryCall"]; + kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"Inexistent"]; + kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"EmptyCall"]; + kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"UnaryCall"]; } - (void)testConnectionToRemoteServer { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress - method:kInexistentMethod + path:kInexistentMethod.HTTPPath requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { @@ -95,7 +95,7 @@ static GRPCMethodName *kUnaryCallMethod; __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress - method:kEmptyCallMethod + path:kEmptyCallMethod.HTTPPath requestsWriter:[GRXWriter writerWithValue:[NSData data]]]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { @@ -123,7 +123,7 @@ static GRPCMethodName *kUnaryCallMethod; id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress - method:kUnaryCallMethod + path:kUnaryCallMethod.HTTPPath requestsWriter:requestsWriter]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { @@ -153,7 +153,7 @@ static GRPCMethodName *kUnaryCallMethod; id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress - method:kUnaryCallMethod + path:kUnaryCallMethod.HTTPPath requestsWriter:requestsWriter]; call.requestMetadata[@"Authorization"] = @"Bearer bogusToken"; diff --git a/src/objective-c/tests/LocalClearTextTests.m b/src/objective-c/tests/LocalClearTextTests.m index 05cc10410a..10c9f13ea3 100644 --- a/src/objective-c/tests/LocalClearTextTests.m +++ b/src/objective-c/tests/LocalClearTextTests.m @@ -35,7 +35,7 @@ #import <XCTest/XCTest.h> #import <GRPCClient/GRPCCall.h> -#import <GRPCClient/GRPCMethodName.h> +#import <ProtoRPC/ProtoMethod.h> #import <RouteGuide/RouteGuide.pbobjc.h> #import <RouteGuide/RouteGuide.pbrpc.h> #import <RxLibrary/GRXWriteable.h> @@ -87,14 +87,14 @@ static NSString * const kService = @"RouteGuide"; __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."]; __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; - GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage - interface:kService - method:@"RecordRoute"]; + ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"RecordRoute"]; id<GRXWriter> requestsWriter = [GRXWriter emptyWriter]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost - method:method + path:method.HTTPPath requestsWriter:requestsWriter]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { @@ -115,9 +115,9 @@ static NSString * const kService = @"RouteGuide"; __weak XCTestExpectation *response = [self expectationWithDescription:@"Response received."]; __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; - GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:kPackage - interface:kService - method:@"GetFeature"]; + ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:kPackage + service:kService + method:@"GetFeature"]; RGDPoint *point = [RGDPoint message]; point.latitude = 28E7; @@ -125,7 +125,7 @@ static NSString * const kService = @"RouteGuide"; id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[point data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kRouteGuideHost - method:method + path:method.HTTPPath requestsWriter:requestsWriter]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb index 0ff8bb9aa7..6dd0234489 100644 --- a/src/ruby/ext/grpc/extconf.rb +++ b/src/ruby/ext/grpc/extconf.rb @@ -89,7 +89,7 @@ $CFLAGS << ' -Wno-return-type ' $CFLAGS << ' -Wall ' $CFLAGS << ' -pedantic ' -$LDFLAGS << ' -lgrpc -lgpr -ldl' +$LDFLAGS << ' -lgrpc -lgpr -lz -ldl' crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy') have_library('grpc', 'grpc_channel_destroy') |