aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lib')
-rw-r--r--src/core/lib/channel/channel_args.c26
-rw-r--r--src/core/lib/channel/channel_args.h2
-rw-r--r--src/core/lib/channel/channel_stack.c33
-rw-r--r--src/core/lib/channel/channel_stack.h43
-rw-r--r--src/core/lib/channel/channel_stack_builder.c4
-rw-r--r--src/core/lib/channel/compress_filter.c53
-rw-r--r--src/core/lib/channel/compress_filter.h6
-rw-r--r--src/core/lib/channel/connected_channel.c12
-rw-r--r--src/core/lib/channel/http_client_filter.c50
-rw-r--r--src/core/lib/channel/http_client_filter.h2
-rw-r--r--src/core/lib/channel/http_server_filter.c33
-rw-r--r--src/core/lib/compression/compression.c (renamed from src/core/lib/compression/compression_algorithm.c)44
-rw-r--r--src/core/lib/debug/trace.c8
-rw-r--r--src/core/lib/http/httpcli.c140
-rw-r--r--src/core/lib/http/httpcli.h26
-rw-r--r--src/core/lib/http/httpcli_security_connector.c6
-rw-r--r--src/core/lib/http/parser.c246
-rw-r--r--src/core/lib/http/parser.c.orig357
-rw-r--r--src/core/lib/http/parser.h17
-rw-r--r--src/core/lib/iomgr/closure.c36
-rw-r--r--src/core/lib/iomgr/closure.h34
-rw-r--r--src/core/lib/iomgr/endpoint.h2
-rw-r--r--src/core/lib/iomgr/endpoint_pair_posix.c4
-rw-r--r--src/core/lib/iomgr/error.c578
-rw-r--r--src/core/lib/iomgr/error.h196
-rw-r--r--src/core/lib/iomgr/ev_epoll_linux.c1872
-rw-r--r--src/core/lib/iomgr/ev_epoll_linux.h (renamed from src/core/lib/surface/surface_trace.h)23
-rw-r--r--src/core/lib/iomgr/ev_poll_and_epoll_posix.c2043
-rw-r--r--src/core/lib/iomgr/ev_poll_and_epoll_posix.h41
-rw-r--r--src/core/lib/iomgr/ev_poll_posix.c151
-rw-r--r--src/core/lib/iomgr/ev_posix.c34
-rw-r--r--src/core/lib/iomgr/ev_posix.h21
-rw-r--r--src/core/lib/iomgr/exec_ctx.c15
-rw-r--r--src/core/lib/iomgr/exec_ctx.h6
-rw-r--r--src/core/lib/iomgr/executor.c4
-rw-r--r--src/core/lib/iomgr/executor.h2
-rw-r--r--src/core/lib/iomgr/iocp_windows.c43
-rw-r--r--src/core/lib/iomgr/iocp_windows.h8
-rw-r--r--src/core/lib/iomgr/iomgr.c9
-rw-r--r--src/core/lib/iomgr/iomgr_windows.c2
-rw-r--r--src/core/lib/iomgr/load_file.c (renamed from src/core/lib/support/load_file.c)32
-rw-r--r--src/core/lib/iomgr/load_file.h (renamed from src/core/lib/support/load_file.h)15
-rw-r--r--src/core/lib/iomgr/network_status_tracker.c121
-rw-r--r--src/core/lib/iomgr/network_status_tracker.h41
-rw-r--r--src/core/lib/iomgr/polling_entity.c104
-rw-r--r--src/core/lib/iomgr/polling_entity.h81
-rw-r--r--src/core/lib/iomgr/pollset.h11
-rw-r--r--src/core/lib/iomgr/pollset_set_windows.c5
-rw-r--r--src/core/lib/iomgr/pollset_windows.c16
-rw-r--r--src/core/lib/iomgr/resolve_address.h14
-rw-r--r--src/core/lib/iomgr/resolve_address_posix.c93
-rw-r--r--src/core/lib/iomgr/resolve_address_windows.c88
-rw-r--r--src/core/lib/iomgr/sockaddr.h4
-rw-r--r--src/core/lib/iomgr/sockaddr_windows.h (renamed from src/core/lib/iomgr/sockaddr_win32.h)6
-rw-r--r--src/core/lib/iomgr/socket_utils_common_posix.c159
-rw-r--r--src/core/lib/iomgr/socket_utils_posix.h34
-rw-r--r--src/core/lib/iomgr/socket_windows.c63
-rw-r--r--src/core/lib/iomgr/socket_windows.h13
-rw-r--r--src/core/lib/iomgr/tcp_client_posix.c93
-rw-r--r--src/core/lib/iomgr/tcp_client_windows.c68
-rw-r--r--src/core/lib/iomgr/tcp_posix.c78
-rw-r--r--src/core/lib/iomgr/tcp_server.h10
-rw-r--r--src/core/lib/iomgr/tcp_server_posix.c265
-rw-r--r--src/core/lib/iomgr/tcp_server_windows.c211
-rw-r--r--src/core/lib/iomgr/tcp_windows.c100
-rw-r--r--src/core/lib/iomgr/tcp_windows.h2
-rw-r--r--src/core/lib/iomgr/timer.c39
-rw-r--r--src/core/lib/iomgr/udp_server.c14
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix.c15
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix.h3
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix_noop.c6
-rw-r--r--src/core/lib/iomgr/wakeup_fd_eventfd.c26
-rw-r--r--src/core/lib/iomgr/wakeup_fd_pipe.c22
-rw-r--r--src/core/lib/iomgr/wakeup_fd_posix.c12
-rw-r--r--src/core/lib/iomgr/wakeup_fd_posix.h15
-rw-r--r--src/core/lib/iomgr/workqueue.h9
-rw-r--r--src/core/lib/iomgr/workqueue_posix.c66
-rw-r--r--src/core/lib/iomgr/workqueue_windows.c4
-rw-r--r--src/core/lib/profiling/basic_timers.c8
-rw-r--r--src/core/lib/security/credentials/composite/composite_credentials.c20
-rw-r--r--src/core/lib/security/credentials/credentials.c8
-rw-r--r--src/core/lib/security/credentials/credentials.h16
-rw-r--r--src/core/lib/security/credentials/fake/fake_credentials.c13
-rw-r--r--src/core/lib/security/credentials/google_default/credentials_windows.c (renamed from src/core/lib/security/credentials/google_default/credentials_win32.c)4
-rw-r--r--src/core/lib/security/credentials/google_default/google_default_credentials.c124
-rw-r--r--src/core/lib/security/credentials/iam/iam_credentials.c4
-rw-r--r--src/core/lib/security/credentials/jwt/jwt_credentials.c15
-rw-r--r--src/core/lib/security/credentials/jwt/jwt_verifier.c46
-rw-r--r--src/core/lib/security/credentials/oauth2/oauth2_credentials.c45
-rw-r--r--src/core/lib/security/credentials/oauth2/oauth2_credentials.h4
-rw-r--r--src/core/lib/security/credentials/plugin/plugin_credentials.c9
-rw-r--r--src/core/lib/security/transport/client_auth_filter.c52
-rw-r--r--src/core/lib/security/transport/handshake.c116
-rw-r--r--src/core/lib/security/transport/handshake.h12
-rw-r--r--src/core/lib/security/transport/secure_endpoint.c29
-rw-r--r--src/core/lib/security/transport/security_connector.c33
-rw-r--r--src/core/lib/security/transport/security_connector.h10
-rw-r--r--src/core/lib/security/transport/server_auth_filter.c34
-rw-r--r--src/core/lib/security/transport/tsi_error.c40
-rw-r--r--src/core/lib/security/transport/tsi_error.h42
-rw-r--r--src/core/lib/support/avl.c11
-rw-r--r--src/core/lib/support/cpu_windows.c4
-rw-r--r--src/core/lib/support/env_windows.c (renamed from src/core/lib/support/env_win32.c)6
-rw-r--r--src/core/lib/support/log.c4
-rw-r--r--src/core/lib/support/log_linux.c4
-rw-r--r--src/core/lib/support/log_windows.c (renamed from src/core/lib/support/log_win32.c)8
-rw-r--r--src/core/lib/support/string.c10
-rw-r--r--src/core/lib/support/string.h4
-rw-r--r--src/core/lib/support/string_util_windows.c (renamed from src/core/lib/support/string_util_win32.c)4
-rw-r--r--src/core/lib/support/string_windows.c (renamed from src/core/lib/support/string_win32.c)4
-rw-r--r--src/core/lib/support/string_windows.h (renamed from src/core/lib/support/string_win32.h)10
-rw-r--r--src/core/lib/support/subprocess_windows.c2
-rw-r--r--src/core/lib/support/sync_windows.c (renamed from src/core/lib/support/sync_win32.c)4
-rw-r--r--src/core/lib/support/thd_windows.c (renamed from src/core/lib/support/thd_win32.c)4
-rw-r--r--src/core/lib/support/time_windows.c (renamed from src/core/lib/support/time_win32.c)4
-rw-r--r--src/core/lib/support/tmpfile_msys.c2
-rw-r--r--src/core/lib/support/tmpfile_windows.c (renamed from src/core/lib/support/tmpfile_win32.c)6
-rw-r--r--src/core/lib/surface/alarm.c4
-rw-r--r--src/core/lib/surface/byte_buffer_reader.c10
-rw-r--r--src/core/lib/surface/call.c522
-rw-r--r--src/core/lib/surface/call.h3
-rw-r--r--src/core/lib/surface/call_log_batch.c2
-rw-r--r--src/core/lib/surface/channel.c85
-rw-r--r--src/core/lib/surface/channel.h9
-rw-r--r--src/core/lib/surface/channel_ping.c6
-rw-r--r--src/core/lib/surface/completion_queue.c104
-rw-r--r--src/core/lib/surface/completion_queue.h8
-rw-r--r--src/core/lib/surface/init.c8
-rw-r--r--src/core/lib/surface/lame_client.c20
-rw-r--r--src/core/lib/surface/server.c128
-rw-r--r--src/core/lib/surface/version.c2
-rw-r--r--src/core/lib/transport/connectivity_state.c58
-rw-r--r--src/core/lib/transport/connectivity_state.h5
-rw-r--r--src/core/lib/transport/metadata.c43
-rw-r--r--src/core/lib/transport/static_metadata.c11
-rw-r--r--src/core/lib/transport/static_metadata.h124
-rw-r--r--src/core/lib/transport/transport.c125
-rw-r--r--src/core/lib/transport/transport.h24
-rw-r--r--src/core/lib/transport/transport_impl.h4
-rw-r--r--src/core/lib/transport/transport_op_string.c19
140 files changed, 8503 insertions, 1786 deletions
diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
index 569be4dc28..79ceeb66b3 100644
--- a/src/core/lib/channel/channel_args.c
+++ b/src/core/lib/channel/channel_args.c
@@ -35,6 +35,7 @@
#include <grpc/grpc.h>
#include "src/core/lib/support/string.h"
+#include <grpc/compression.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
@@ -148,6 +149,7 @@ grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a) {
void grpc_channel_args_destroy(grpc_channel_args *a) {
size_t i;
+ if (!a) return;
for (i = 0; i < a->num_args; i++) {
switch (a->args[i].type) {
case GRPC_ARG_STRING:
@@ -181,6 +183,7 @@ grpc_compression_algorithm grpc_channel_args_get_compression_algorithm(
grpc_channel_args *grpc_channel_args_set_compression_algorithm(
grpc_channel_args *a, grpc_compression_algorithm algorithm) {
+ GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT);
grpc_arg tmp;
tmp.type = GRPC_ARG_INTEGER;
tmp.key = GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM;
@@ -200,7 +203,8 @@ static int find_compression_algorithm_states_bitset(const grpc_channel_args *a,
!strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET,
a->args[i].key)) {
*states_arg = &a->args[i].value.integer;
- return 1; /* GPR_TRUE */
+ **states_arg |= 0x1; /* forcefully enable support for no compression */
+ return 1;
}
}
}
@@ -209,15 +213,23 @@ static int find_compression_algorithm_states_bitset(const grpc_channel_args *a,
grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
grpc_channel_args **a, grpc_compression_algorithm algorithm, int state) {
- int *states_arg;
+ int *states_arg = NULL;
grpc_channel_args *result = *a;
const int states_arg_found =
find_compression_algorithm_states_bitset(*a, &states_arg);
- if (states_arg_found) {
+ if (grpc_channel_args_get_compression_algorithm(*a) == algorithm &&
+ state == 0) {
+ char *algo_name = NULL;
+ GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0);
+ gpr_log(GPR_ERROR,
+ "Tried to disable default compression algorithm '%s'. The "
+ "operation has been ignored.",
+ algo_name);
+ } else if (states_arg_found) {
if (state != 0) {
GPR_BITSET((unsigned *)states_arg, algorithm);
- } else {
+ } else if (algorithm != GRPC_COMPRESS_NONE) {
GPR_BITCLEAR((unsigned *)states_arg, algorithm);
}
} else {
@@ -229,7 +241,7 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
if (state != 0) {
GPR_BITSET((unsigned *)&tmp.value.integer, algorithm);
- } else {
+ } else if (algorithm != GRPC_COMPRESS_NONE) {
GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm);
}
result = grpc_channel_args_copy_and_add(*a, &tmp, 1);
@@ -239,11 +251,11 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
return result;
}
-int grpc_channel_args_compression_algorithm_get_states(
+uint32_t grpc_channel_args_compression_algorithm_get_states(
const grpc_channel_args *a) {
int *states_arg;
if (find_compression_algorithm_states_bitset(a, &states_arg)) {
- return *states_arg;
+ return (uint32_t)*states_arg;
} else {
return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */
}
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index 23c7b7b897..653d04f427 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -81,7 +81,7 @@ grpc_channel_args *grpc_channel_args_compression_algorithm_set_state(
*
* The i-th bit of the returned bitset corresponds to the i-th entry in the
* grpc_compression_algorithm enum. */
-int grpc_channel_args_compression_algorithm_get_states(
+uint32_t grpc_channel_args_compression_algorithm_get_states(
const grpc_channel_args *a);
int grpc_channel_args_compare(const grpc_channel_args *a,
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
index ad182d1f69..87175d7943 100644
--- a/src/core/lib/channel/channel_stack.c
+++ b/src/core/lib/channel/channel_stack.c
@@ -106,6 +106,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
const grpc_channel_filter **filters,
size_t filter_count,
const grpc_channel_args *channel_args,
+ grpc_transport *optional_transport,
const char *name, grpc_channel_stack *stack) {
size_t call_size =
ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) +
@@ -127,6 +128,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
for (i = 0; i < filter_count; i++) {
args.channel_stack = stack;
args.channel_args = channel_args;
+ args.optional_transport = optional_transport;
args.is_first = i == 0;
args.is_last = i == (filter_count - 1);
elems[i].filter = filters[i];
@@ -189,9 +191,9 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
}
}
-void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_call_stack *call_stack,
- grpc_pollset *pollset) {
+void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_call_stack *call_stack,
+ grpc_polling_entity *pollent) {
size_t count = call_stack->count;
grpc_call_element *call_elems;
char *user_data;
@@ -203,17 +205,19 @@ void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
/* init per-filter data */
for (i = 0; i < count; i++) {
- call_elems[i].filter->set_pollset(exec_ctx, &call_elems[i], pollset);
+ call_elems[i].filter->set_pollset_or_pollset_set(exec_ctx, &call_elems[i],
+ pollent);
user_data +=
ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data);
}
}
-void grpc_call_stack_ignore_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_call_element *elem,
- grpc_pollset *pollset) {}
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(
+ grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ grpc_polling_entity *pollent) {}
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
+ const grpc_call_stats *call_stats,
void *and_free_memory) {
grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack);
size_t count = stack->count;
@@ -221,7 +225,7 @@ void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
/* destroy per-filter data */
for (i = 0; i < count; i++) {
- elems[i].filter->destroy_call_elem(exec_ctx, &elems[i],
+ elems[i].filter->destroy_call_elem(exec_ctx, &elems[i], call_stats,
i == count - 1 ? and_free_memory : NULL);
}
}
@@ -259,6 +263,17 @@ void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
grpc_call_element *cur_elem) {
grpc_transport_stream_op op;
memset(&op, 0, sizeof(op));
- op.cancel_with_status = GRPC_STATUS_CANCELLED;
+ op.cancel_error = GRPC_ERROR_CANCELLED;
+ grpc_call_next_op(exec_ctx, cur_elem, &op);
+}
+
+void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *cur_elem,
+ grpc_status_code status,
+ gpr_slice *optional_message) {
+ grpc_transport_stream_op op;
+ memset(&op, 0, sizeof(op));
+ grpc_transport_stream_op_add_cancellation_with_message(&op, status,
+ optional_message);
grpc_call_next_op(exec_ctx, cur_elem, &op);
}
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index 36c17cb467..d72c015b67 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -45,7 +45,10 @@
#include <grpc/grpc.h>
#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/transport/transport.h"
typedef struct grpc_channel_element grpc_channel_element;
@@ -57,6 +60,8 @@ typedef struct grpc_call_stack grpc_call_stack;
typedef struct {
grpc_channel_stack *channel_stack;
const grpc_channel_args *channel_args;
+ /** Transport, iff it is known */
+ grpc_transport *optional_transport;
int is_first;
int is_last;
} grpc_channel_element_args;
@@ -67,6 +72,12 @@ typedef struct {
grpc_call_context_element *context;
} grpc_call_element_args;
+typedef struct {
+ grpc_transport_stream_stats transport_stream_stats;
+ gpr_timespec latency; /* From call creating to enqueing of received status */
+ grpc_status_code final_status;
+} grpc_call_stats;
+
/* Channel filters specify:
1. the amount of memory needed in the channel & call (via the sizeof_XXX
members)
@@ -101,14 +112,16 @@ typedef struct {
argument. */
void (*init_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_call_element_args *args);
- void (*set_pollset)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- grpc_pollset *pollset);
+ void (*set_pollset_or_pollset_set)(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_polling_entity *pollent);
/* Destroy per call data.
The filter does not need to do any chaining.
The bottom filter of a stack will be passed a non-NULL pointer to
\a and_free_memory that should be passed to gpr_free when destruction
is complete. */
void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ const grpc_call_stats *stats,
void *and_free_memory);
/* sizeof(per channel data) */
@@ -187,6 +200,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs,
grpc_iomgr_cb_func destroy, void *destroy_arg,
const grpc_channel_filter **filters,
size_t filter_count, const grpc_channel_args *args,
+ grpc_transport *optional_transport,
const char *name, grpc_channel_stack *stack);
/* Destroy a channel stack */
void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx,
@@ -201,10 +215,11 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx,
grpc_call_context_element *context,
const void *transport_server_data,
grpc_call_stack *call_stack);
-/* Set a pollset for a call stack: must occur before the first op is started */
-void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_call_stack *call_stack,
- grpc_pollset *pollset);
+/* Set a pollset or a pollset_set for a call stack: must occur before the first
+ * op is started */
+void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_call_stack *call_stack,
+ grpc_polling_entity *pollent);
#ifdef GRPC_STREAM_REFCOUNT_DEBUG
#define GRPC_CALL_STACK_REF(call_stack, reason) \
@@ -228,13 +243,14 @@ void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx,
/* Destroy a call stack */
void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
+ const grpc_call_stats *call_stats,
void *and_free_memory);
-/* Ignore set pollset - used by filters to implement the set_pollset method
- if they don't care about pollsets at all. Does nothing. */
-void grpc_call_stack_ignore_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_call_element *elem,
- grpc_pollset *pollset);
+/* Ignore set pollset{_set} - used by filters if they don't care about pollsets
+ * at all. Does nothing. */
+void grpc_call_stack_ignore_set_pollset_or_pollset_set(
+ grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ grpc_polling_entity *pollent);
/* Call the next operation in a call stack */
void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op);
@@ -257,6 +273,11 @@ void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
void grpc_call_element_send_cancel(grpc_exec_ctx *exec_ctx,
grpc_call_element *cur_elem);
+void grpc_call_element_send_cancel_with_message(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *cur_elem,
+ grpc_status_code status,
+ gpr_slice *optional_message);
+
extern int grpc_trace_channel;
#define GRPC_CALL_LOG_OP(sev, elem, op) \
diff --git a/src/core/lib/channel/channel_stack_builder.c b/src/core/lib/channel/channel_stack_builder.c
index a8646c9565..eda4968f48 100644
--- a/src/core/lib/channel/channel_stack_builder.c
+++ b/src/core/lib/channel/channel_stack_builder.c
@@ -257,8 +257,8 @@ void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
// and initialize it
grpc_channel_stack_init(exec_ctx, initial_refs, destroy,
destroy_arg == NULL ? result : destroy_arg, filters,
- num_filters, builder->args, builder->name,
- channel_stack);
+ num_filters, builder->args, builder->transport,
+ builder->name, channel_stack);
// run post-initialization functions
i = 0;
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 0e548c61b8..32ebe53ee6 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -73,8 +73,8 @@ typedef struct call_data {
typedef struct channel_data {
/** The default, channel-level, compression algorithm */
grpc_compression_algorithm default_compression_algorithm;
- /** Compression options for the channel */
- grpc_compression_options compression_options;
+ /** Bitset of enabled algorithms */
+ uint32_t enabled_algorithms_bitset;
/** Supported compression algorithms */
uint32_t supported_compression_algorithms;
} channel_data;
@@ -96,9 +96,8 @@ static grpc_mdelem *compression_md_filter(void *user_data, grpc_mdelem *md) {
md_c_str);
calld->compression_algorithm = GRPC_COMPRESS_NONE;
}
- if (grpc_compression_options_is_algorithm_enabled(
- &channeld->compression_options, calld->compression_algorithm) ==
- 0) {
+ if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
+ calld->compression_algorithm)) {
gpr_log(GPR_ERROR,
"Invalid compression algorithm: '%s' (previously disabled). "
"Ignoring.",
@@ -155,11 +154,11 @@ static void process_send_initial_metadata(
static void continue_send_message(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem);
-static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
+static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
grpc_call_element *elem = elemp;
call_data *calld = elem->call_data;
gpr_slice_buffer_reset_and_unref(&calld->slices);
- calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, success);
+ calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
}
static void finish_send_message(grpc_exec_ctx *exec_ctx,
@@ -178,8 +177,8 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
const float savings_ratio = 1.0f - (float)after_size / (float)before_size;
GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
&algo_name));
- gpr_log(GPR_DEBUG,
- "Compressed[%s] %d bytes vs. %d bytes (%.2f%% savings)",
+ gpr_log(GPR_DEBUG, "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR
+ " bytes (%.2f%% savings)",
algo_name, before_size, after_size, 100 * savings_ratio);
}
gpr_slice_buffer_swap(&calld->slices, &tmp);
@@ -189,10 +188,10 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
char *algo_name;
GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
&algo_name));
- gpr_log(
- GPR_DEBUG,
- "Algorithm '%s' enabled but decided not to compress. Input size: %d",
- algo_name, calld->slices.length);
+ gpr_log(GPR_DEBUG,
+ "Algorithm '%s' enabled but decided not to compress. Input size: "
+ "%" PRIuPTR,
+ algo_name, calld->slices.length);
}
}
@@ -207,7 +206,7 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
grpc_call_next_op(exec_ctx, elem, &calld->send_op);
}
-static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
+static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
grpc_call_element *elem = elemp;
call_data *calld = elem->call_data;
gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
@@ -271,7 +270,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {
+ const grpc_call_stats *stats, void *ignored) {
/* grab pointers to our data from the call element */
call_data *calld = elem->call_data;
gpr_slice_buffer_destroy(&calld->slices);
@@ -282,32 +281,26 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_channel_element_args *args) {
channel_data *channeld = elem->channel_data;
- grpc_compression_algorithm algo_idx;
- grpc_compression_options_init(&channeld->compression_options);
- channeld->compression_options.enabled_algorithms_bitset =
- (uint32_t)grpc_channel_args_compression_algorithm_get_states(
- args->channel_args);
+ channeld->enabled_algorithms_bitset =
+ grpc_channel_args_compression_algorithm_get_states(args->channel_args);
channeld->default_compression_algorithm =
grpc_channel_args_get_compression_algorithm(args->channel_args);
/* Make sure the default isn't disabled. */
- if (!grpc_compression_options_is_algorithm_enabled(
- &channeld->compression_options,
- channeld->default_compression_algorithm)) {
+ if (!GPR_BITGET(channeld->enabled_algorithms_bitset,
+ channeld->default_compression_algorithm)) {
gpr_log(GPR_DEBUG,
"compression algorithm %d not enabled: switching to none",
channeld->default_compression_algorithm);
channeld->default_compression_algorithm = GRPC_COMPRESS_NONE;
}
- channeld->compression_options.default_compression_algorithm =
- channeld->default_compression_algorithm;
- channeld->supported_compression_algorithms = 0;
- for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
+ channeld->supported_compression_algorithms = 1; /* always support identity */
+ for (grpc_compression_algorithm algo_idx = 1;
+ algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) {
/* skip disabled algorithms */
- if (grpc_compression_options_is_algorithm_enabled(
- &channeld->compression_options, algo_idx) == 0) {
+ if (!GPR_BITGET(channeld->enabled_algorithms_bitset, algo_idx)) {
continue;
}
channeld->supported_compression_algorithms |= 1u << algo_idx;
@@ -325,7 +318,7 @@ const grpc_channel_filter grpc_compress_filter = {
grpc_channel_next_op,
sizeof(call_data),
init_call_elem,
- grpc_call_stack_ignore_set_pollset,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
diff --git a/src/core/lib/channel/compress_filter.h b/src/core/lib/channel/compress_filter.h
index 0ce5d08837..e4a2a829d5 100644
--- a/src/core/lib/channel/compress_filter.h
+++ b/src/core/lib/channel/compress_filter.h
@@ -34,9 +34,9 @@
#ifndef GRPC_CORE_LIB_CHANNEL_COMPRESS_FILTER_H
#define GRPC_CORE_LIB_CHANNEL_COMPRESS_FILTER_H
-#include "src/core/lib/channel/channel_stack.h"
+#include <grpc/impl/codegen/compression_types.h>
-#define GRPC_COMPRESS_REQUEST_ALGORITHM_KEY "grpc-internal-encoding-request"
+#include "src/core/lib/channel/channel_stack.h"
extern int grpc_compression_trace;
@@ -48,7 +48,7 @@ extern int grpc_compression_trace;
* - Channel configuration, as established at channel creation time.
* - The metadata accompanying the outgoing data to be compressed. This is
* taken as a request only. We may choose not to honor it. The metadata key
- * is given by \a GRPC_COMPRESS_REQUEST_ALGORITHM_KEY.
+ * is given by \a GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY.
*
* Compression can be disabled for concrete messages (for instance in order to
* prevent CRIME/BEAST type attacks) by having the GRPC_WRITE_NO_COMPRESS set in
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
index 68a3a7d6fd..0a7d27a1dc 100644
--- a/src/core/lib/channel/connected_channel.c
+++ b/src/core/lib/channel/connected_channel.c
@@ -93,16 +93,18 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
GPR_ASSERT(r == 0);
}
-static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- grpc_pollset *pollset) {
+static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_polling_entity *pollent) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
- grpc_transport_set_pollset(exec_ctx, chand->transport,
- TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollset);
+ grpc_transport_set_pops(exec_ctx, chand->transport,
+ TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollent);
}
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ const grpc_call_stats *stats,
void *and_free_memory) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
@@ -137,7 +139,7 @@ static const grpc_channel_filter connected_channel_filter = {
con_start_transport_op,
sizeof(call_data),
init_call_elem,
- set_pollset,
+ set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index 516e708d1f..8057e251f0 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -38,6 +38,10 @@
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/transport_impl.h"
+
+#define EXPECTED_CONTENT_TYPE "application/grpc"
+#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1
typedef struct call_data {
grpc_linked_mdelem method;
@@ -72,15 +76,39 @@ static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) {
if (md == GRPC_MDELEM_STATUS_200) {
return NULL;
} else if (md->key == GRPC_MDSTR_STATUS) {
- grpc_call_element_send_cancel(a->exec_ctx, a->elem);
+ char *message_string;
+ gpr_asprintf(&message_string, "Received http2 header with status: %s",
+ grpc_mdstr_as_c_string(md->value));
+ gpr_slice message = gpr_slice_from_copied_string(message_string);
+ gpr_free(message_string);
+ grpc_call_element_send_cancel_with_message(a->exec_ctx, a->elem,
+ GRPC_STATUS_CANCELLED, &message);
+ return NULL;
+ } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) {
return NULL;
} else if (md->key == GRPC_MDSTR_CONTENT_TYPE) {
+ const char *value_str = grpc_mdstr_as_c_string(md->value);
+ if (strncmp(value_str, EXPECTED_CONTENT_TYPE,
+ EXPECTED_CONTENT_TYPE_LENGTH) == 0 &&
+ (value_str[EXPECTED_CONTENT_TYPE_LENGTH] == '+' ||
+ value_str[EXPECTED_CONTENT_TYPE_LENGTH] == ';')) {
+ /* Although the C implementation doesn't (currently) generate them,
+ any custom +-suffix is explicitly valid. */
+ /* TODO(klempner): We should consider preallocating common values such
+ as +proto or +json, or at least stashing them if we see them. */
+ /* TODO(klempner): Should we be surfacing this to application code? */
+ } else {
+ /* TODO(klempner): We're currently allowing this, but we shouldn't
+ see it without a proxy so log for now. */
+ gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
+ }
return NULL;
}
return md;
}
-static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
+ grpc_error *error) {
grpc_call_element *elem = user_data;
call_data *calld = elem->call_data;
client_recv_filter_args a;
@@ -88,7 +116,7 @@ static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
a.exec_ctx = exec_ctx;
grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
&a);
- calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+ calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
}
static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
@@ -156,7 +184,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {}
+ const grpc_call_stats *stats, void *ignored) {}
static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
unsigned i;
@@ -179,7 +207,8 @@ static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) {
return GRPC_MDELEM_SCHEME_HTTP;
}
-static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args) {
+static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args,
+ const char *transport_name) {
gpr_strvec v;
size_t i;
int is_first = 1;
@@ -201,8 +230,8 @@ static grpc_mdstr *user_agent_from_args(const grpc_channel_args *args) {
}
}
- gpr_asprintf(&tmp, "%sgrpc-c/%s (%s)", is_first ? "" : " ",
- grpc_version_string(), GPR_PLATFORM_STRING);
+ gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s)", is_first ? "" : " ",
+ grpc_version_string(), GPR_PLATFORM_STRING, transport_name);
is_first = 0;
gpr_strvec_add(&v, tmp);
@@ -233,9 +262,12 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element_args *args) {
channel_data *chand = elem->channel_data;
GPR_ASSERT(!args->is_last);
+ GPR_ASSERT(args->optional_transport != NULL);
chand->static_scheme = scheme_from_args(args->channel_args);
chand->user_agent = grpc_mdelem_from_metadata_strings(
- GRPC_MDSTR_USER_AGENT, user_agent_from_args(args->channel_args));
+ GRPC_MDSTR_USER_AGENT,
+ user_agent_from_args(args->channel_args,
+ args->optional_transport->vtable->name));
}
/* Destructor for channel data */
@@ -250,7 +282,7 @@ const grpc_channel_filter grpc_http_client_filter = {
grpc_channel_next_op,
sizeof(call_data),
init_call_elem,
- grpc_call_stack_ignore_set_pollset,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
diff --git a/src/core/lib/channel/http_client_filter.h b/src/core/lib/channel/http_client_filter.h
index a884b36318..47081175ea 100644
--- a/src/core/lib/channel/http_client_filter.h
+++ b/src/core/lib/channel/http_client_filter.h
@@ -1,5 +1,4 @@
/*
- *
* Copyright 2015, Google Inc.
* All rights reserved.
*
@@ -39,6 +38,7 @@
/* Processes metadata on the client side for HTTP2 transports */
extern const grpc_channel_filter grpc_http_client_filter;
+/* Channel arg to override the http2 :scheme header */
#define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme"
#endif /* GRPC_CORE_LIB_CHANNEL_HTTP_CLIENT_FILTER_H */
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index ba865416de..d0beebd817 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -108,7 +108,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
} else {
/* TODO(klempner): We're currently allowing this, but we shouldn't
see it without a proxy so log for now. */
- gpr_log(GPR_INFO, "Unexpected content-type %s", value_str);
+ gpr_log(GPR_INFO, "Unexpected content-type '%s'", value_str);
}
return NULL;
} else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD ||
@@ -142,10 +142,11 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
}
}
-static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
+ grpc_error *err) {
grpc_call_element *elem = user_data;
call_data *calld = elem->call_data;
- if (success) {
+ if (err == GRPC_ERROR_NONE) {
server_filter_args a;
a.elem = elem;
a.exec_ctx = exec_ctx;
@@ -157,27 +158,35 @@ static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
calld->seen_path && calld->seen_authority) {
/* do nothing */
} else {
+ err = GRPC_ERROR_CREATE("Bad incoming HTTP headers");
if (!calld->seen_path) {
- gpr_log(GPR_ERROR, "Missing :path header");
+ err = grpc_error_add_child(err,
+ GRPC_ERROR_CREATE("Missing :path header"));
}
if (!calld->seen_authority) {
- gpr_log(GPR_ERROR, "Missing :authority header");
+ err = grpc_error_add_child(
+ err, GRPC_ERROR_CREATE("Missing :authority header"));
}
if (!calld->seen_method) {
- gpr_log(GPR_ERROR, "Missing :method header");
+ err = grpc_error_add_child(err,
+ GRPC_ERROR_CREATE("Missing :method header"));
}
if (!calld->seen_scheme) {
- gpr_log(GPR_ERROR, "Missing :scheme header");
+ err = grpc_error_add_child(err,
+ GRPC_ERROR_CREATE("Missing :scheme header"));
}
if (!calld->seen_te_trailers) {
- gpr_log(GPR_ERROR, "Missing te trailers header");
+ err = grpc_error_add_child(
+ err, GRPC_ERROR_CREATE("Missing te: trailers header"));
}
/* Error this call out */
- success = 0;
grpc_call_element_send_cancel(exec_ctx, elem);
}
+ } else {
+ GRPC_ERROR_REF(err);
}
- calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+ calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, err);
+ GRPC_ERROR_UNREF(err);
}
static void hs_mutate_op(grpc_call_element *elem,
@@ -226,7 +235,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {}
+ const grpc_call_stats *stats, void *ignored) {}
/* Constructor for channel_data */
static void init_channel_elem(grpc_exec_ctx *exec_ctx,
@@ -244,7 +253,7 @@ const grpc_channel_filter grpc_http_server_filter = {
grpc_channel_next_op,
sizeof(call_data),
init_call_elem,
- grpc_call_stack_ignore_set_pollset,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
diff --git a/src/core/lib/compression/compression_algorithm.c b/src/core/lib/compression/compression.c
index 820871d579..54efb5e855 100644
--- a/src/core/lib/compression/compression_algorithm.c
+++ b/src/core/lib/compression/compression.c
@@ -125,6 +125,28 @@ grpc_mdelem *grpc_compression_encoding_mdelem(
return NULL;
}
+void grpc_compression_options_init(grpc_compression_options *opts) {
+ memset(opts, 0, sizeof(*opts));
+ /* all enabled by default */
+ opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
+}
+
+void grpc_compression_options_enable_algorithm(
+ grpc_compression_options *opts, grpc_compression_algorithm algorithm) {
+ GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm);
+}
+
+void grpc_compression_options_disable_algorithm(
+ grpc_compression_options *opts, grpc_compression_algorithm algorithm) {
+ GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm);
+}
+
+int grpc_compression_options_is_algorithm_enabled(
+ const grpc_compression_options *opts,
+ grpc_compression_algorithm algorithm) {
+ return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm);
+}
+
/* TODO(dgq): Add the ability to specify parameters to the individual
* compression algorithms */
grpc_compression_algorithm grpc_compression_algorithm_for_level(
@@ -180,25 +202,3 @@ grpc_compression_algorithm grpc_compression_algorithm_for_level(
abort();
};
}
-
-void grpc_compression_options_init(grpc_compression_options *opts) {
- opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
- opts->default_compression_algorithm = GRPC_COMPRESS_NONE;
-}
-
-void grpc_compression_options_enable_algorithm(
- grpc_compression_options *opts, grpc_compression_algorithm algorithm) {
- GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm);
-}
-
-void grpc_compression_options_disable_algorithm(
- grpc_compression_options *opts, grpc_compression_algorithm algorithm) {
- GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm);
-}
-
-int grpc_compression_options_is_algorithm_enabled(
- const grpc_compression_options *opts,
- grpc_compression_algorithm algorithm) {
- if (algorithm >= GRPC_COMPRESS_ALGORITHMS_COUNT) return 0;
- return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm);
-}
diff --git a/src/core/lib/debug/trace.c b/src/core/lib/debug/trace.c
index 555f497b78..c56046785b 100644
--- a/src/core/lib/debug/trace.c
+++ b/src/core/lib/debug/trace.c
@@ -88,7 +88,11 @@ static void parse(const char *s) {
split(s, &strings, &nstrings);
for (i = 0; i < nstrings; i++) {
- grpc_tracer_set_enabled(strings[i], 1);
+ if (strings[i][0] == '-') {
+ grpc_tracer_set_enabled(strings[i] + 1, 0);
+ } else {
+ grpc_tracer_set_enabled(strings[i], 1);
+ }
}
for (i = 0; i < nstrings; i++) {
@@ -117,7 +121,7 @@ int grpc_tracer_set_enabled(const char *name, int enabled) {
tracer *t;
if (0 == strcmp(name, "all")) {
for (t = tracers; t; t = t->next) {
- *t->flag = 1;
+ *t->flag = enabled;
}
} else {
int found = 0;
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index f22721ac8f..18135bcb58 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -39,12 +39,14 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
#include "src/core/lib/http/format_request.h"
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "src/core/lib/iomgr/tcp_client.h"
#include "src/core/lib/support/string.h"
@@ -59,16 +61,16 @@ typedef struct {
gpr_timespec deadline;
int have_read_byte;
const grpc_httpcli_handshaker *handshaker;
- grpc_httpcli_response_cb on_response;
- void *user_data;
+ grpc_closure *on_done;
grpc_httpcli_context *context;
- grpc_pollset *pollset;
+ grpc_polling_entity *pollent;
grpc_iomgr_object iomgr_obj;
gpr_slice_buffer incoming;
gpr_slice_buffer outgoing;
grpc_closure on_read;
grpc_closure done_write;
grpc_closure connected;
+ grpc_error *overall_error;
} internal_request;
static grpc_httpcli_get_override g_get_override = NULL;
@@ -76,6 +78,7 @@ static grpc_httpcli_post_override g_post_override = NULL;
static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg,
grpc_endpoint *endpoint, const char *host,
+ gpr_timespec deadline,
void (*on_done)(grpc_exec_ctx *exec_ctx,
void *arg,
grpc_endpoint *endpoint)) {
@@ -93,14 +96,14 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context) {
grpc_pollset_set_destroy(context->pollset_set);
}
-static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req);
+static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
+ grpc_error *due_to_error);
static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
- int success) {
- grpc_pollset_set_del_pollset(exec_ctx, req->context->pollset_set,
- req->pollset);
- req->on_response(exec_ctx, req->user_data,
- success ? &req->parser.http.response : NULL);
+ grpc_error *error) {
+ grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent,
+ req->context->pollset_set);
+ grpc_exec_ctx_sched(exec_ctx, req->on_done, error, NULL);
grpc_http_parser_destroy(&req->parser);
if (req->addresses != NULL) {
grpc_resolved_addresses_destroy(req->addresses);
@@ -114,39 +117,49 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
grpc_iomgr_unregister_object(&req->iomgr_obj);
gpr_slice_buffer_destroy(&req->incoming);
gpr_slice_buffer_destroy(&req->outgoing);
+ GRPC_ERROR_UNREF(req->overall_error);
gpr_free(req);
}
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success);
+static void append_error(internal_request *req, grpc_error *error) {
+ if (req->overall_error == GRPC_ERROR_NONE) {
+ req->overall_error = GRPC_ERROR_CREATE("Failed HTTP/1 client request");
+ }
+ grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1];
+ char *addr_text = grpc_sockaddr_to_uri((struct sockaddr *)addr->addr);
+ req->overall_error = grpc_error_add_child(
+ req->overall_error,
+ grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text));
+ gpr_free(addr_text);
+}
static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) {
grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read);
}
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
+ grpc_error *error) {
internal_request *req = user_data;
size_t i;
for (i = 0; i < req->incoming.count; i++) {
if (GPR_SLICE_LENGTH(req->incoming.slices[i])) {
req->have_read_byte = 1;
- if (!grpc_http_parser_parse(&req->parser, req->incoming.slices[i])) {
- finish(exec_ctx, req, 0);
+ grpc_error *err =
+ grpc_http_parser_parse(&req->parser, req->incoming.slices[i]);
+ if (err != GRPC_ERROR_NONE) {
+ finish(exec_ctx, req, err);
return;
}
}
}
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
do_read(exec_ctx, req);
} else if (!req->have_read_byte) {
- next_address(exec_ctx, req);
+ next_address(exec_ctx, req, GRPC_ERROR_REF(error));
} else {
- int parse_success = grpc_http_parser_eof(&req->parser);
- if (parse_success && (req->parser.type != GRPC_HTTP_RESPONSE)) {
- parse_success = 0;
- }
- finish(exec_ctx, req, parse_success);
+ finish(exec_ctx, req, grpc_http_parser_eof(&req->parser));
}
}
@@ -154,12 +167,12 @@ static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) {
do_read(exec_ctx, req);
}
-static void done_write(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
internal_request *req = arg;
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
on_written(exec_ctx, req);
} else {
- next_address(exec_ctx, req);
+ next_address(exec_ctx, req, GRPC_ERROR_REF(error));
}
}
@@ -174,7 +187,8 @@ static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
internal_request *req = arg;
if (!ep) {
- next_address(exec_ctx, req);
+ next_address(exec_ctx, req,
+ GRPC_ERROR_CREATE("Unexplained handshake failure"));
return;
}
@@ -182,23 +196,30 @@ static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
start_write(exec_ctx, req);
}
-static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_connected(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
internal_request *req = arg;
if (!req->ep) {
- next_address(exec_ctx, req);
+ next_address(exec_ctx, req, GRPC_ERROR_REF(error));
return;
}
req->handshaker->handshake(
exec_ctx, req, req->ep,
req->ssl_host_override ? req->ssl_host_override : req->host,
- on_handshake_done);
+ req->deadline, on_handshake_done);
}
-static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) {
+static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
+ grpc_error *error) {
grpc_resolved_address *addr;
+ if (error != GRPC_ERROR_NONE) {
+ append_error(req, error);
+ }
if (req->next_address == req->addresses->naddrs) {
- finish(exec_ctx, req, 0);
+ finish(exec_ctx, req,
+ GRPC_ERROR_CREATE_REFERENCING("Failed HTTP requests to all targets",
+ &req->overall_error, 1));
return;
}
addr = &req->addresses->addrs[req->next_address++];
@@ -208,34 +229,34 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) {
(struct sockaddr *)&addr->addr, addr->len, req->deadline);
}
-static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
- grpc_resolved_addresses *addresses) {
+static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
internal_request *req = arg;
- if (!addresses) {
- finish(exec_ctx, req, 0);
+ if (error != GRPC_ERROR_NONE) {
+ finish(exec_ctx, req, error);
return;
}
- req->addresses = addresses;
req->next_address = 0;
- next_address(exec_ctx, req);
+ next_address(exec_ctx, req, GRPC_ERROR_NONE);
}
-static void internal_request_begin(
- grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
- grpc_pollset *pollset, const grpc_httpcli_request *request,
- gpr_timespec deadline, grpc_httpcli_response_cb on_response,
- void *user_data, const char *name, gpr_slice request_text) {
+static void internal_request_begin(grpc_exec_ctx *exec_ctx,
+ grpc_httpcli_context *context,
+ grpc_polling_entity *pollent,
+ const grpc_httpcli_request *request,
+ gpr_timespec deadline, grpc_closure *on_done,
+ grpc_httpcli_response *response,
+ const char *name, gpr_slice request_text) {
internal_request *req = gpr_malloc(sizeof(internal_request));
memset(req, 0, sizeof(*req));
req->request_text = request_text;
- grpc_http_parser_init(&req->parser);
- req->on_response = on_response;
- req->user_data = user_data;
+ grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response);
+ req->on_done = on_done;
req->deadline = deadline;
req->handshaker =
request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
req->context = context;
- req->pollset = pollset;
+ req->pollent = pollent;
+ req->overall_error = GRPC_ERROR_NONE;
grpc_closure_init(&req->on_read, on_read, req);
grpc_closure_init(&req->done_write, done_write, req);
gpr_slice_buffer_init(&req->incoming);
@@ -244,45 +265,46 @@ static void internal_request_begin(
req->host = gpr_strdup(request->host);
req->ssl_host_override = gpr_strdup(request->ssl_host_override);
- grpc_pollset_set_add_pollset(exec_ctx, req->context->pollset_set,
- req->pollset);
+ GPR_ASSERT(pollent);
+ grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent,
+ req->context->pollset_set);
grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
- on_resolved, req);
+ grpc_closure_create(on_resolved, req), &req->addresses);
}
void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
const grpc_httpcli_request *request,
- gpr_timespec deadline,
- grpc_httpcli_response_cb on_response, void *user_data) {
+ gpr_timespec deadline, grpc_closure *on_done,
+ grpc_httpcli_response *response) {
char *name;
if (g_get_override &&
- g_get_override(exec_ctx, request, deadline, on_response, user_data)) {
+ g_get_override(exec_ctx, request, deadline, on_done, response)) {
return;
}
gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
- internal_request_begin(exec_ctx, context, pollset, request, deadline,
- on_response, user_data, name,
+ internal_request_begin(exec_ctx, context, pollent, request, deadline, on_done,
+ response, name,
grpc_httpcli_format_get_request(request));
gpr_free(name);
}
void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
const grpc_httpcli_request *request,
const char *body_bytes, size_t body_size,
- gpr_timespec deadline,
- grpc_httpcli_response_cb on_response, void *user_data) {
+ gpr_timespec deadline, grpc_closure *on_done,
+ grpc_httpcli_response *response) {
char *name;
if (g_post_override &&
g_post_override(exec_ctx, request, body_bytes, body_size, deadline,
- on_response, user_data)) {
+ on_done, response)) {
return;
}
gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
internal_request_begin(
- exec_ctx, context, pollset, request, deadline, on_response, user_data,
- name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+ exec_ctx, context, pollent, request, deadline, on_done, response, name,
+ grpc_httpcli_format_post_request(request, body_bytes, body_size));
gpr_free(name);
}
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 11a32a125c..662e176f4c 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -41,6 +41,7 @@
#include "src/core/lib/http/parser.h"
#include "src/core/lib/iomgr/endpoint.h"
#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/pollset_set.h"
/* User agent this library reports */
@@ -56,7 +57,7 @@ typedef struct grpc_httpcli_context {
typedef struct {
const char *default_port;
void (*handshake)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint,
- const char *host,
+ const char *host, gpr_timespec deadline,
void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
grpc_endpoint *endpoint));
} grpc_httpcli_handshaker;
@@ -81,11 +82,6 @@ typedef struct grpc_httpcli_request {
/* Expose the parser response type as a httpcli response too */
typedef struct grpc_http_response grpc_httpcli_response;
-/* Callback for grpc_httpcli_get and grpc_httpcli_post. */
-typedef void (*grpc_httpcli_response_cb)(grpc_exec_ctx *exec_ctx,
- void *user_data,
- const grpc_http_response *response);
-
void grpc_httpcli_context_init(grpc_httpcli_context *context);
void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
@@ -100,10 +96,10 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call) */
void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
const grpc_httpcli_request *request,
- gpr_timespec deadline,
- grpc_httpcli_response_cb on_response, void *user_data);
+ gpr_timespec deadline, grpc_closure *on_complete,
+ grpc_httpcli_response *response);
/* Asynchronously perform a HTTP POST.
'context' specifies the http context under which to do the post
@@ -121,22 +117,22 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
supplied pointer to pass to said call)
Does not support ?var1=val1&var2=val2 in the path. */
void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
const grpc_httpcli_request *request,
const char *body_bytes, size_t body_size,
- gpr_timespec deadline,
- grpc_httpcli_response_cb on_response, void *user_data);
+ gpr_timespec deadline, grpc_closure *on_complete,
+ grpc_httpcli_response *response);
/* override functions return 1 if they handled the request, 0 otherwise */
typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx,
const grpc_httpcli_request *request,
gpr_timespec deadline,
- grpc_httpcli_response_cb on_response,
- void *user_data);
+ grpc_closure *on_complete,
+ grpc_httpcli_response *response);
typedef int (*grpc_httpcli_post_override)(
grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
const char *body_bytes, size_t body_size, gpr_timespec deadline,
- grpc_httpcli_response_cb on_response, void *user_data);
+ grpc_closure *on_complete, grpc_httpcli_response *response);
void grpc_httpcli_set_override(grpc_httpcli_get_override get,
grpc_httpcli_post_override post);
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index 5590928968..a57d93bb7b 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -61,6 +61,7 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) {
static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_httpcli_ssl_channel_security_connector *c =
@@ -79,7 +80,7 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
- nonsecure_endpoint, cb, user_data);
+ nonsecure_endpoint, deadline, cb, user_data);
}
}
@@ -163,6 +164,7 @@ static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp,
static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
grpc_endpoint *tcp, const char *host,
+ gpr_timespec deadline,
void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
grpc_endpoint *endpoint)) {
grpc_channel_security_connector *sc = NULL;
@@ -181,7 +183,7 @@ static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
pem_root_certs, pem_root_certs_size, host, &sc) ==
GRPC_SECURITY_OK);
grpc_channel_security_connector_do_handshake(
- exec_ctx, sc, tcp, on_secure_transport_setup_done, c);
+ exec_ctx, sc, tcp, deadline, on_secure_transport_setup_done, c);
GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
}
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
index 09b2ed40d1..92ed08ae51 100644
--- a/src/core/lib/http/parser.c
+++ b/src/core/lib/http/parser.c
@@ -48,37 +48,38 @@ static char *buf2str(void *buffer, size_t length) {
return out;
}
-static int handle_response_line(grpc_http_parser *parser) {
+static grpc_error *handle_response_line(grpc_http_parser *parser) {
uint8_t *beg = parser->cur_line;
uint8_t *cur = beg;
uint8_t *end = beg + parser->cur_line_length;
- if (cur == end || *cur++ != 'H') goto error;
- if (cur == end || *cur++ != 'T') goto error;
- if (cur == end || *cur++ != 'T') goto error;
- if (cur == end || *cur++ != 'P') goto error;
- if (cur == end || *cur++ != '/') goto error;
- if (cur == end || *cur++ != '1') goto error;
- if (cur == end || *cur++ != '.') goto error;
- if (cur == end || *cur < '0' || *cur++ > '1') goto error;
- if (cur == end || *cur++ != ' ') goto error;
- if (cur == end || *cur < '1' || *cur++ > '9') goto error;
- if (cur == end || *cur < '0' || *cur++ > '9') goto error;
- if (cur == end || *cur < '0' || *cur++ > '9') goto error;
- parser->http.response.status =
+ if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+ if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+ if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'");
+ if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'");
+ if (cur == end || *cur < '0' || *cur++ > '1') {
+ return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1");
+ }
+ if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+ if (cur == end || *cur < '1' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ if (cur == end || *cur < '0' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ if (cur == end || *cur < '0' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ parser->http.response->status =
(cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
- if (cur == end || *cur++ != ' ') goto error;
+ if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
/* we don't really care about the status code message */
- return 1;
-
-error:
- if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing response line");
- return 0;
+ return GRPC_ERROR_NONE;
}
-static int handle_request_line(grpc_http_parser *parser) {
+static grpc_error *handle_request_line(grpc_http_parser *parser) {
uint8_t *beg = parser->cur_line;
uint8_t *cur = beg;
uint8_t *end = beg + parser->cur_line_length;
@@ -87,84 +88,81 @@ static int handle_request_line(grpc_http_parser *parser) {
while (cur != end && *cur++ != ' ')
;
- if (cur == end) goto error;
- parser->http.request.method = buf2str(beg, (size_t)(cur - beg - 1));
+ if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line");
+ parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1));
beg = cur;
while (cur != end && *cur++ != ' ')
;
- if (cur == end) goto error;
- parser->http.request.path = buf2str(beg, (size_t)(cur - beg - 1));
-
- if (cur == end || *cur++ != 'H') goto error;
- if (cur == end || *cur++ != 'T') goto error;
- if (cur == end || *cur++ != 'T') goto error;
- if (cur == end || *cur++ != 'P') goto error;
- if (cur == end || *cur++ != '/') goto error;
+ if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line");
+ parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1));
+
+ if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+ if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
vers_major = (uint8_t)(*cur++ - '1' + 1);
++cur;
- if (cur == end) goto error;
+ if (cur == end)
+ return GRPC_ERROR_CREATE("End of line in HTTP version string");
vers_minor = (uint8_t)(*cur++ - '1' + 1);
if (vers_major == 1) {
if (vers_minor == 0) {
- parser->http.request.version = GRPC_HTTP_HTTP10;
+ parser->http.request->version = GRPC_HTTP_HTTP10;
} else if (vers_minor == 1) {
- parser->http.request.version = GRPC_HTTP_HTTP11;
+ parser->http.request->version = GRPC_HTTP_HTTP11;
} else {
- goto error;
+ return GRPC_ERROR_CREATE(
+ "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
}
} else if (vers_major == 2) {
if (vers_minor == 0) {
- parser->http.request.version = GRPC_HTTP_HTTP20;
+ parser->http.request->version = GRPC_HTTP_HTTP20;
} else {
- goto error;
+ return GRPC_ERROR_CREATE(
+ "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
}
} else {
- goto error;
+ return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
}
- return 1;
-
-error:
- if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing request line");
- return 0;
+ return GRPC_ERROR_NONE;
}
-static int handle_first_line(grpc_http_parser *parser) {
- if (parser->cur_line[0] == 'H') {
- parser->type = GRPC_HTTP_RESPONSE;
- return handle_response_line(parser);
- } else {
- parser->type = GRPC_HTTP_REQUEST;
- return handle_request_line(parser);
+static grpc_error *handle_first_line(grpc_http_parser *parser) {
+ switch (parser->type) {
+ case GRPC_HTTP_REQUEST:
+ return handle_request_line(parser);
+ case GRPC_HTTP_RESPONSE:
+ return handle_response_line(parser);
}
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
}
-static int add_header(grpc_http_parser *parser) {
+static grpc_error *add_header(grpc_http_parser *parser) {
uint8_t *beg = parser->cur_line;
uint8_t *cur = beg;
uint8_t *end = beg + parser->cur_line_length;
size_t *hdr_count = NULL;
grpc_http_header **hdrs = NULL;
grpc_http_header hdr = {NULL, NULL};
+ grpc_error *error = GRPC_ERROR_NONE;
GPR_ASSERT(cur != end);
if (*cur == ' ' || *cur == '\t') {
- if (grpc_http1_trace)
- gpr_log(GPR_ERROR, "Continued header lines not supported yet");
- goto error;
+ error = GRPC_ERROR_CREATE("Continued header lines not supported yet");
+ goto done;
}
while (cur != end && *cur != ':') {
cur++;
}
if (cur == end) {
- if (grpc_http1_trace) {
- gpr_log(GPR_ERROR, "Didn't find ':' in header string");
- }
- goto error;
+ error = GRPC_ERROR_CREATE("Didn't find ':' in header string");
+ goto done;
}
GPR_ASSERT(cur >= beg);
hdr.key = buf2str(beg, (size_t)(cur - beg));
@@ -176,14 +174,15 @@ static int add_header(grpc_http_parser *parser) {
GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length);
hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length);
- if (parser->type == GRPC_HTTP_RESPONSE) {
- hdr_count = &parser->http.response.hdr_count;
- hdrs = &parser->http.response.hdrs;
- } else if (parser->type == GRPC_HTTP_REQUEST) {
- hdr_count = &parser->http.request.hdr_count;
- hdrs = &parser->http.request.hdrs;
- } else {
- return 0;
+ switch (parser->type) {
+ case GRPC_HTTP_RESPONSE:
+ hdr_count = &parser->http.response->hdr_count;
+ hdrs = &parser->http.response->hdrs;
+ break;
+ case GRPC_HTTP_REQUEST:
+ hdr_count = &parser->http.request->hdr_count;
+ hdrs = &parser->http.request->hdrs;
+ break;
}
if (*hdr_count == parser->hdr_capacity) {
@@ -192,20 +191,21 @@ static int add_header(grpc_http_parser *parser) {
*hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
}
(*hdrs)[(*hdr_count)++] = hdr;
- return 1;
-error:
- gpr_free(hdr.key);
- gpr_free(hdr.value);
- return 0;
+done:
+ if (error != GRPC_ERROR_NONE) {
+ gpr_free(hdr.key);
+ gpr_free(hdr.value);
+ }
+ return error;
}
-static int finish_line(grpc_http_parser *parser) {
+static grpc_error *finish_line(grpc_http_parser *parser) {
+ grpc_error *err;
switch (parser->state) {
case GRPC_HTTP_FIRST_LINE:
- if (!handle_first_line(parser)) {
- return 0;
- }
+ err = handle_first_line(parser);
+ if (err != GRPC_ERROR_NONE) return err;
parser->state = GRPC_HTTP_HEADERS;
break;
case GRPC_HTTP_HEADERS:
@@ -213,30 +213,31 @@ static int finish_line(grpc_http_parser *parser) {
parser->state = GRPC_HTTP_BODY;
break;
}
- if (!add_header(parser)) {
- return 0;
+ err = add_header(parser);
+ if (err != GRPC_ERROR_NONE) {
+ return err;
}
break;
case GRPC_HTTP_BODY:
- GPR_UNREACHABLE_CODE(return 0);
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
}
parser->cur_line_length = 0;
- return 1;
+ return GRPC_ERROR_NONE;
}
-static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) {
size_t *body_length = NULL;
char **body = NULL;
if (parser->type == GRPC_HTTP_RESPONSE) {
- body_length = &parser->http.response.body_length;
- body = &parser->http.response.body;
+ body_length = &parser->http.response->body_length;
+ body = &parser->http.response->body;
} else if (parser->type == GRPC_HTTP_REQUEST) {
- body_length = &parser->http.request.body_length;
- body = &parser->http.request.body;
+ body_length = &parser->http.request->body_length;
+ body = &parser->http.request->body;
} else {
- return 0;
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
}
if (*body_length == parser->body_capacity) {
@@ -246,34 +247,34 @@ static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
(*body)[*body_length] = (char)byte;
(*body_length)++;
- return 1;
+ return GRPC_ERROR_NONE;
}
-static int check_line(grpc_http_parser *parser) {
+static bool check_line(grpc_http_parser *parser) {
if (parser->cur_line_length >= 2 &&
parser->cur_line[parser->cur_line_length - 2] == '\r' &&
parser->cur_line[parser->cur_line_length - 1] == '\n') {
- return 1;
+ return true;
}
// HTTP request with \n\r line termiantors.
else if (parser->cur_line_length >= 2 &&
parser->cur_line[parser->cur_line_length - 2] == '\n' &&
parser->cur_line[parser->cur_line_length - 1] == '\r') {
- return 1;
+ return true;
}
// HTTP request with only \n line terminators.
else if (parser->cur_line_length >= 1 &&
parser->cur_line[parser->cur_line_length - 1] == '\n') {
parser->cur_line_end_length = 1;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
-static int addbyte(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
switch (parser->state) {
case GRPC_HTTP_FIRST_LINE:
case GRPC_HTTP_HEADERS:
@@ -288,7 +289,7 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) {
if (check_line(parser)) {
return finish_line(parser);
} else {
- return 1;
+ return GRPC_ERROR_NONE;
}
GPR_UNREACHABLE_CODE(return 0);
case GRPC_HTTP_BODY:
@@ -297,46 +298,53 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) {
GPR_UNREACHABLE_CODE(return 0);
}
-void grpc_http_parser_init(grpc_http_parser *parser) {
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+ void *request_or_response) {
memset(parser, 0, sizeof(*parser));
parser->state = GRPC_HTTP_FIRST_LINE;
- parser->type = GRPC_HTTP_UNKNOWN;
+ parser->type = type;
+ parser->http.request_or_response = request_or_response;
parser->cur_line_end_length = 2;
}
-void grpc_http_parser_destroy(grpc_http_parser *parser) {
+void grpc_http_parser_destroy(grpc_http_parser *parser) {}
+
+void grpc_http_request_destroy(grpc_http_request *request) {
size_t i;
- if (parser->type == GRPC_HTTP_RESPONSE) {
- gpr_free(parser->http.response.body);
- for (i = 0; i < parser->http.response.hdr_count; i++) {
- gpr_free(parser->http.response.hdrs[i].key);
- gpr_free(parser->http.response.hdrs[i].value);
- }
- gpr_free(parser->http.response.hdrs);
- } else if (parser->type == GRPC_HTTP_REQUEST) {
- gpr_free(parser->http.request.body);
- for (i = 0; i < parser->http.request.hdr_count; i++) {
- gpr_free(parser->http.request.hdrs[i].key);
- gpr_free(parser->http.request.hdrs[i].value);
- }
- gpr_free(parser->http.request.hdrs);
- gpr_free(parser->http.request.method);
- gpr_free(parser->http.request.path);
+ gpr_free(request->body);
+ for (i = 0; i < request->hdr_count; i++) {
+ gpr_free(request->hdrs[i].key);
+ gpr_free(request->hdrs[i].value);
}
+ gpr_free(request->hdrs);
+ gpr_free(request->method);
+ gpr_free(request->path);
}
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
+void grpc_http_response_destroy(grpc_http_response *response) {
+ size_t i;
+ gpr_free(response->body);
+ for (i = 0; i < response->hdr_count; i++) {
+ gpr_free(response->hdrs[i].key);
+ gpr_free(response->hdrs[i].value);
+ }
+ gpr_free(response->hdrs);
+}
+
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
size_t i;
for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
- if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) {
- return 0;
- }
+ grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+ if (err != GRPC_ERROR_NONE) return err;
}
- return 1;
+ return GRPC_ERROR_NONE;
}
-int grpc_http_parser_eof(grpc_http_parser *parser) {
- return parser->state == GRPC_HTTP_BODY;
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) {
+ if (parser->state != GRPC_HTTP_BODY) {
+ return GRPC_ERROR_CREATE("Did not finish headers");
+ }
+ return GRPC_ERROR_NONE;
}
diff --git a/src/core/lib/http/parser.c.orig b/src/core/lib/http/parser.c.orig
new file mode 100644
index 0000000000..74d90fd8bf
--- /dev/null
+++ b/src/core/lib/http/parser.c.orig
@@ -0,0 +1,357 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/http/parser.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+int grpc_http1_trace = 0;
+
+static char *buf2str(void *buffer, size_t length) {
+ char *out = gpr_malloc(length + 1);
+ memcpy(out, buffer, length);
+ out[length] = 0;
+ return out;
+}
+
+static grpc_error *handle_response_line(grpc_http_parser *parser) {
+ uint8_t *beg = parser->cur_line;
+ uint8_t *cur = beg;
+ uint8_t *end = beg + parser->cur_line_length;
+
+ if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+ if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+ if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'");
+ if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'");
+ if (cur == end || *cur < '0' || *cur++ > '1') {
+ return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1");
+ }
+ if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+ if (cur == end || *cur < '1' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ if (cur == end || *cur < '0' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ if (cur == end || *cur < '0' || *cur++ > '9')
+ return GRPC_ERROR_CREATE("Expected status code");
+ parser->http.response->status =
+ (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
+ if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+
+ /* we don't really care about the status code message */
+
+ return GRPC_ERROR_NONE;
+}
+
+static grpc_error *handle_request_line(grpc_http_parser *parser) {
+ uint8_t *beg = parser->cur_line;
+ uint8_t *cur = beg;
+ uint8_t *end = beg + parser->cur_line_length;
+ uint8_t vers_major = 0;
+ uint8_t vers_minor = 0;
+
+ while (cur != end && *cur++ != ' ')
+ ;
+ if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line");
+ parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1));
+
+ beg = cur;
+ while (cur != end && *cur++ != ' ')
+ ;
+ if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line");
+ parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1));
+
+ if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+ if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+ if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+ vers_major = (uint8_t)(*cur++ - '1' + 1);
+ ++cur;
+ if (cur == end)
+ return GRPC_ERROR_CREATE("End of line in HTTP version string");
+ vers_minor = (uint8_t)(*cur++ - '1' + 1);
+
+ if (vers_major == 1) {
+ if (vers_minor == 0) {
+ parser->http.request->version = GRPC_HTTP_HTTP10;
+ } else if (vers_minor == 1) {
+ parser->http.request->version = GRPC_HTTP_HTTP11;
+ } else {
+ return GRPC_ERROR_CREATE(
+ "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+ }
+ } else if (vers_major == 2) {
+ if (vers_minor == 0) {
+ parser->http.request->version = GRPC_HTTP_HTTP20;
+ } else {
+ return GRPC_ERROR_CREATE(
+ "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+ }
+ } else {
+ return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+ }
+
+ return GRPC_ERROR_NONE;
+}
+
+static grpc_error *handle_first_line(grpc_http_parser *parser) {
+ switch (parser->type) {
+ case GRPC_HTTP_REQUEST:
+ return handle_request_line(parser);
+ case GRPC_HTTP_RESPONSE:
+ return handle_response_line(parser);
+ }
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+}
+
+static grpc_error *add_header(grpc_http_parser *parser) {
+ uint8_t *beg = parser->cur_line;
+ uint8_t *cur = beg;
+ uint8_t *end = beg + parser->cur_line_length;
+ size_t *hdr_count = NULL;
+ grpc_http_header **hdrs = NULL;
+ grpc_http_header hdr = {NULL, NULL};
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ GPR_ASSERT(cur != end);
+
+ if (*cur == ' ' || *cur == '\t') {
+ error = GRPC_ERROR_CREATE("Continued header lines not supported yet");
+ goto done;
+ }
+
+ while (cur != end && *cur != ':') {
+ cur++;
+ }
+ if (cur == end) {
+<<<<<<< HEAD
+ error = GRPC_ERROR_CREATE("Didn't find ':' in header string");
+ goto done;
+=======
+ if (grpc_http1_trace) {
+ gpr_log(GPR_ERROR, "Didn't find ':' in header string");
+ }
+ goto error;
+>>>>>>> a709afe241d8b264a1c326315f757b4a8d330207
+ }
+ GPR_ASSERT(cur >= beg);
+ hdr.key = buf2str(beg, (size_t)(cur - beg));
+ cur++; /* skip : */
+
+ while (cur != end && (*cur == ' ' || *cur == '\t')) {
+ cur++;
+ }
+ GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length);
+ hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length);
+
+ switch (parser->type) {
+ case GRPC_HTTP_RESPONSE:
+ hdr_count = &parser->http.response->hdr_count;
+ hdrs = &parser->http.response->hdrs;
+ break;
+ case GRPC_HTTP_REQUEST:
+ hdr_count = &parser->http.request->hdr_count;
+ hdrs = &parser->http.request->hdrs;
+ break;
+ }
+
+ if (*hdr_count == parser->hdr_capacity) {
+ parser->hdr_capacity =
+ GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
+ *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
+ }
+ (*hdrs)[(*hdr_count)++] = hdr;
+
+done:
+ if (error != GRPC_ERROR_NONE) {
+ gpr_free(hdr.key);
+ gpr_free(hdr.value);
+ }
+ return error;
+}
+
+static grpc_error *finish_line(grpc_http_parser *parser) {
+ grpc_error *err;
+ switch (parser->state) {
+ case GRPC_HTTP_FIRST_LINE:
+ err = handle_first_line(parser);
+ if (err != GRPC_ERROR_NONE) return err;
+ parser->state = GRPC_HTTP_HEADERS;
+ break;
+ case GRPC_HTTP_HEADERS:
+ if (parser->cur_line_length == parser->cur_line_end_length) {
+ parser->state = GRPC_HTTP_BODY;
+ break;
+ }
+ err = add_header(parser);
+ if (err != GRPC_ERROR_NONE) {
+ return err;
+ }
+ break;
+ case GRPC_HTTP_BODY:
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+ }
+
+ parser->cur_line_length = 0;
+ return GRPC_ERROR_NONE;
+}
+
+static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+ size_t *body_length = NULL;
+ char **body = NULL;
+
+ if (parser->type == GRPC_HTTP_RESPONSE) {
+ body_length = &parser->http.response->body_length;
+ body = &parser->http.response->body;
+ } else if (parser->type == GRPC_HTTP_REQUEST) {
+ body_length = &parser->http.request->body_length;
+ body = &parser->http.request->body;
+ } else {
+ GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+ }
+
+ if (*body_length == parser->body_capacity) {
+ parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
+ *body = gpr_realloc((void *)*body, parser->body_capacity);
+ }
+ (*body)[*body_length] = (char)byte;
+ (*body_length)++;
+
+ return GRPC_ERROR_NONE;
+}
+
+static bool check_line(grpc_http_parser *parser) {
+ if (parser->cur_line_length >= 2 &&
+ parser->cur_line[parser->cur_line_length - 2] == '\r' &&
+ parser->cur_line[parser->cur_line_length - 1] == '\n') {
+ return true;
+ }
+
+ // HTTP request with \n\r line termiantors.
+ else if (parser->cur_line_length >= 2 &&
+ parser->cur_line[parser->cur_line_length - 2] == '\n' &&
+ parser->cur_line[parser->cur_line_length - 1] == '\r') {
+ return true;
+ }
+
+ // HTTP request with only \n line terminators.
+ else if (parser->cur_line_length >= 1 &&
+ parser->cur_line[parser->cur_line_length - 1] == '\n') {
+ parser->cur_line_end_length = 1;
+ return true;
+ }
+
+ return false;
+}
+
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
+ switch (parser->state) {
+ case GRPC_HTTP_FIRST_LINE:
+ case GRPC_HTTP_HEADERS:
+ if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
+ if (grpc_http1_trace)
+ gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
+ GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
+ return 0;
+ }
+ parser->cur_line[parser->cur_line_length] = byte;
+ parser->cur_line_length++;
+ if (check_line(parser)) {
+ return finish_line(parser);
+ } else {
+ return GRPC_ERROR_NONE;
+ }
+ GPR_UNREACHABLE_CODE(return 0);
+ case GRPC_HTTP_BODY:
+ return addbyte_body(parser, byte);
+ }
+ GPR_UNREACHABLE_CODE(return 0);
+}
+
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+ void *request_or_response) {
+ memset(parser, 0, sizeof(*parser));
+ parser->state = GRPC_HTTP_FIRST_LINE;
+ parser->type = type;
+ parser->http.request_or_response = request_or_response;
+ parser->cur_line_end_length = 2;
+}
+
+void grpc_http_parser_destroy(grpc_http_parser *parser) {}
+
+void grpc_http_request_destroy(grpc_http_request *request) {
+ size_t i;
+ gpr_free(request->body);
+ for (i = 0; i < request->hdr_count; i++) {
+ gpr_free(request->hdrs[i].key);
+ gpr_free(request->hdrs[i].value);
+ }
+ gpr_free(request->hdrs);
+ gpr_free(request->method);
+ gpr_free(request->path);
+}
+
+void grpc_http_response_destroy(grpc_http_response *response) {
+ size_t i;
+ gpr_free(response->body);
+ for (i = 0; i < response->hdr_count; i++) {
+ gpr_free(response->hdrs[i].key);
+ gpr_free(response->hdrs[i].value);
+ }
+ gpr_free(response->hdrs);
+}
+
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
+ size_t i;
+
+ for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
+ grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+ if (err != GRPC_ERROR_NONE) return err;
+ }
+
+ return GRPC_ERROR_NONE;
+}
+
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) {
+ if (parser->state != GRPC_HTTP_BODY) {
+ return GRPC_ERROR_CREATE("Did not finish headers");
+ }
+ return GRPC_ERROR_NONE;
+}
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 536637e9a2..6df3cc8b13 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -36,6 +36,7 @@
#include <grpc/support/port_platform.h>
#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/error.h"
/* Maximum length of a header string of the form 'Key: Value\r\n' */
#define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096
@@ -61,7 +62,6 @@ typedef enum {
typedef enum {
GRPC_HTTP_RESPONSE,
GRPC_HTTP_REQUEST,
- GRPC_HTTP_UNKNOWN
} grpc_http_type;
/* A request */
@@ -97,8 +97,9 @@ typedef struct {
grpc_http_type type;
union {
- grpc_http_response response;
- grpc_http_request request;
+ grpc_http_response *response;
+ grpc_http_request *request;
+ void *request_or_response;
} http;
size_t body_capacity;
size_t hdr_capacity;
@@ -108,11 +109,15 @@ typedef struct {
size_t cur_line_end_length;
} grpc_http_parser;
-void grpc_http_parser_init(grpc_http_parser *parser);
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+ void *request_or_response);
void grpc_http_parser_destroy(grpc_http_parser *parser);
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
-int grpc_http_parser_eof(grpc_http_parser *parser);
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser);
+
+void grpc_http_request_destroy(grpc_http_request *request);
+void grpc_http_response_destroy(grpc_http_response *response);
extern int grpc_http1_trace;
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
index 27793c32e4..0b6c3b2539 100644
--- a/src/core/lib/iomgr/closure.c
+++ b/src/core/lib/iomgr/closure.c
@@ -39,25 +39,32 @@ void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
void *cb_arg) {
closure->cb = cb;
closure->cb_arg = cb_arg;
- closure->final_data = 0;
}
-void grpc_closure_list_add(grpc_closure_list *closure_list,
- grpc_closure *closure, bool success) {
- if (closure == NULL) return;
- closure->final_data = (success != 0);
+void grpc_closure_list_append(grpc_closure_list *closure_list,
+ grpc_closure *closure, grpc_error *error) {
+ if (closure == NULL) {
+ GRPC_ERROR_UNREF(error);
+ return;
+ }
+ closure->error = error;
+ closure->next_data.next = NULL;
if (closure_list->head == NULL) {
closure_list->head = closure;
} else {
- closure_list->tail->final_data |= (uintptr_t)closure;
+ closure_list->tail->next_data.next = closure;
}
closure_list->tail = closure;
}
-void grpc_closure_list_fail_all(grpc_closure_list *list) {
- for (grpc_closure *c = list->head; c != NULL; c = grpc_closure_next(c)) {
- c->final_data &= ~(uintptr_t)1;
+void grpc_closure_list_fail_all(grpc_closure_list *list,
+ grpc_error *forced_failure) {
+ for (grpc_closure *c = list->head; c != NULL; c = c->next_data.next) {
+ if (c->error == GRPC_ERROR_NONE) {
+ c->error = GRPC_ERROR_REF(forced_failure);
+ }
}
+ GRPC_ERROR_UNREF(forced_failure);
}
bool grpc_closure_list_empty(grpc_closure_list closure_list) {
@@ -71,7 +78,7 @@ void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst) {
if (dst->head == NULL) {
*dst = *src;
} else {
- dst->tail->final_data |= (uintptr_t)src->head;
+ dst->tail->next_data.next = src->head;
dst->tail = src->tail;
}
src->head = src->tail = NULL;
@@ -83,12 +90,13 @@ typedef struct {
grpc_closure wrapper;
} wrapped_closure;
-static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
wrapped_closure *wc = arg;
grpc_iomgr_cb_func cb = wc->cb;
void *cb_arg = wc->cb_arg;
gpr_free(wc);
- cb(exec_ctx, cb_arg, success);
+ cb(exec_ctx, cb_arg, error);
}
grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) {
@@ -98,7 +106,3 @@ grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) {
grpc_closure_init(&wc->wrapper, closure_wrapper, wc);
return &wc->wrapper;
}
-
-grpc_closure *grpc_closure_next(grpc_closure *closure) {
- return (grpc_closure *)(closure->final_data & ~(uintptr_t)1);
-}
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index fdc2daed9d..08e59a168e 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -36,6 +36,7 @@
#include <grpc/support/port_platform.h>
#include <stdbool.h>
+#include "src/core/lib/iomgr/error.h"
struct grpc_closure;
typedef struct grpc_closure grpc_closure;
@@ -52,10 +53,10 @@ typedef struct grpc_closure_list {
/** gRPC Callback definition.
*
* \param arg Arbitrary input.
- * \param success An indication on the state of the iomgr. On false, cleanup
- * actions should be taken (eg, shutdown). */
+ * \param error GRPC_ERROR_NONE if no error occurred, otherwise some grpc_error
+ * describing what went wrong */
typedef void (*grpc_iomgr_cb_func)(grpc_exec_ctx *exec_ctx, void *arg,
- bool success);
+ grpc_error *error);
/** A closure over a grpc_iomgr_cb_func. */
struct grpc_closure {
@@ -65,10 +66,15 @@ struct grpc_closure {
/** Arguments to be passed to "cb". */
void *cb_arg;
- /** Once enqueued, contains in the lower bit the success of the closure,
- and in the upper bits the pointer to the next closure in the list.
- Before enqueing for execution, this is usable for scratch data. */
- uintptr_t final_data;
+ /** Once queued, the result of the closure. Before then: scratch space */
+ grpc_error *error;
+
+ /** Once queued, next indicates the next queued closure; before then, scratch
+ * space */
+ union {
+ grpc_closure *next;
+ uintptr_t scratch;
+ } next_data;
};
/** Initializes \a closure with \a cb and \a cb_arg. */
@@ -81,13 +87,14 @@ grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg);
#define GRPC_CLOSURE_LIST_INIT \
{ NULL, NULL }
-/** add \a closure to the end of \a list and set \a closure's success to \a
- * success */
-void grpc_closure_list_add(grpc_closure_list *list, grpc_closure *closure,
- bool success);
+/** add \a closure to the end of \a list
+ and set \a closure's result to \a error */
+void grpc_closure_list_append(grpc_closure_list *list, grpc_closure *closure,
+ grpc_error *error);
/** force all success bits in \a list to false */
-void grpc_closure_list_fail_all(grpc_closure_list *list);
+void grpc_closure_list_fail_all(grpc_closure_list *list,
+ grpc_error *forced_failure);
/** append all closures from \a src to \a dst and empty \a src. */
void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst);
@@ -95,7 +102,4 @@ void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst);
/** return whether \a list is empty. */
bool grpc_closure_list_empty(grpc_closure_list list);
-/** return the next pointer for a queued closure list */
-grpc_closure *grpc_closure_next(grpc_closure *closure);
-
#endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h
index 3877ceb1e2..f9808bbda1 100644
--- a/src/core/lib/iomgr/endpoint.h
+++ b/src/core/lib/iomgr/endpoint.h
@@ -82,7 +82,7 @@ char *grpc_endpoint_get_peer(grpc_endpoint *ep);
void grpc_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
gpr_slice_buffer *slices, grpc_closure *cb);
-/* Causes any pending read/write callbacks to run immediately with
+/* Causes any pending and future read/write callbacks to run immediately with
success==0 */
void grpc_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep);
void grpc_endpoint_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep);
diff --git a/src/core/lib/iomgr/endpoint_pair_posix.c b/src/core/lib/iomgr/endpoint_pair_posix.c
index e0ce47c773..e295fb4867 100644
--- a/src/core/lib/iomgr/endpoint_pair_posix.c
+++ b/src/core/lib/iomgr/endpoint_pair_posix.c
@@ -58,8 +58,8 @@ static void create_sockets(int sv[2]) {
GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
flags = fcntl(sv[1], F_GETFL, 0);
GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
- GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]));
- GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]));
+ GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE);
+ GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE);
}
grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,
diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c
new file mode 100644
index 0000000000..149c55663c
--- /dev/null
+++ b/src/core/lib/iomgr/error.c
@@ -0,0 +1,578 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/iomgr/error.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/status.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/avl.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#ifdef GPR_WINDOWS
+#include <grpc/support/log_windows.h>
+#endif
+
+#include "src/core/lib/profiling/timers.h"
+
+static void destroy_integer(void *key) {}
+
+static void *copy_integer(void *key) { return key; }
+
+static long compare_integers(void *key1, void *key2) {
+ return GPR_ICMP((uintptr_t)key1, (uintptr_t)key2);
+}
+
+static void destroy_string(void *str) { gpr_free(str); }
+
+static void *copy_string(void *str) { return gpr_strdup(str); }
+
+static void destroy_err(void *err) { GRPC_ERROR_UNREF(err); }
+
+static void *copy_err(void *err) { return GRPC_ERROR_REF(err); }
+
+static void destroy_time(void *tm) { gpr_free(tm); }
+
+static gpr_timespec *box_time(gpr_timespec tm) {
+ gpr_timespec *out = gpr_malloc(sizeof(*out));
+ *out = tm;
+ return out;
+}
+
+static void *copy_time(void *tm) { return box_time(*(gpr_timespec *)tm); }
+
+static const gpr_avl_vtable avl_vtable_ints = {destroy_integer, copy_integer,
+ compare_integers,
+ destroy_integer, copy_integer};
+
+static const gpr_avl_vtable avl_vtable_strs = {destroy_integer, copy_integer,
+ compare_integers, destroy_string,
+ copy_string};
+
+static const gpr_avl_vtable avl_vtable_times = {
+ destroy_integer, copy_integer, compare_integers, destroy_time, copy_time};
+
+static const gpr_avl_vtable avl_vtable_errs = {
+ destroy_integer, copy_integer, compare_integers, destroy_err, copy_err};
+
+static const char *error_int_name(grpc_error_ints key) {
+ switch (key) {
+ case GRPC_ERROR_INT_ERRNO:
+ return "errno";
+ case GRPC_ERROR_INT_FILE_LINE:
+ return "file_line";
+ case GRPC_ERROR_INT_STREAM_ID:
+ return "stream_id";
+ case GRPC_ERROR_INT_GRPC_STATUS:
+ return "grpc_status";
+ case GRPC_ERROR_INT_OFFSET:
+ return "offset";
+ case GRPC_ERROR_INT_INDEX:
+ return "index";
+ case GRPC_ERROR_INT_SIZE:
+ return "size";
+ case GRPC_ERROR_INT_HTTP2_ERROR:
+ return "http2_error";
+ case GRPC_ERROR_INT_TSI_CODE:
+ return "tsi_code";
+ case GRPC_ERROR_INT_SECURITY_STATUS:
+ return "security_status";
+ case GRPC_ERROR_INT_FD:
+ return "fd";
+ case GRPC_ERROR_INT_WSA_ERROR:
+ return "wsa_error";
+ case GRPC_ERROR_INT_HTTP_STATUS:
+ return "http_status";
+ case GRPC_ERROR_INT_LIMIT:
+ return "limit";
+ }
+ GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char *error_str_name(grpc_error_strs key) {
+ switch (key) {
+ case GRPC_ERROR_STR_DESCRIPTION:
+ return "description";
+ case GRPC_ERROR_STR_OS_ERROR:
+ return "os_error";
+ case GRPC_ERROR_STR_TARGET_ADDRESS:
+ return "target_address";
+ case GRPC_ERROR_STR_SYSCALL:
+ return "syscall";
+ case GRPC_ERROR_STR_FILE:
+ return "file";
+ case GRPC_ERROR_STR_GRPC_MESSAGE:
+ return "grpc_message";
+ case GRPC_ERROR_STR_RAW_BYTES:
+ return "raw_bytes";
+ case GRPC_ERROR_STR_TSI_ERROR:
+ return "tsi_error";
+ case GRPC_ERROR_STR_FILENAME:
+ return "filename";
+ }
+ GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char *error_time_name(grpc_error_times key) {
+ switch (key) {
+ case GRPC_ERROR_TIME_CREATED:
+ return "created";
+ }
+ GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+struct grpc_error {
+ gpr_refcount refs;
+ gpr_avl ints;
+ gpr_avl strs;
+ gpr_avl times;
+ gpr_avl errs;
+ uintptr_t next_err;
+};
+
+static bool is_special(grpc_error *err) {
+ return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM ||
+ err == GRPC_ERROR_CANCELLED;
+}
+
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
+ const char *func) {
+ if (is_special(err)) return err;
+ gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
+ err->refs.count, err->refs.count + 1, file, line, func);
+ gpr_ref(&err->refs);
+ return err;
+}
+#else
+grpc_error *grpc_error_ref(grpc_error *err) {
+ if (is_special(err)) return err;
+ gpr_ref(&err->refs);
+ return err;
+}
+#endif
+
+static void error_destroy(grpc_error *err) {
+ GPR_ASSERT(!is_special(err));
+ gpr_avl_unref(err->ints);
+ gpr_avl_unref(err->strs);
+ gpr_avl_unref(err->errs);
+ gpr_avl_unref(err->times);
+ gpr_free(err);
+}
+
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+void grpc_error_unref(grpc_error *err, const char *file, int line,
+ const char *func) {
+ if (is_special(err)) return;
+ gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d %s]", err,
+ err->refs.count, err->refs.count - 1, file, line, func);
+ if (gpr_unref(&err->refs)) {
+ error_destroy(err);
+ }
+}
+#else
+void grpc_error_unref(grpc_error *err) {
+ if (is_special(err)) return;
+ if (gpr_unref(&err->refs)) {
+ error_destroy(err);
+ }
+}
+#endif
+
+grpc_error *grpc_error_create(const char *file, int line, const char *desc,
+ grpc_error **referencing,
+ size_t num_referencing) {
+ GPR_TIMER_BEGIN("grpc_error_create", 0);
+ grpc_error *err = gpr_malloc(sizeof(*err));
+ if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL
+ return GRPC_ERROR_OOM;
+ }
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+ gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line);
+#endif
+ err->ints = gpr_avl_add(gpr_avl_create(&avl_vtable_ints),
+ (void *)(uintptr_t)GRPC_ERROR_INT_FILE_LINE,
+ (void *)(uintptr_t)line);
+ err->strs = gpr_avl_add(
+ gpr_avl_add(gpr_avl_create(&avl_vtable_strs),
+ (void *)(uintptr_t)GRPC_ERROR_STR_FILE, gpr_strdup(file)),
+ (void *)(uintptr_t)GRPC_ERROR_STR_DESCRIPTION, gpr_strdup(desc));
+ err->errs = gpr_avl_create(&avl_vtable_errs);
+ err->next_err = 0;
+ for (size_t i = 0; i < num_referencing; i++) {
+ if (referencing[i] == GRPC_ERROR_NONE) continue;
+ err->errs = gpr_avl_add(err->errs, (void *)(err->next_err++),
+ GRPC_ERROR_REF(referencing[i]));
+ }
+ err->times = gpr_avl_add(gpr_avl_create(&avl_vtable_times),
+ (void *)(uintptr_t)GRPC_ERROR_TIME_CREATED,
+ box_time(gpr_now(GPR_CLOCK_REALTIME)));
+ gpr_ref_init(&err->refs, 1);
+ GPR_TIMER_END("grpc_error_create", 0);
+ return err;
+}
+
+static grpc_error *copy_error_and_unref(grpc_error *in) {
+ GPR_TIMER_BEGIN("copy_error_and_unref", 0);
+ grpc_error *out;
+ if (is_special(in)) {
+ if (in == GRPC_ERROR_NONE)
+ out = GRPC_ERROR_CREATE("no error");
+ else if (in == GRPC_ERROR_OOM)
+ out = GRPC_ERROR_CREATE("oom");
+ else if (in == GRPC_ERROR_CANCELLED)
+ out =
+ grpc_error_set_int(GRPC_ERROR_CREATE("cancelled"),
+ GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED);
+ else
+ out = GRPC_ERROR_CREATE("unknown");
+ } else {
+ out = gpr_malloc(sizeof(*out));
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+ gpr_log(GPR_DEBUG, "%p create copying", out);
+#endif
+ out->ints = gpr_avl_ref(in->ints);
+ out->strs = gpr_avl_ref(in->strs);
+ out->errs = gpr_avl_ref(in->errs);
+ out->times = gpr_avl_ref(in->times);
+ out->next_err = in->next_err;
+ gpr_ref_init(&out->refs, 1);
+ GRPC_ERROR_UNREF(in);
+ }
+ GPR_TIMER_END("copy_error_and_unref", 0);
+ return out;
+}
+
+grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
+ intptr_t value) {
+ GPR_TIMER_BEGIN("grpc_error_set_int", 0);
+ grpc_error *new = copy_error_and_unref(src);
+ new->ints = gpr_avl_add(new->ints, (void *)(uintptr_t)which, (void *)value);
+ GPR_TIMER_END("grpc_error_set_int", 0);
+ return new;
+}
+
+bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) {
+ GPR_TIMER_BEGIN("grpc_error_get_int", 0);
+ void *pp;
+ if (is_special(err)) {
+ if (err == GRPC_ERROR_CANCELLED && which == GRPC_ERROR_INT_GRPC_STATUS) {
+ *p = GRPC_STATUS_CANCELLED;
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return true;
+ }
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return false;
+ }
+ if (gpr_avl_maybe_get(err->ints, (void *)(uintptr_t)which, &pp)) {
+ if (p != NULL) *p = (intptr_t)pp;
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return true;
+ }
+ GPR_TIMER_END("grpc_error_get_int", 0);
+ return false;
+}
+
+grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
+ const char *value) {
+ GPR_TIMER_BEGIN("grpc_error_set_str", 0);
+ grpc_error *new = copy_error_and_unref(src);
+ new->strs =
+ gpr_avl_add(new->strs, (void *)(uintptr_t)which, gpr_strdup(value));
+ GPR_TIMER_END("grpc_error_set_str", 0);
+ return new;
+}
+
+const char *grpc_error_get_str(grpc_error *err, grpc_error_strs which) {
+ if (is_special(err)) return NULL;
+ return gpr_avl_get(err->strs, (void *)(uintptr_t)which);
+}
+
+grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) {
+ GPR_TIMER_BEGIN("grpc_error_add_child", 0);
+ grpc_error *new = copy_error_and_unref(src);
+ new->errs = gpr_avl_add(new->errs, (void *)(new->next_err++), child);
+ GPR_TIMER_END("grpc_error_add_child", 0);
+ return new;
+}
+
+static const char *no_error_string = "null";
+static const char *oom_error_string = "\"Out of memory\"";
+static const char *cancelled_error_string = "\"Cancelled\"";
+
+typedef struct {
+ char *key;
+ char *value;
+} kv_pair;
+
+typedef struct {
+ kv_pair *kvs;
+ size_t num_kvs;
+ size_t cap_kvs;
+} kv_pairs;
+
+static void append_kv(kv_pairs *kvs, char *key, char *value) {
+ if (kvs->num_kvs == kvs->cap_kvs) {
+ kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4);
+ kvs->kvs = gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs);
+ }
+ kvs->kvs[kvs->num_kvs].key = key;
+ kvs->kvs[kvs->num_kvs].value = value;
+ kvs->num_kvs++;
+}
+
+static void collect_kvs(gpr_avl_node *node, char *key(void *k),
+ char *fmt(void *v), kv_pairs *kvs) {
+ if (node == NULL) return;
+ append_kv(kvs, key(node->key), fmt(node->value));
+ collect_kvs(node->left, key, fmt, kvs);
+ collect_kvs(node->right, key, fmt, kvs);
+}
+
+static char *key_int(void *p) {
+ return gpr_strdup(error_int_name((grpc_error_ints)(uintptr_t)p));
+}
+
+static char *key_str(void *p) {
+ return gpr_strdup(error_str_name((grpc_error_strs)(uintptr_t)p));
+}
+
+static char *key_time(void *p) {
+ return gpr_strdup(error_time_name((grpc_error_times)(uintptr_t)p));
+}
+
+static char *fmt_int(void *p) {
+ char *s;
+ gpr_asprintf(&s, "%" PRIdPTR, (intptr_t)p);
+ return s;
+}
+
+static void append_chr(char c, char **s, size_t *sz, size_t *cap) {
+ if (*sz == *cap) {
+ *cap = GPR_MAX(8, 3 * *cap / 2);
+ *s = gpr_realloc(*s, *cap);
+ }
+ (*s)[(*sz)++] = c;
+}
+
+static void append_str(const char *str, char **s, size_t *sz, size_t *cap) {
+ for (const char *c = str; *c; c++) {
+ append_chr(*c, s, sz, cap);
+ }
+}
+
+static void append_esc_str(const char *str, char **s, size_t *sz, size_t *cap) {
+ static const char *hex = "0123456789abcdef";
+ append_chr('"', s, sz, cap);
+ for (const uint8_t *c = (const uint8_t *)str; *c; c++) {
+ if (*c < 32 || *c >= 127) {
+ append_chr('\\', s, sz, cap);
+ switch (*c) {
+ case '\b':
+ append_chr('b', s, sz, cap);
+ break;
+ case '\f':
+ append_chr('f', s, sz, cap);
+ break;
+ case '\n':
+ append_chr('n', s, sz, cap);
+ break;
+ case '\r':
+ append_chr('r', s, sz, cap);
+ break;
+ case '\t':
+ append_chr('t', s, sz, cap);
+ break;
+ default:
+ append_chr('u', s, sz, cap);
+ append_chr('0', s, sz, cap);
+ append_chr('0', s, sz, cap);
+ append_chr(hex[*c >> 4], s, sz, cap);
+ append_chr(hex[*c & 0x0f], s, sz, cap);
+ break;
+ }
+ } else {
+ append_chr((char)*c, s, sz, cap);
+ }
+ }
+ append_chr('"', s, sz, cap);
+}
+
+static char *fmt_str(void *p) {
+ char *s = NULL;
+ size_t sz = 0;
+ size_t cap = 0;
+ append_esc_str(p, &s, &sz, &cap);
+ append_chr(0, &s, &sz, &cap);
+ return s;
+}
+
+static char *fmt_time(void *p) {
+ gpr_timespec tm = *(gpr_timespec *)p;
+ char *out;
+ char *pfx = "!!";
+ switch (tm.clock_type) {
+ case GPR_CLOCK_MONOTONIC:
+ pfx = "@monotonic:";
+ break;
+ case GPR_CLOCK_REALTIME:
+ pfx = "@";
+ break;
+ case GPR_CLOCK_PRECISE:
+ pfx = "@precise:";
+ break;
+ case GPR_TIMESPAN:
+ pfx = "";
+ break;
+ }
+ gpr_asprintf(&out, "\"%s%" PRId64 ".%09d\"", pfx, tm.tv_sec, tm.tv_nsec);
+ return out;
+}
+
+static void add_errs(gpr_avl_node *n, char **s, size_t *sz, size_t *cap) {
+ if (n == NULL) return;
+ add_errs(n->left, s, sz, cap);
+ const char *e = grpc_error_string(n->value);
+ append_str(e, s, sz, cap);
+ grpc_error_free_string(e);
+ add_errs(n->right, s, sz, cap);
+}
+
+static char *errs_string(grpc_error *err) {
+ char *s = NULL;
+ size_t sz = 0;
+ size_t cap = 0;
+ append_chr('[', &s, &sz, &cap);
+ add_errs(err->errs.root, &s, &sz, &cap);
+ append_chr(']', &s, &sz, &cap);
+ append_chr(0, &s, &sz, &cap);
+ return s;
+}
+
+static int cmp_kvs(const void *a, const void *b) {
+ const kv_pair *ka = a;
+ const kv_pair *kb = b;
+ return strcmp(ka->key, kb->key);
+}
+
+static const char *finish_kvs(kv_pairs *kvs) {
+ char *s = NULL;
+ size_t sz = 0;
+ size_t cap = 0;
+
+ append_chr('{', &s, &sz, &cap);
+ for (size_t i = 0; i < kvs->num_kvs; i++) {
+ if (i != 0) append_chr(',', &s, &sz, &cap);
+ append_esc_str(kvs->kvs[i].key, &s, &sz, &cap);
+ gpr_free(kvs->kvs[i].key);
+ append_chr(':', &s, &sz, &cap);
+ append_str(kvs->kvs[i].value, &s, &sz, &cap);
+ gpr_free(kvs->kvs[i].value);
+ }
+ append_chr('}', &s, &sz, &cap);
+ append_chr(0, &s, &sz, &cap);
+
+ gpr_free(kvs->kvs);
+ return s;
+}
+
+void grpc_error_free_string(const char *str) {
+ if (str == no_error_string) return;
+ if (str == oom_error_string) return;
+ if (str == cancelled_error_string) return;
+ gpr_free((char *)str);
+}
+
+const char *grpc_error_string(grpc_error *err) {
+ GPR_TIMER_BEGIN("grpc_error_string", 0);
+ if (err == GRPC_ERROR_NONE) return no_error_string;
+ if (err == GRPC_ERROR_OOM) return oom_error_string;
+ if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string;
+
+ kv_pairs kvs;
+ memset(&kvs, 0, sizeof(kvs));
+
+ collect_kvs(err->ints.root, key_int, fmt_int, &kvs);
+ collect_kvs(err->strs.root, key_str, fmt_str, &kvs);
+ collect_kvs(err->times.root, key_time, fmt_time, &kvs);
+ if (!gpr_avl_is_empty(err->errs)) {
+ append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err));
+ }
+
+ qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs);
+
+ const char *out = finish_kvs(&kvs);
+ GPR_TIMER_END("grpc_error_string", 0);
+ return out;
+}
+
+grpc_error *grpc_os_error(const char *file, int line, int err,
+ const char *call_name) {
+ return grpc_error_set_str(
+ grpc_error_set_str(
+ grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0),
+ GRPC_ERROR_INT_ERRNO, err),
+ GRPC_ERROR_STR_OS_ERROR, strerror(err)),
+ GRPC_ERROR_STR_SYSCALL, call_name);
+}
+
+#ifdef GPR_WINDOWS
+grpc_error *grpc_wsa_error(const char *file, int line, int err,
+ const char *call_name) {
+ char *utf8_message = gpr_format_message(err);
+ grpc_error *error = grpc_error_set_str(
+ grpc_error_set_str(
+ grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0),
+ GRPC_ERROR_INT_WSA_ERROR, err),
+ GRPC_ERROR_STR_OS_ERROR, utf8_message),
+ GRPC_ERROR_STR_SYSCALL, call_name);
+ gpr_free(utf8_message);
+ return error;
+}
+#endif
+
+bool grpc_log_if_error(const char *what, grpc_error *error, const char *file,
+ int line) {
+ if (error == GRPC_ERROR_NONE) return true;
+ const char *msg = grpc_error_string(error);
+ gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg);
+ grpc_error_free_string(msg);
+ GRPC_ERROR_UNREF(error);
+ return false;
+}
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
new file mode 100644
index 0000000000..13f898e31a
--- /dev/null
+++ b/src/core/lib/iomgr/error.h
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <grpc/support/time.h>
+
+/// Opaque representation of an error.
+/// Errors are refcounted objects that represent the result of an operation.
+/// Ownership laws:
+/// if a grpc_error is returned by a function, the caller owns a ref to that
+/// instance
+/// if a grpc_error is passed to a grpc_closure callback function (functions
+/// with the signature:
+/// void (*f)(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error))
+/// then those functions do not automatically own a ref to error
+/// if a grpc_error is passed to *ANY OTHER FUNCTION* then that function takes
+/// ownership of the error
+/// Errors have:
+/// a set of ints, strings, and timestamps that describe the error
+/// always present are:
+/// GRPC_ERROR_STR_FILE, GRPC_ERROR_INT_FILE_LINE - source location the error
+/// was generated
+/// GRPC_ERROR_STR_DESCRIPTION - a human readable description of the error
+/// GRPC_ERROR_TIME_CREATED - a timestamp indicating when the error happened
+/// an error can also have children; these are other errors that are believed
+/// to have contributed to this one. By accumulating children, we can begin
+/// to root cause high level failures from low level failures, without having
+/// to derive execution paths from log lines
+typedef struct grpc_error grpc_error;
+
+typedef enum {
+ /// 'errno' from the operating system
+ GRPC_ERROR_INT_ERRNO,
+ /// __LINE__ from the call site creating the error
+ GRPC_ERROR_INT_FILE_LINE,
+ /// stream identifier: for errors that are associated with an individual
+ /// wire stream
+ GRPC_ERROR_INT_STREAM_ID,
+ /// grpc status code representing this error
+ GRPC_ERROR_INT_GRPC_STATUS,
+ /// offset into some binary blob (usually represented by
+ /// GRPC_ERROR_STR_RAW_BYTES) where the error occurred
+ GRPC_ERROR_INT_OFFSET,
+ /// context sensitive index associated with the error
+ GRPC_ERROR_INT_INDEX,
+ /// context sensitive size associated with the error
+ GRPC_ERROR_INT_SIZE,
+ /// http2 error code associated with the error (see the HTTP2 RFC)
+ GRPC_ERROR_INT_HTTP2_ERROR,
+ /// TSI status code associated with the error
+ GRPC_ERROR_INT_TSI_CODE,
+ /// grpc_security_status associated with the error
+ GRPC_ERROR_INT_SECURITY_STATUS,
+ /// WSAGetLastError() reported when this error occurred
+ GRPC_ERROR_INT_WSA_ERROR,
+ /// File descriptor associated with this error
+ GRPC_ERROR_INT_FD,
+ /// HTTP status (i.e. 404)
+ GRPC_ERROR_INT_HTTP_STATUS,
+ /// context sensitive limit associated with the error
+ GRPC_ERROR_INT_LIMIT,
+} grpc_error_ints;
+
+typedef enum {
+ /// top-level textual description of this error
+ GRPC_ERROR_STR_DESCRIPTION,
+ /// source file in which this error occurred
+ GRPC_ERROR_STR_FILE,
+ /// operating system description of this error
+ GRPC_ERROR_STR_OS_ERROR,
+ /// syscall that generated this error
+ GRPC_ERROR_STR_SYSCALL,
+ /// peer that we were trying to communicate when this error occurred
+ GRPC_ERROR_STR_TARGET_ADDRESS,
+ /// grpc status message associated with this error
+ GRPC_ERROR_STR_GRPC_MESSAGE,
+ /// hex dump (or similar) with the data that generated this error
+ GRPC_ERROR_STR_RAW_BYTES,
+ /// tsi error string associated with this error
+ GRPC_ERROR_STR_TSI_ERROR,
+ /// filename that we were trying to read/write when this error occurred
+ GRPC_ERROR_STR_FILENAME,
+} grpc_error_strs;
+
+typedef enum {
+ /// timestamp of error creation
+ GRPC_ERROR_TIME_CREATED,
+} grpc_error_times;
+
+#define GRPC_ERROR_NONE ((grpc_error *)NULL)
+#define GRPC_ERROR_OOM ((grpc_error *)1)
+#define GRPC_ERROR_CANCELLED ((grpc_error *)2)
+
+const char *grpc_error_string(grpc_error *error);
+void grpc_error_free_string(const char *str);
+
+/// Create an error - but use GRPC_ERROR_CREATE instead
+grpc_error *grpc_error_create(const char *file, int line, const char *desc,
+ grpc_error **referencing, size_t num_referencing);
+/// Create an error (this is the preferred way of generating an error that is
+/// not due to a system call - for system calls, use GRPC_OS_ERROR or
+/// GRPC_WSA_ERROR as appropriate)
+/// \a referencing is an array of num_referencing elements indicating one or
+/// more errors that are believed to have contributed to this one
+/// err = grpc_error_create(x, y, z, r, nr) is equivalent to:
+/// err = grpc_error_create(x, y, z, NULL, 0);
+/// for (i=0; i<nr; i++) err = grpc_error_add_child(err, r[i]);
+#define GRPC_ERROR_CREATE(desc) \
+ grpc_error_create(__FILE__, __LINE__, desc, NULL, 0)
+
+// Create an error that references some other errors. This function adds a
+// reference to each error in errs - it does not consume an existing reference
+#define GRPC_ERROR_CREATE_REFERENCING(desc, errs, count) \
+ grpc_error_create(__FILE__, __LINE__, desc, errs, count)
+
+//#define GRPC_ERROR_REFCOUNT_DEBUG
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
+ const char *func);
+void grpc_error_unref(grpc_error *err, const char *file, int line,
+ const char *func);
+#define GRPC_ERROR_REF(err) grpc_error_ref(err, __FILE__, __LINE__, __func__)
+#define GRPC_ERROR_UNREF(err) \
+ grpc_error_unref(err, __FILE__, __LINE__, __func__)
+#else
+grpc_error *grpc_error_ref(grpc_error *err);
+void grpc_error_unref(grpc_error *err);
+#define GRPC_ERROR_REF(err) grpc_error_ref(err)
+#define GRPC_ERROR_UNREF(err) grpc_error_unref(err)
+#endif
+
+grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
+ intptr_t value) GRPC_MUST_USE_RESULT;
+bool grpc_error_get_int(grpc_error *error, grpc_error_ints which, intptr_t *p);
+grpc_error *grpc_error_set_time(grpc_error *src, grpc_error_times which,
+ gpr_timespec value) GRPC_MUST_USE_RESULT;
+grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
+ const char *value) GRPC_MUST_USE_RESULT;
+const char *grpc_error_get_str(grpc_error *error, grpc_error_strs which);
+/// Add a child error: an error that is believed to have contributed to this
+/// error occurring. Allows root causing high level errors from lower level
+/// errors that contributed to them.
+grpc_error *grpc_error_add_child(grpc_error *src,
+ grpc_error *child) GRPC_MUST_USE_RESULT;
+grpc_error *grpc_os_error(const char *file, int line, int err,
+ const char *call_name) GRPC_MUST_USE_RESULT;
+/// create an error associated with errno!=0 (an 'operating system' error)
+#define GRPC_OS_ERROR(err, call_name) \
+ grpc_os_error(__FILE__, __LINE__, err, call_name)
+grpc_error *grpc_wsa_error(const char *file, int line, int err,
+ const char *call_name) GRPC_MUST_USE_RESULT;
+/// windows only: create an error associated with WSAGetLastError()!=0
+#define GRPC_WSA_ERROR(err, call_name) \
+ grpc_wsa_error(__FILE__, __LINE__, err, call_name)
+
+bool grpc_log_if_error(const char *what, grpc_error *error, const char *file,
+ int line);
+#define GRPC_LOG_IF_ERROR(what, error) \
+ grpc_log_if_error((what), (error), __FILE__, __LINE__)
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */
diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c
new file mode 100644
index 0000000000..cf0fe736a0
--- /dev/null
+++ b/src/core/lib/iomgr/ev_epoll_linux.c
@@ -0,0 +1,1872 @@
+/*
+ *
+ * Copyright 2016, 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/grpc_posix.h>
+#include <grpc/support/port_platform.h>
+
+/* This polling engine is only relevant on linux kernels supporting epoll() */
+#ifdef GPR_LINUX_EPOLL
+
+#include "src/core/lib/iomgr/ev_epoll_linux.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/tls.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/block_annotate.h"
+
+/* TODO: sreek - Move this to init.c and initialize this like other tracers. */
+static int grpc_polling_trace = 0; /* Disabled by default */
+#define GRPC_POLLING_TRACE(fmt, ...) \
+ if (grpc_polling_trace) { \
+ gpr_log(GPR_INFO, (fmt), __VA_ARGS__); \
+ }
+
+static int grpc_wakeup_signal = -1;
+static bool is_grpc_wakeup_signal_initialized = false;
+
+/* Implements the function defined in grpc_posix.h. This function might be
+ * called before even calling grpc_init() to set either a different signal to
+ * use. If signum == -1, then the use of signals is disabled */
+void grpc_use_signal(int signum) {
+ grpc_wakeup_signal = signum;
+ is_grpc_wakeup_signal_initialized = true;
+
+ if (grpc_wakeup_signal < 0) {
+ gpr_log(GPR_INFO,
+ "Use of signals is disabled. Epoll engine will not be used");
+ } else {
+ gpr_log(GPR_INFO, "epoll engine will be using signal: %d",
+ grpc_wakeup_signal);
+ }
+}
+
+struct polling_island;
+
+/*******************************************************************************
+ * Fd Declarations
+ */
+struct grpc_fd {
+ int fd;
+ /* refst format:
+ bit 0 : 1=Active / 0=Orphaned
+ bits 1-n : refcount
+ Ref/Unref by two to avoid altering the orphaned bit */
+ gpr_atm refst;
+
+ gpr_mu mu;
+
+ /* Indicates that the fd is shutdown and that any pending read/write closures
+ should fail */
+ bool shutdown;
+
+ /* The fd is either closed or we relinquished control of it. In either cases,
+ this indicates that the 'fd' on this structure is no longer valid */
+ bool orphaned;
+
+ /* TODO: sreek - Move this to a lockfree implementation */
+ grpc_closure *read_closure;
+ grpc_closure *write_closure;
+
+ /* The polling island to which this fd belongs to and the mutex protecting the
+ the field */
+ gpr_mu pi_mu;
+ struct polling_island *polling_island;
+
+ struct grpc_fd *freelist_next;
+ grpc_closure *on_done_closure;
+
+ /* The pollset that last noticed that the fd is readable */
+ grpc_pollset *read_notifier_pollset;
+
+ grpc_iomgr_object iomgr_object;
+};
+
+/* Reference counting for fds */
+// #define GRPC_FD_REF_COUNT_DEBUG
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line);
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__)
+#else
+static void fd_ref(grpc_fd *fd);
+static void fd_unref(grpc_fd *fd);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd)
+#endif
+
+static void fd_global_init(void);
+static void fd_global_shutdown(void);
+
+#define CLOSURE_NOT_READY ((grpc_closure *)0)
+#define CLOSURE_READY ((grpc_closure *)1)
+
+/*******************************************************************************
+ * Polling island Declarations
+ */
+
+// #define GRPC_PI_REF_COUNT_DEBUG
+#ifdef GRPC_PI_REF_COUNT_DEBUG
+
+#define PI_ADD_REF(p, r) pi_add_ref_dbg((p), (r), __FILE__, __LINE__)
+#define PI_UNREF(p, r) pi_unref_dbg((p), (r), __FILE__, __LINE__)
+
+#else /* defined(GRPC_PI_REF_COUNT_DEBUG) */
+
+#define PI_ADD_REF(p, r) pi_add_ref((p))
+#define PI_UNREF(p, r) pi_unref((p))
+
+#endif /* !defined(GPRC_PI_REF_COUNT_DEBUG) */
+
+typedef struct polling_island {
+ gpr_mu mu;
+ /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement
+ the refcount.
+ Once the ref count becomes zero, this structure is destroyed which means
+ we should ensure that there is never a scenario where a PI_ADD_REF() is
+ racing with a PI_UNREF() that just made the ref_count zero. */
+ gpr_refcount ref_count;
+
+ /* Pointer to the polling_island this merged into.
+ * merged_to value is only set once in polling_island's lifetime (and that too
+ * only if the island is merged with another island). Because of this, we can
+ * use gpr_atm type here so that we can do atomic access on this and reduce
+ * lock contention on 'mu' mutex.
+ *
+ * Note that if this field is not NULL (i.e not 0), all the remaining fields
+ * (except mu and ref_count) are invalid and must be ignored. */
+ gpr_atm merged_to;
+
+ /* The fd of the underlying epoll set */
+ int epoll_fd;
+
+ /* The file descriptors in the epoll set */
+ size_t fd_cnt;
+ size_t fd_capacity;
+ grpc_fd **fds;
+
+ /* Polling islands that are no longer needed are kept in a freelist so that
+ they can be reused. This field points to the next polling island in the
+ free list */
+ struct polling_island *next_free;
+} polling_island;
+
+/*******************************************************************************
+ * Pollset Declarations
+ */
+struct grpc_pollset_worker {
+ /* Thread id of this worker */
+ pthread_t pt_id;
+
+ /* Used to prevent a worker from getting kicked multiple times */
+ gpr_atm is_kicked;
+ struct grpc_pollset_worker *next;
+ struct grpc_pollset_worker *prev;
+};
+
+struct grpc_pollset {
+ gpr_mu mu;
+ grpc_pollset_worker root_worker;
+ bool kicked_without_pollers;
+
+ bool shutting_down; /* Is the pollset shutting down ? */
+ bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */
+ grpc_closure *shutdown_done; /* Called after after shutdown is complete */
+
+ /* The polling island to which this pollset belongs to */
+ struct polling_island *polling_island;
+};
+
+/*******************************************************************************
+ * Pollset-set Declarations
+ */
+/* TODO: sreek - Change the pollset_set implementation such that a pollset_set
+ * directly points to a polling_island (and adding an fd/pollset/pollset_set to
+ * the current pollset_set would result in polling island merges. This would
+ * remove the need to maintain fd_count here. This will also significantly
+ * simplify the grpc_fd structure since we would no longer need to explicitly
+ * maintain the orphaned state */
+struct grpc_pollset_set {
+ gpr_mu mu;
+
+ size_t pollset_count;
+ size_t pollset_capacity;
+ grpc_pollset **pollsets;
+
+ size_t pollset_set_count;
+ size_t pollset_set_capacity;
+ struct grpc_pollset_set **pollset_sets;
+
+ size_t fd_count;
+ size_t fd_capacity;
+ grpc_fd **fds;
+};
+
+/*******************************************************************************
+ * Common helpers
+ */
+
+static void append_error(grpc_error **composite, grpc_error *error,
+ const char *desc) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE(desc);
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+/*******************************************************************************
+ * Polling island Definitions
+ */
+
+/* The wakeup fd that is used to wake up all threads in a Polling island. This
+ is useful in the polling island merge operation where we need to wakeup all
+ the threads currently polling the smaller polling island (so that they can
+ start polling the new/merged polling island)
+
+ NOTE: This fd is initialized to be readable and MUST NOT be consumed i.e the
+ threads that woke up MUST NOT call grpc_wakeup_fd_consume_wakeup() */
+static grpc_wakeup_fd polling_island_wakeup_fd;
+
+/* Polling island freelist */
+static gpr_mu g_pi_freelist_mu;
+static polling_island *g_pi_freelist = NULL;
+
+static void polling_island_delete(); /* Forward declaration */
+
+#ifdef GRPC_TSAN
+/* Currently TSAN may incorrectly flag data races between epoll_ctl and
+ epoll_wait for any grpc_fd structs that are added to the epoll set via
+ epoll_ctl and are returned (within a very short window) via epoll_wait().
+
+ To work-around this race, we establish a happens-before relation between
+ the code just-before epoll_ctl() and the code after epoll_wait() by using
+ this atomic */
+gpr_atm g_epoll_sync;
+#endif /* defined(GRPC_TSAN) */
+
+#ifdef GRPC_PI_REF_COUNT_DEBUG
+void pi_add_ref(polling_island *pi);
+void pi_unref(polling_island *pi);
+
+void pi_add_ref_dbg(polling_island *pi, char *reason, char *file, int line) {
+ long old_cnt = gpr_atm_acq_load(&(pi->ref_count.count));
+ pi_add_ref(pi);
+ gpr_log(GPR_DEBUG, "Add ref pi: %p, old: %ld -> new:%ld (%s) - (%s, %d)",
+ (void *)pi, old_cnt, old_cnt + 1, reason, file, line);
+}
+
+void pi_unref_dbg(polling_island *pi, char *reason, char *file, int line) {
+ long old_cnt = gpr_atm_acq_load(&(pi->ref_count.count));
+ pi_unref(pi);
+ gpr_log(GPR_DEBUG, "Unref pi: %p, old:%ld -> new:%ld (%s) - (%s, %d)",
+ (void *)pi, old_cnt, (old_cnt - 1), reason, file, line);
+}
+#endif
+
+void pi_add_ref(polling_island *pi) { gpr_ref(&pi->ref_count); }
+
+void pi_unref(polling_island *pi) {
+ /* If ref count went to zero, delete the polling island.
+ Note that this deletion not be done under a lock. Once the ref count goes
+ to zero, we are guaranteed that no one else holds a reference to the
+ polling island (and that there is no racing pi_add_ref() call either).
+
+ Also, if we are deleting the polling island and the merged_to field is
+ non-empty, we should remove a ref to the merged_to polling island
+ */
+ if (gpr_unref(&pi->ref_count)) {
+ polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ polling_island_delete(pi);
+ if (next != NULL) {
+ PI_UNREF(next, "pi_delete"); /* Recursive call */
+ }
+ }
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds,
+ size_t fd_count, bool add_fd_refs,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ struct epoll_event ev;
+ char *err_msg;
+ const char *err_desc = "polling_island_add_fds";
+
+#ifdef GRPC_TSAN
+ /* See the definition of g_epoll_sync for more context */
+ gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0);
+#endif /* defined(GRPC_TSAN) */
+
+ for (i = 0; i < fd_count; i++) {
+ ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET);
+ ev.data.ptr = fds[i];
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, fds[i]->fd, &ev);
+
+ if (err < 0) {
+ if (errno != EEXIST) {
+ gpr_asprintf(
+ &err_msg,
+ "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)",
+ pi->epoll_fd, fds[i]->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+
+ continue;
+ }
+
+ if (pi->fd_cnt == pi->fd_capacity) {
+ pi->fd_capacity = GPR_MAX(pi->fd_capacity + 8, pi->fd_cnt * 3 / 2);
+ pi->fds = gpr_realloc(pi->fds, sizeof(grpc_fd *) * pi->fd_capacity);
+ }
+
+ pi->fds[pi->fd_cnt++] = fds[i];
+ if (add_fd_refs) {
+ GRPC_FD_REF(fds[i], "polling_island");
+ }
+ }
+}
+
+/* The caller is expected to hold pi->mu before calling this */
+static void polling_island_add_wakeup_fd_locked(polling_island *pi,
+ grpc_wakeup_fd *wakeup_fd,
+ grpc_error **error) {
+ struct epoll_event ev;
+ int err;
+ char *err_msg;
+ const char *err_desc = "polling_island_add_wakeup_fd";
+
+ ev.events = (uint32_t)(EPOLLIN | EPOLLET);
+ ev.data.ptr = wakeup_fd;
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD,
+ GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev);
+ if (err < 0 && errno != EEXIST) {
+ gpr_asprintf(&err_msg,
+ "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with "
+ "error: %d (%s)",
+ pi->epoll_fd,
+ GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), errno,
+ strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_remove_all_fds_locked(polling_island *pi,
+ bool remove_fd_refs,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ char *err_msg;
+ const char *err_desc = "polling_island_remove_fds";
+
+ for (i = 0; i < pi->fd_cnt; i++) {
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL);
+ if (err < 0 && errno != ENOENT) {
+ gpr_asprintf(&err_msg,
+ "epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with "
+ "error: %d (%s)",
+ pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+
+ if (remove_fd_refs) {
+ GRPC_FD_UNREF(pi->fds[i], "polling_island");
+ }
+ }
+
+ pi->fd_cnt = 0;
+}
+
+/* The caller is expected to hold pi->mu lock before calling this function */
+static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd,
+ bool is_fd_closed,
+ grpc_error **error) {
+ int err;
+ size_t i;
+ char *err_msg;
+ const char *err_desc = "polling_island_remove_fd";
+
+ /* If fd is already closed, then it would have been automatically been removed
+ from the epoll set */
+ if (!is_fd_closed) {
+ err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL);
+ if (err < 0 && errno != ENOENT) {
+ gpr_asprintf(
+ &err_msg,
+ "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)",
+ pi->epoll_fd, fd->fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ }
+ }
+
+ for (i = 0; i < pi->fd_cnt; i++) {
+ if (pi->fds[i] == fd) {
+ pi->fds[i] = pi->fds[--pi->fd_cnt];
+ GRPC_FD_UNREF(fd, "polling_island");
+ break;
+ }
+ }
+}
+
+/* Might return NULL in case of an error */
+static polling_island *polling_island_create(grpc_fd *initial_fd,
+ grpc_error **error) {
+ polling_island *pi = NULL;
+ char *err_msg;
+ const char *err_desc = "polling_island_create";
+
+ /* Try to get one from the polling island freelist */
+ gpr_mu_lock(&g_pi_freelist_mu);
+ if (g_pi_freelist != NULL) {
+ pi = g_pi_freelist;
+ g_pi_freelist = g_pi_freelist->next_free;
+ pi->next_free = NULL;
+ }
+ gpr_mu_unlock(&g_pi_freelist_mu);
+
+ /* Create new polling island if we could not get one from the free list */
+ if (pi == NULL) {
+ pi = gpr_malloc(sizeof(*pi));
+ gpr_mu_init(&pi->mu);
+ pi->fd_cnt = 0;
+ pi->fd_capacity = 0;
+ pi->fds = NULL;
+ }
+
+ gpr_ref_init(&pi->ref_count, 0);
+ gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL);
+
+ pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (pi->epoll_fd < 0) {
+ gpr_asprintf(&err_msg, "epoll_create1 failed with error %d (%s)", errno,
+ strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ gpr_free(err_msg);
+ } else {
+ polling_island_add_wakeup_fd_locked(pi, &grpc_global_wakeup_fd, error);
+ pi->next_free = NULL;
+
+ if (initial_fd != NULL) {
+ /* Lock the polling island here just in case we got this structure from
+ the freelist and the polling island lock was not released yet (by the
+ code that adds the polling island to the freelist) */
+ gpr_mu_lock(&pi->mu);
+ polling_island_add_fds_locked(pi, &initial_fd, 1, true, error);
+ gpr_mu_unlock(&pi->mu);
+ }
+ }
+
+ return pi;
+}
+
+static void polling_island_delete(polling_island *pi) {
+ GPR_ASSERT(pi->fd_cnt == 0);
+
+ gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL);
+
+ close(pi->epoll_fd);
+ pi->epoll_fd = -1;
+
+ gpr_mu_lock(&g_pi_freelist_mu);
+ pi->next_free = g_pi_freelist;
+ g_pi_freelist = pi;
+ gpr_mu_unlock(&g_pi_freelist_mu);
+}
+
+/* Attempts to gets the last polling island in the linked list (liked by the
+ * 'merged_to' field). Since this does not lock the polling island, there are no
+ * guarantees that the island returned is the last island */
+static polling_island *polling_island_maybe_get_latest(polling_island *pi) {
+ polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ while (next != NULL) {
+ pi = next;
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ }
+
+ return pi;
+}
+
+/* Gets the lock on the *latest* polling island i.e the last polling island in
+ the linked list (linked by the 'merged_to' field). Call gpr_mu_unlock on the
+ returned polling island's mu.
+ Usage: To lock/unlock polling island "pi", do the following:
+ polling_island *pi_latest = polling_island_lock(pi);
+ ...
+ ... critical section ..
+ ...
+ gpr_mu_unlock(&pi_latest->mu); // NOTE: use pi_latest->mu. NOT pi->mu */
+static polling_island *polling_island_lock(polling_island *pi) {
+ polling_island *next = NULL;
+
+ while (true) {
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ if (next == NULL) {
+ /* Looks like 'pi' is the last node in the linked list but unless we check
+ this by holding the pi->mu lock, we cannot be sure (i.e without the
+ pi->mu lock, we don't prevent island merges).
+ To be absolutely sure, check once more by holding the pi->mu lock */
+ gpr_mu_lock(&pi->mu);
+ next = (polling_island *)gpr_atm_acq_load(&pi->merged_to);
+ if (next == NULL) {
+ /* pi is infact the last node and we have the pi->mu lock. we're done */
+ break;
+ }
+
+ /* pi->merged_to is not NULL i.e pi isn't the last node anymore. pi->mu
+ * isn't the lock we are interested in. Continue traversing the list */
+ gpr_mu_unlock(&pi->mu);
+ }
+
+ pi = next;
+ }
+
+ return pi;
+}
+
+/* Gets the lock on the *latest* polling islands in the linked lists pointed by
+ *p and *q (and also updates *p and *q to point to the latest polling islands)
+
+ This function is needed because calling the following block of code to obtain
+ locks on polling islands (*p and *q) is prone to deadlocks.
+ {
+ polling_island_lock(*p, true);
+ polling_island_lock(*q, true);
+ }
+
+ Usage/example:
+ polling_island *p1;
+ polling_island *p2;
+ ..
+ polling_island_lock_pair(&p1, &p2);
+ ..
+ .. Critical section with both p1 and p2 locked
+ ..
+ // Release locks: Always call polling_island_unlock_pair() to release locks
+ polling_island_unlock_pair(p1, p2);
+*/
+static void polling_island_lock_pair(polling_island **p, polling_island **q) {
+ polling_island *pi_1 = *p;
+ polling_island *pi_2 = *q;
+ polling_island *next_1 = NULL;
+ polling_island *next_2 = NULL;
+
+ /* The algorithm is simple:
+ - Go to the last polling islands in the linked lists *pi_1 and *pi_2 (and
+ keep updating pi_1 and pi_2)
+ - Then obtain locks on the islands by following a lock order rule of
+ locking polling_island with lower address first
+ Special case: Before obtaining the locks, check if pi_1 and pi_2 are
+ pointing to the same island. If that is the case, we can just call
+ polling_island_lock()
+ - After obtaining both the locks, double check that the polling islands
+ are still the last polling islands in their respective linked lists
+ (this is because there might have been polling island merges before
+ we got the lock)
+ - If the polling islands are the last islands, we are done. If not,
+ release the locks and continue the process from the first step */
+ while (true) {
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ while (next_1 != NULL) {
+ pi_1 = next_1;
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ }
+
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ while (next_2 != NULL) {
+ pi_2 = next_2;
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ }
+
+ if (pi_1 == pi_2) {
+ pi_1 = pi_2 = polling_island_lock(pi_1);
+ break;
+ }
+
+ if (pi_1 < pi_2) {
+ gpr_mu_lock(&pi_1->mu);
+ gpr_mu_lock(&pi_2->mu);
+ } else {
+ gpr_mu_lock(&pi_2->mu);
+ gpr_mu_lock(&pi_1->mu);
+ }
+
+ next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to);
+ next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to);
+ if (next_1 == NULL && next_2 == NULL) {
+ break;
+ }
+
+ gpr_mu_unlock(&pi_1->mu);
+ gpr_mu_unlock(&pi_2->mu);
+ }
+
+ *p = pi_1;
+ *q = pi_2;
+}
+
+static void polling_island_unlock_pair(polling_island *p, polling_island *q) {
+ if (p == q) {
+ gpr_mu_unlock(&p->mu);
+ } else {
+ gpr_mu_unlock(&p->mu);
+ gpr_mu_unlock(&q->mu);
+ }
+}
+
+static polling_island *polling_island_merge(polling_island *p,
+ polling_island *q,
+ grpc_error **error) {
+ /* Get locks on both the polling islands */
+ polling_island_lock_pair(&p, &q);
+
+ if (p != q) {
+ /* Make sure that p points to the polling island with fewer fds than q */
+ if (p->fd_cnt > q->fd_cnt) {
+ GPR_SWAP(polling_island *, p, q);
+ }
+
+ /* Merge p with q i.e move all the fds from p (The one with fewer fds) to q
+ Note that the refcounts on the fds being moved will not change here.
+ This is why the last param in the following two functions is 'false') */
+ polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error);
+ polling_island_remove_all_fds_locked(p, false, error);
+
+ /* Wakeup all the pollers (if any) on p so that they pickup this change */
+ polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error);
+
+ /* Add the 'merged_to' link from p --> q */
+ gpr_atm_rel_store(&p->merged_to, (gpr_atm)q);
+ PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */
+ }
+ /* else if p == q, nothing needs to be done */
+
+ polling_island_unlock_pair(p, q);
+
+ /* Return the merged polling island (Note that no merge would have happened
+ if p == q which is ok) */
+ return q;
+}
+
+static grpc_error *polling_island_global_init() {
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_init(&g_pi_freelist_mu);
+ g_pi_freelist = NULL;
+
+ error = grpc_wakeup_fd_init(&polling_island_wakeup_fd);
+ if (error == GRPC_ERROR_NONE) {
+ error = grpc_wakeup_fd_wakeup(&polling_island_wakeup_fd);
+ }
+
+ return error;
+}
+
+static void polling_island_global_shutdown() {
+ polling_island *next;
+ gpr_mu_lock(&g_pi_freelist_mu);
+ gpr_mu_unlock(&g_pi_freelist_mu);
+ while (g_pi_freelist != NULL) {
+ next = g_pi_freelist->next_free;
+ gpr_mu_destroy(&g_pi_freelist->mu);
+ gpr_free(g_pi_freelist->fds);
+ gpr_free(g_pi_freelist);
+ g_pi_freelist = next;
+ }
+ gpr_mu_destroy(&g_pi_freelist_mu);
+
+ grpc_wakeup_fd_destroy(&polling_island_wakeup_fd);
+}
+
+/*******************************************************************************
+ * Fd Definitions
+ */
+
+/* We need to keep a freelist not because of any concerns of malloc performance
+ * but instead so that implementations with multiple threads in (for example)
+ * epoll_wait deal with the race between pollset removal and incoming poll
+ * notifications.
+ *
+ * The problem is that the poller ultimately holds a reference to this
+ * object, so it is very difficult to know when is safe to free it, at least
+ * without some expensive synchronization.
+ *
+ * If we keep the object freelisted, in the worst case losing this race just
+ * becomes a spurious read notification on a reused fd.
+ */
+
+/* The alarm system needs to be able to wakeup 'some poller' sometimes
+ * (specifically when a new alarm needs to be triggered earlier than the next
+ * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a
+ * case occurs. */
+
+/* TODO: sreek: Right now, this wakes up all pollers. In future we should make
+ * sure to wake up one polling thread (which can wake up other threads if
+ * needed) */
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
+static grpc_fd *fd_freelist = NULL;
+static gpr_mu fd_freelist_mu;
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
+#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 %ld -> %ld [%s; %s:%d]", fd->fd,
+ (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
+ gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
+#else
+#define REF_BY(fd, n, reason) ref_by(fd, n)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n)
+static void ref_by(grpc_fd *fd, int n) {
+#endif
+ GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
+}
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file,
+ int line) {
+ gpr_atm old;
+ gpr_log(GPR_DEBUG, "FD %d %p unref %d %ld -> %ld [%s; %s:%d]", fd->fd,
+ (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
+ gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line);
+#else
+static void unref_by(grpc_fd *fd, int n) {
+ gpr_atm old;
+#endif
+ old = gpr_atm_full_fetch_add(&fd->refst, -n);
+ if (old == n) {
+ /* Add the fd to the freelist */
+ gpr_mu_lock(&fd_freelist_mu);
+ fd->freelist_next = fd_freelist;
+ fd_freelist = fd;
+ grpc_iomgr_unregister_object(&fd->iomgr_object);
+
+ gpr_mu_unlock(&fd_freelist_mu);
+ } else {
+ GPR_ASSERT(old > n);
+ }
+}
+
+/* Increment refcount by two to avoid changing the orphan bit */
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ ref_by(fd, 2, reason, file, line);
+}
+
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ unref_by(fd, 2, reason, file, line);
+}
+#else
+static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); }
+static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
+#endif
+
+static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
+
+static void fd_global_shutdown(void) {
+ gpr_mu_lock(&fd_freelist_mu);
+ gpr_mu_unlock(&fd_freelist_mu);
+ while (fd_freelist != NULL) {
+ grpc_fd *fd = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ gpr_mu_destroy(&fd->mu);
+ gpr_free(fd);
+ }
+ gpr_mu_destroy(&fd_freelist_mu);
+}
+
+static grpc_fd *fd_create(int fd, const char *name) {
+ grpc_fd *new_fd = NULL;
+
+ gpr_mu_lock(&fd_freelist_mu);
+ if (fd_freelist != NULL) {
+ new_fd = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ }
+ gpr_mu_unlock(&fd_freelist_mu);
+
+ if (new_fd == NULL) {
+ new_fd = gpr_malloc(sizeof(grpc_fd));
+ gpr_mu_init(&new_fd->mu);
+ gpr_mu_init(&new_fd->pi_mu);
+ }
+
+ /* Note: It is not really needed to get the new_fd->mu lock here. If this is a
+ newly created fd (or an fd we got from the freelist), no one else would be
+ holding a lock to it anyway. */
+ gpr_mu_lock(&new_fd->mu);
+
+ gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
+ new_fd->fd = fd;
+ new_fd->shutdown = false;
+ new_fd->orphaned = false;
+ new_fd->read_closure = CLOSURE_NOT_READY;
+ new_fd->write_closure = CLOSURE_NOT_READY;
+ new_fd->polling_island = NULL;
+ new_fd->freelist_next = NULL;
+ new_fd->on_done_closure = NULL;
+ new_fd->read_notifier_pollset = NULL;
+
+ gpr_mu_unlock(&new_fd->mu);
+
+ char *fd_name;
+ gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
+ grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+ gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, (void *)new_fd, fd_name);
+#endif
+ gpr_free(fd_name);
+ return new_fd;
+}
+
+static bool fd_is_orphaned(grpc_fd *fd) {
+ return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
+}
+
+static int fd_wrapped_fd(grpc_fd *fd) {
+ int ret_fd = -1;
+ gpr_mu_lock(&fd->mu);
+ if (!fd->orphaned) {
+ ret_fd = fd->fd;
+ }
+ gpr_mu_unlock(&fd->mu);
+
+ return ret_fd;
+}
+
+static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *on_done, int *release_fd,
+ const char *reason) {
+ bool is_fd_closed = false;
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_lock(&fd->mu);
+ fd->on_done_closure = on_done;
+
+ /* If release_fd is not NULL, we should be relinquishing control of the file
+ descriptor fd->fd (but we still own the grpc_fd structure). */
+ if (release_fd != NULL) {
+ *release_fd = fd->fd;
+ } else {
+ close(fd->fd);
+ is_fd_closed = true;
+ }
+
+ fd->orphaned = true;
+
+ /* Remove the active status but keep referenced. We want this grpc_fd struct
+ to be alive (and not added to freelist) until the end of this function */
+ REF_BY(fd, 1, reason);
+
+ /* Remove the fd from the polling island:
+ - Get a lock on the latest polling island (i.e the last island in the
+ linked list pointed by fd->polling_island). This is the island that
+ would actually contain the fd
+ - Remove the fd from the latest polling island
+ - Unlock the latest polling island
+ - Set fd->polling_island to NULL (but remove the ref on the polling island
+ before doing this.) */
+ gpr_mu_lock(&fd->pi_mu);
+ if (fd->polling_island != NULL) {
+ polling_island *pi_latest = polling_island_lock(fd->polling_island);
+ polling_island_remove_fd_locked(pi_latest, fd, is_fd_closed, &error);
+ gpr_mu_unlock(&pi_latest->mu);
+
+ PI_UNREF(fd->polling_island, "fd_orphan");
+ fd->polling_island = NULL;
+ }
+ gpr_mu_unlock(&fd->pi_mu);
+
+ grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, error, NULL);
+
+ gpr_mu_unlock(&fd->mu);
+ UNREF_BY(fd, 2, reason); /* Drop the reference */
+ GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error));
+}
+
+static grpc_error *fd_shutdown_error(bool shutdown) {
+ if (!shutdown) {
+ return GRPC_ERROR_NONE;
+ } else {
+ return GRPC_ERROR_CREATE("FD shutdown");
+ }
+}
+
+static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st, grpc_closure *closure) {
+ if (fd->shutdown) {
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
+ NULL);
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready ==> switch to a waiting state by setting the closure */
+ *st = closure;
+ } else if (*st == CLOSURE_READY) {
+ /* already ready ==> queue the closure to run immediately */
+ *st = CLOSURE_NOT_READY;
+ grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+ NULL);
+ } else {
+ /* upcallptr was set to a different closure. This is an error! */
+ gpr_log(GPR_ERROR,
+ "User called a notify_on function with a previous callback still "
+ "pending");
+ abort();
+ }
+}
+
+/* returns 1 if state becomes not ready */
+static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st) {
+ if (*st == CLOSURE_READY) {
+ /* duplicate ready ==> ignore */
+ return 0;
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready, and not waiting ==> flag ready */
+ *st = CLOSURE_READY;
+ return 0;
+ } else {
+ /* waiting ==> queue closure */
+ grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
+ *st = CLOSURE_NOT_READY;
+ return 1;
+ }
+}
+
+static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_fd *fd) {
+ grpc_pollset *notifier = NULL;
+
+ gpr_mu_lock(&fd->mu);
+ notifier = fd->read_notifier_pollset;
+ gpr_mu_unlock(&fd->mu);
+
+ return notifier;
+}
+
+static bool fd_is_shutdown(grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ const bool r = fd->shutdown;
+ gpr_mu_unlock(&fd->mu);
+ return r;
+}
+
+/* Might be called multiple times */
+static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ /* Do the actual shutdown only once */
+ if (!fd->shutdown) {
+ fd->shutdown = true;
+
+ shutdown(fd->fd, SHUT_RDWR);
+ /* Flush any pending read and write closures. Since fd->shutdown is 'true'
+ at this point, the closures would be called with 'success = false' */
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ }
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->read_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->write_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+/*******************************************************************************
+ * Pollset Definitions
+ */
+GPR_TLS_DECL(g_current_thread_pollset);
+GPR_TLS_DECL(g_current_thread_worker);
+static __thread bool g_initialized_sigmask;
+static __thread sigset_t g_orig_sigmask;
+
+static void sig_handler(int sig_num) {
+#ifdef GRPC_EPOLL_DEBUG
+ gpr_log(GPR_INFO, "Received signal %d", sig_num);
+#endif
+}
+
+static void poller_kick_init() { signal(grpc_wakeup_signal, sig_handler); }
+
+/* Global state management */
+static grpc_error *pollset_global_init(void) {
+ gpr_tls_init(&g_current_thread_pollset);
+ gpr_tls_init(&g_current_thread_worker);
+ poller_kick_init();
+ return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+}
+
+static void pollset_global_shutdown(void) {
+ grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd);
+ gpr_tls_destroy(&g_current_thread_pollset);
+ gpr_tls_destroy(&g_current_thread_worker);
+}
+
+static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) {
+ grpc_error *err = GRPC_ERROR_NONE;
+
+ /* Kick the worker only if it was not already kicked */
+ if (gpr_atm_no_barrier_cas(&worker->is_kicked, (gpr_atm)0, (gpr_atm)1)) {
+ GRPC_POLLING_TRACE(
+ "pollset_worker_kick: Kicking worker: %p (thread id: %ld)",
+ (void *)worker, worker->pt_id);
+ int err_num = pthread_kill(worker->pt_id, grpc_wakeup_signal);
+ if (err_num != 0) {
+ err = GRPC_OS_ERROR(err_num, "pthread_kill");
+ }
+ }
+ return err;
+}
+
+/* Return 1 if the pollset has active threads in pollset_work (pollset must
+ * be locked) */
+static int pollset_has_workers(grpc_pollset *p) {
+ return p->root_worker.next != &p->root_worker;
+}
+
+static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev->next = worker->next;
+ worker->next->prev = worker->prev;
+}
+
+static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) {
+ if (pollset_has_workers(p)) {
+ grpc_pollset_worker *w = p->root_worker.next;
+ remove_worker(p, w);
+ return w;
+ } else {
+ return NULL;
+ }
+}
+
+static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->next = &p->root_worker;
+ worker->prev = worker->next->prev;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev = &p->root_worker;
+ worker->next = worker->prev->next;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+/* p->mu must be held before calling this function */
+static grpc_error *pollset_kick(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker) {
+ GPR_TIMER_BEGIN("pollset_kick", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
+ const char *err_desc = "Kick Failure";
+ grpc_pollset_worker *worker = specific_worker;
+ if (worker != NULL) {
+ if (worker == GRPC_POLLSET_KICK_BROADCAST) {
+ if (pollset_has_workers(p)) {
+ GPR_TIMER_BEGIN("pollset_kick.broadcast", 0);
+ for (worker = p->root_worker.next; worker != &p->root_worker;
+ worker = worker->next) {
+ if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) {
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ }
+ }
+ GPR_TIMER_END("pollset_kick.broadcast", 0);
+ } else {
+ p->kicked_without_pollers = true;
+ }
+ } else {
+ GPR_TIMER_MARK("kicked_specifically", 0);
+ if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) {
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ }
+ }
+ } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) {
+ /* Since worker == NULL, it means that we can kick "any" worker on this
+ pollset 'p'. If 'p' happens to be the same pollset this thread is
+ currently polling (i.e in pollset_work() function), then there is no need
+ to kick any other worker since the current thread can just absorb the
+ kick. This is the reason why we enter this case only when
+ g_current_thread_pollset is != p */
+
+ GPR_TIMER_MARK("kick_anonymous", 0);
+ worker = pop_front_worker(p);
+ if (worker != NULL) {
+ GPR_TIMER_MARK("finally_kick", 0);
+ push_back_worker(p, worker);
+ append_error(&error, pollset_worker_kick(worker), err_desc);
+ } else {
+ GPR_TIMER_MARK("kicked_no_pollers", 0);
+ p->kicked_without_pollers = true;
+ }
+ }
+
+ GPR_TIMER_END("pollset_kick", 0);
+ GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error));
+ return error;
+}
+
+static grpc_error *kick_poller(void) {
+ return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
+
+static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
+ gpr_mu_init(&pollset->mu);
+ *mu = &pollset->mu;
+
+ pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
+ pollset->kicked_without_pollers = false;
+
+ pollset->shutting_down = false;
+ pollset->finish_shutdown_called = false;
+ pollset->shutdown_done = NULL;
+
+ pollset->polling_island = NULL;
+}
+
+/* Convert a timespec to milliseconds:
+ - Very small or negative poll times are clamped to zero to do a non-blocking
+ poll (which becomes spin polling)
+ - Other small values are rounded up to one millisecond
+ - Longer than a millisecond polls are rounded up to the next nearest
+ millisecond to avoid spinning
+ - Infinite timeouts are converted to -1 */
+static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
+ gpr_timespec now) {
+ gpr_timespec timeout;
+ static const int64_t max_spin_polling_us = 10;
+ if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
+ return -1;
+ }
+
+ if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
+ max_spin_polling_us,
+ GPR_TIMESPAN))) <= 0) {
+ return 0;
+ }
+ timeout = gpr_time_sub(deadline, now);
+ return gpr_time_to_millis(gpr_time_add(
+ timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
+}
+
+static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_pollset *notifier) {
+ /* Need the fd->mu since we might be racing with fd_notify_on_read */
+ gpr_mu_lock(&fd->mu);
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ fd->read_notifier_pollset = notifier;
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ /* Need the fd->mu since we might be racing with fd_notify_on_write */
+ gpr_mu_lock(&fd->mu);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void pollset_release_polling_island(grpc_pollset *ps, char *reason) {
+ if (ps->polling_island != NULL) {
+ PI_UNREF(ps->polling_island, reason);
+ }
+ ps->polling_island = NULL;
+}
+
+static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset) {
+ /* The pollset cannot have any workers if we are at this stage */
+ GPR_ASSERT(!pollset_has_workers(pollset));
+
+ pollset->finish_shutdown_called = true;
+
+ /* Release the ref and set pollset->polling_island to NULL */
+ pollset_release_polling_island(pollset, "ps_shutdown");
+ grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
+}
+
+/* pollset->mu lock must be held by the caller before calling this */
+static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_closure *closure) {
+ GPR_TIMER_BEGIN("pollset_shutdown", 0);
+ GPR_ASSERT(!pollset->shutting_down);
+ pollset->shutting_down = true;
+ pollset->shutdown_done = closure;
+ pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+
+ /* If the pollset has any workers, we cannot call finish_shutdown_locked()
+ because it would release the underlying polling island. In such a case, we
+ let the last worker call finish_shutdown_locked() from pollset_work() */
+ if (!pollset_has_workers(pollset)) {
+ GPR_ASSERT(!pollset->finish_shutdown_called);
+ GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0);
+ finish_shutdown_locked(exec_ctx, pollset);
+ }
+ GPR_TIMER_END("pollset_shutdown", 0);
+}
+
+/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other
+ * than destroying the mutexes, there is nothing special that needs to be done
+ * here */
+static void pollset_destroy(grpc_pollset *pollset) {
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ gpr_mu_destroy(&pollset->mu);
+}
+
+static void pollset_reset(grpc_pollset *pollset) {
+ GPR_ASSERT(pollset->shutting_down);
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ pollset->shutting_down = false;
+ pollset->finish_shutdown_called = false;
+ pollset->kicked_without_pollers = false;
+ pollset->shutdown_done = NULL;
+ pollset_release_polling_island(pollset, "ps_reset");
+}
+
+#define GRPC_EPOLL_MAX_EVENTS 1000
+/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */
+static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ grpc_pollset_worker *worker, int timeout_ms,
+ sigset_t *sig_mask, grpc_error **error) {
+ struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
+ int epoll_fd = -1;
+ int ep_rv;
+ polling_island *pi = NULL;
+ char *err_msg;
+ const char *err_desc = "pollset_work_and_unlock";
+ GPR_TIMER_BEGIN("pollset_work_and_unlock", 0);
+
+ /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the
+ latest polling island pointed by pollset->polling_island.
+
+ Since epoll_fd is immutable, we can read it without obtaining the polling
+ island lock. There is however a possibility that the polling island (from
+ which we got the epoll_fd) got merged with another island while we are
+ in this function. This is still okay because in such a case, we will wakeup
+ right-away from epoll_wait() and pick up the latest polling_island the next
+ this function (i.e pollset_work_and_unlock()) is called */
+
+ if (pollset->polling_island == NULL) {
+ pollset->polling_island = polling_island_create(NULL, error);
+ if (pollset->polling_island == NULL) {
+ GPR_TIMER_END("pollset_work_and_unlock", 0);
+ return; /* Fatal error. We cannot continue */
+ }
+
+ PI_ADD_REF(pollset->polling_island, "ps");
+ GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p",
+ (void *)pollset, (void *)pollset->polling_island);
+ }
+
+ pi = polling_island_maybe_get_latest(pollset->polling_island);
+ epoll_fd = pi->epoll_fd;
+
+ /* Update the pollset->polling_island since the island being pointed by
+ pollset->polling_island maybe older than the one pointed by pi) */
+ if (pollset->polling_island != pi) {
+ /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the
+ polling island to be deleted */
+ PI_ADD_REF(pi, "ps");
+ PI_UNREF(pollset->polling_island, "ps");
+ pollset->polling_island = pi;
+ }
+
+ /* Add an extra ref so that the island does not get destroyed (which means
+ the epoll_fd won't be closed) while we are are doing an epoll_wait() on the
+ epoll_fd */
+ PI_ADD_REF(pi, "ps_work");
+ gpr_mu_unlock(&pollset->mu);
+
+ do {
+ ep_rv = epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms,
+ sig_mask);
+ if (ep_rv < 0) {
+ if (errno != EINTR) {
+ gpr_asprintf(&err_msg,
+ "epoll_wait() epoll fd: %d failed with error: %d (%s)",
+ epoll_fd, errno, strerror(errno));
+ append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc);
+ } else {
+ /* We were interrupted. Save an interation by doing a zero timeout
+ epoll_wait to see if there are any other events of interest */
+ GRPC_POLLING_TRACE(
+ "pollset_work: pollset: %p, worker: %p received kick",
+ (void *)pollset, (void *)worker);
+ ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0);
+ }
+ }
+
+#ifdef GRPC_TSAN
+ /* See the definition of g_poll_sync for more details */
+ gpr_atm_acq_load(&g_epoll_sync);
+#endif /* defined(GRPC_TSAN) */
+
+ for (int i = 0; i < ep_rv; ++i) {
+ void *data_ptr = ep_ev[i].data.ptr;
+ if (data_ptr == &grpc_global_wakeup_fd) {
+ append_error(error,
+ grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd),
+ err_desc);
+ } else if (data_ptr == &polling_island_wakeup_fd) {
+ GRPC_POLLING_TRACE(
+ "pollset_work: pollset: %p, worker: %p polling island (epoll_fd: "
+ "%d) got merged",
+ (void *)pollset, (void *)worker, epoll_fd);
+ /* This means that our polling island is merged with a different
+ island. We do not have to do anything here since the subsequent call
+ to the function pollset_work_and_unlock() will pick up the correct
+ epoll_fd */
+ } else {
+ grpc_fd *fd = data_ptr;
+ int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
+ int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
+ int write_ev = ep_ev[i].events & EPOLLOUT;
+ if (read_ev || cancel) {
+ fd_become_readable(exec_ctx, fd, pollset);
+ }
+ if (write_ev || cancel) {
+ fd_become_writable(exec_ctx, fd);
+ }
+ }
+ }
+ } while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
+
+ GPR_ASSERT(pi != NULL);
+
+ /* Before leaving, release the extra ref we added to the polling island. It
+ is important to use "pi" here (i.e our old copy of pollset->polling_island
+ that we got before releasing the polling island lock). This is because
+ pollset->polling_island pointer might get udpated in other parts of the
+ code when there is an island merge while we are doing epoll_wait() above */
+ PI_UNREF(pi, "ps_work");
+
+ GPR_TIMER_END("pollset_work_and_unlock", 0);
+}
+
+/* pollset->mu lock must be held by the caller before calling this.
+ The function pollset_work() may temporarily release the lock (pollset->mu)
+ during the course of its execution but it will always re-acquire the lock and
+ ensure that it is held by the time the function returns */
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker_hdl,
+ gpr_timespec now, gpr_timespec deadline) {
+ GPR_TIMER_BEGIN("pollset_work", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
+ int timeout_ms = poll_deadline_to_millis_timeout(deadline, now);
+
+ sigset_t new_mask;
+
+ grpc_pollset_worker worker;
+ worker.next = worker.prev = NULL;
+ worker.pt_id = pthread_self();
+ gpr_atm_no_barrier_store(&worker.is_kicked, (gpr_atm)0);
+
+ *worker_hdl = &worker;
+
+ gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset);
+ gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
+
+ if (pollset->kicked_without_pollers) {
+ /* If the pollset was kicked without pollers, pretend that the current
+ worker got the kick and skip polling. A kick indicates that there is some
+ work that needs attention like an event on the completion queue or an
+ alarm */
+ GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0);
+ pollset->kicked_without_pollers = 0;
+ } else if (!pollset->shutting_down) {
+ /* We use the posix-signal with number 'grpc_wakeup_signal' for waking up
+ (i.e 'kicking') a worker in the pollset. A 'kick' is a way to inform the
+ worker that there is some pending work that needs immediate attention
+ (like an event on the completion queue, or a polling island merge that
+ results in a new epoll-fd to wait on) and that the worker should not
+ spend time waiting in epoll_pwait().
+
+ A worker can be kicked anytime from the point it is added to the pollset
+ via push_front_worker() (or push_back_worker()) to the point it is
+ removed via remove_worker().
+ If the worker is kicked before/during it calls epoll_pwait(), it should
+ immediately exit from epoll_wait(). If the worker is kicked after it
+ returns from epoll_wait(), then nothing really needs to be done.
+
+ To accomplish this, we mask 'grpc_wakeup_signal' on this thread at all
+ times *except* when it is in epoll_pwait(). This way, the worker never
+ misses acting on a kick */
+
+ if (!g_initialized_sigmask) {
+ sigemptyset(&new_mask);
+ sigaddset(&new_mask, grpc_wakeup_signal);
+ pthread_sigmask(SIG_BLOCK, &new_mask, &g_orig_sigmask);
+ sigdelset(&g_orig_sigmask, grpc_wakeup_signal);
+ g_initialized_sigmask = true;
+ /* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'.
+ This is the mask used at all times *except during
+ epoll_wait()*"
+ g_orig_sigmask: The thread mask which allows 'grpc_wakeup_signal' and
+ this is the mask to use *during epoll_wait()*
+
+ The new_mask is set on the worker before it is added to the pollset
+ (i.e before it can be kicked) */
+ }
+
+ push_front_worker(pollset, &worker); /* Add worker to pollset */
+
+ pollset_work_and_unlock(exec_ctx, pollset, &worker, timeout_ms,
+ &g_orig_sigmask, &error);
+ grpc_exec_ctx_flush(exec_ctx);
+
+ gpr_mu_lock(&pollset->mu);
+
+ /* Note: There is no need to reset worker.is_kicked to 0 since we are no
+ longer going to use this worker */
+ remove_worker(pollset, &worker);
+ }
+
+ /* If we are the last worker on the pollset (i.e pollset_has_workers() is
+ false at this point) and the pollset is shutting down, we may have to
+ finish the shutdown process by calling finish_shutdown_locked().
+ See pollset_shutdown() for more details.
+
+ Note: Continuing to access pollset here is safe; it is the caller's
+ responsibility to not destroy a pollset when it has outstanding calls to
+ pollset_work() */
+ if (pollset->shutting_down && !pollset_has_workers(pollset) &&
+ !pollset->finish_shutdown_called) {
+ GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0);
+ finish_shutdown_locked(exec_ctx, pollset);
+
+ gpr_mu_unlock(&pollset->mu);
+ grpc_exec_ctx_flush(exec_ctx);
+ gpr_mu_lock(&pollset->mu);
+ }
+
+ *worker_hdl = NULL;
+
+ gpr_tls_set(&g_current_thread_pollset, (intptr_t)0);
+ gpr_tls_set(&g_current_thread_worker, (intptr_t)0);
+
+ GPR_TIMER_END("pollset_work", 0);
+
+ GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+ return error;
+}
+
+static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_fd *fd) {
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ gpr_mu_lock(&pollset->mu);
+ gpr_mu_lock(&fd->pi_mu);
+
+ polling_island *pi_new = NULL;
+
+ /* 1) If fd->polling_island and pollset->polling_island are both non-NULL and
+ * equal, do nothing.
+ * 2) If fd->polling_island and pollset->polling_island are both NULL, create
+ * a new polling island (with a refcount of 2) and make the polling_island
+ * fields in both fd and pollset to point to the new island
+ * 3) If one of fd->polling_island or pollset->polling_island is NULL, update
+ * the NULL polling_island field to point to the non-NULL polling_island
+ * field (ensure that the refcount on the polling island is incremented by
+ * 1 to account for the newly added reference)
+ * 4) Finally, if fd->polling_island and pollset->polling_island are non-NULL
+ * and different, merge both the polling islands and update the
+ * polling_island fields in both fd and pollset to point to the merged
+ * polling island.
+ */
+ if (fd->polling_island == pollset->polling_island) {
+ pi_new = fd->polling_island;
+ if (pi_new == NULL) {
+ pi_new = polling_island_create(fd, &error);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: Created new polling island. pi_new: %p (fd: %d, "
+ "pollset: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset);
+ }
+ } else if (fd->polling_island == NULL) {
+ pi_new = polling_island_lock(pollset->polling_island);
+ polling_island_add_fds_locked(pi_new, &fd, 1, true, &error);
+ gpr_mu_unlock(&pi_new->mu);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: fd->pi was NULL. pi_new: %p (fd: %d, pollset: %p, "
+ "pollset->pi: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset,
+ (void *)pollset->polling_island);
+ } else if (pollset->polling_island == NULL) {
+ pi_new = polling_island_lock(fd->polling_island);
+ gpr_mu_unlock(&pi_new->mu);
+
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: pollset->pi was NULL. pi_new: %p (fd: %d, pollset: "
+ "%p, fd->pi: %p",
+ (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island);
+ } else {
+ pi_new = polling_island_merge(fd->polling_island, pollset->polling_island,
+ &error);
+ GRPC_POLLING_TRACE(
+ "pollset_add_fd: polling islands merged. pi_new: %p (fd: %d, pollset: "
+ "%p, fd->pi: %p, pollset->pi: %p)",
+ (void *)pi_new, fd->fd, (void *)pollset, (void *)fd->polling_island,
+ (void *)pollset->polling_island);
+ }
+
+ /* At this point, pi_new is the polling island that both fd->polling_island
+ and pollset->polling_island must be pointing to */
+
+ if (fd->polling_island != pi_new) {
+ PI_ADD_REF(pi_new, "fd");
+ if (fd->polling_island != NULL) {
+ PI_UNREF(fd->polling_island, "fd");
+ }
+ fd->polling_island = pi_new;
+ }
+
+ if (pollset->polling_island != pi_new) {
+ PI_ADD_REF(pi_new, "ps");
+ if (pollset->polling_island != NULL) {
+ PI_UNREF(pollset->polling_island, "ps");
+ }
+ pollset->polling_island = pi_new;
+ }
+
+ gpr_mu_unlock(&fd->pi_mu);
+ gpr_mu_unlock(&pollset->mu);
+}
+
+/*******************************************************************************
+ * Pollset-set Definitions
+ */
+
+static grpc_pollset_set *pollset_set_create(void) {
+ grpc_pollset_set *pollset_set = gpr_malloc(sizeof(*pollset_set));
+ memset(pollset_set, 0, sizeof(*pollset_set));
+ gpr_mu_init(&pollset_set->mu);
+ return pollset_set;
+}
+
+static void pollset_set_destroy(grpc_pollset_set *pollset_set) {
+ size_t i;
+ gpr_mu_destroy(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ }
+ gpr_free(pollset_set->pollsets);
+ gpr_free(pollset_set->pollset_sets);
+ gpr_free(pollset_set->fds);
+ gpr_free(pollset_set);
+}
+
+static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->fd_count == pollset_set->fd_capacity) {
+ pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity);
+ pollset_set->fds = gpr_realloc(
+ pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds));
+ }
+ GRPC_FD_REF(fd, "pollset_set");
+ pollset_set->fds[pollset_set->fd_count++] = fd;
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd);
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ if (pollset_set->fds[i] == fd) {
+ pollset_set->fd_count--;
+ GPR_SWAP(grpc_fd *, pollset_set->fds[i],
+ pollset_set->fds[pollset_set->fd_count]);
+ GRPC_FD_UNREF(fd, "pollset_set");
+ break;
+ }
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i, j;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->pollset_count == pollset_set->pollset_capacity) {
+ pollset_set->pollset_capacity =
+ GPR_MAX(8, 2 * pollset_set->pollset_capacity);
+ pollset_set->pollsets =
+ gpr_realloc(pollset_set->pollsets, pollset_set->pollset_capacity *
+ sizeof(*pollset_set->pollsets));
+ }
+ pollset_set->pollsets[pollset_set->pollset_count++] = pollset;
+ for (i = 0, j = 0; i < pollset_set->fd_count; i++) {
+ if (fd_is_orphaned(pollset_set->fds[i])) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ } else {
+ pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]);
+ pollset_set->fds[j++] = pollset_set->fds[i];
+ }
+ }
+ pollset_set->fd_count = j;
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ if (pollset_set->pollsets[i] == pollset) {
+ pollset_set->pollset_count--;
+ GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i],
+ pollset_set->pollsets[pollset_set->pollset_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i, j;
+ gpr_mu_lock(&bag->mu);
+ if (bag->pollset_set_count == bag->pollset_set_capacity) {
+ bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity);
+ bag->pollset_sets =
+ gpr_realloc(bag->pollset_sets,
+ bag->pollset_set_capacity * sizeof(*bag->pollset_sets));
+ }
+ bag->pollset_sets[bag->pollset_set_count++] = item;
+ for (i = 0, j = 0; i < bag->fd_count; i++) {
+ if (fd_is_orphaned(bag->fds[i])) {
+ GRPC_FD_UNREF(bag->fds[i], "pollset_set");
+ } else {
+ pollset_set_add_fd(exec_ctx, item, bag->fds[i]);
+ bag->fds[j++] = bag->fds[i];
+ }
+ }
+ bag->fd_count = j;
+ gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i;
+ gpr_mu_lock(&bag->mu);
+ for (i = 0; i < bag->pollset_set_count; i++) {
+ if (bag->pollset_sets[i] == item) {
+ bag->pollset_set_count--;
+ GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i],
+ bag->pollset_sets[bag->pollset_set_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&bag->mu);
+}
+
+/* Test helper functions
+ * */
+void *grpc_fd_get_polling_island(grpc_fd *fd) {
+ polling_island *pi;
+
+ gpr_mu_lock(&fd->pi_mu);
+ pi = fd->polling_island;
+ gpr_mu_unlock(&fd->pi_mu);
+
+ return pi;
+}
+
+void *grpc_pollset_get_polling_island(grpc_pollset *ps) {
+ polling_island *pi;
+
+ gpr_mu_lock(&ps->mu);
+ pi = ps->polling_island;
+ gpr_mu_unlock(&ps->mu);
+
+ return pi;
+}
+
+bool grpc_are_polling_islands_equal(void *p, void *q) {
+ polling_island *p1 = p;
+ polling_island *p2 = q;
+
+ /* Note: polling_island_lock_pair() may change p1 and p2 to point to the
+ latest polling islands in their respective linked lists */
+ polling_island_lock_pair(&p1, &p2);
+ polling_island_unlock_pair(p1, p2);
+
+ return p1 == p2;
+}
+
+/*******************************************************************************
+ * Event engine binding
+ */
+
+static void shutdown_engine(void) {
+ fd_global_shutdown();
+ pollset_global_shutdown();
+ polling_island_global_shutdown();
+}
+
+static const grpc_event_engine_vtable vtable = {
+ .pollset_size = sizeof(grpc_pollset),
+
+ .fd_create = fd_create,
+ .fd_wrapped_fd = fd_wrapped_fd,
+ .fd_orphan = fd_orphan,
+ .fd_shutdown = fd_shutdown,
+ .fd_is_shutdown = fd_is_shutdown,
+ .fd_notify_on_read = fd_notify_on_read,
+ .fd_notify_on_write = fd_notify_on_write,
+ .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset,
+
+ .pollset_init = pollset_init,
+ .pollset_shutdown = pollset_shutdown,
+ .pollset_reset = pollset_reset,
+ .pollset_destroy = pollset_destroy,
+ .pollset_work = pollset_work,
+ .pollset_kick = pollset_kick,
+ .pollset_add_fd = pollset_add_fd,
+
+ .pollset_set_create = pollset_set_create,
+ .pollset_set_destroy = pollset_set_destroy,
+ .pollset_set_add_pollset = pollset_set_add_pollset,
+ .pollset_set_del_pollset = pollset_set_del_pollset,
+ .pollset_set_add_pollset_set = pollset_set_add_pollset_set,
+ .pollset_set_del_pollset_set = pollset_set_del_pollset_set,
+ .pollset_set_add_fd = pollset_set_add_fd,
+ .pollset_set_del_fd = pollset_set_del_fd,
+
+ .kick_poller = kick_poller,
+
+ .shutdown_engine = shutdown_engine,
+};
+
+/* It is possible that GLIBC has epoll but the underlying kernel doesn't.
+ * Create a dummy epoll_fd to make sure epoll support is available */
+static bool is_epoll_available() {
+ int fd = epoll_create1(EPOLL_CLOEXEC);
+ if (fd < 0) {
+ gpr_log(
+ GPR_ERROR,
+ "epoll_create1 failed with error: %d. Not using epoll polling engine",
+ fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void) {
+ /* If use of signals is disabled, we cannot use epoll engine*/
+ if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) {
+ return NULL;
+ }
+
+ if (!is_epoll_available()) {
+ return NULL;
+ }
+
+ if (!is_grpc_wakeup_signal_initialized) {
+ grpc_use_signal(SIGRTMIN + 2);
+ }
+
+ fd_global_init();
+
+ if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+ return NULL;
+ }
+
+ if (!GRPC_LOG_IF_ERROR("polling_island_global_init",
+ polling_island_global_init())) {
+ return NULL;
+ }
+
+ return &vtable;
+}
+
+#else /* defined(GPR_LINUX_EPOLL) */
+#if defined(GPR_POSIX_SOCKET)
+#include "src/core/lib/iomgr/ev_posix.h"
+/* If GPR_LINUX_EPOLL is not defined, it means epoll is not available. Return
+ * NULL */
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void) { return NULL; }
+#endif /* defined(GPR_POSIX_SOCKET) */
+
+void grpc_use_signal(int signum) {}
+#endif /* !defined(GPR_LINUX_EPOLL) */
diff --git a/src/core/lib/surface/surface_trace.h b/src/core/lib/iomgr/ev_epoll_linux.h
index a69a0fff57..7a494aba19 100644
--- a/src/core/lib/surface/surface_trace.h
+++ b/src/core/lib/iomgr/ev_epoll_linux.h
@@ -31,18 +31,17 @@
*
*/
-#ifndef GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H
-#define GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H
+#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H
+#define GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H
-#include <grpc/support/log.h>
-#include "src/core/lib/debug/trace.h"
-#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/iomgr/ev_posix.h"
-#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \
- if (grpc_api_trace) { \
- char *_ev = grpc_event_string(event); \
- gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
- gpr_free(_ev); \
- }
+const grpc_event_engine_vtable *grpc_init_epoll_linux(void);
-#endif /* GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H */
+#ifdef GPR_LINUX_EPOLL
+void *grpc_fd_get_polling_island(grpc_fd *fd);
+void *grpc_pollset_get_polling_island(grpc_pollset *ps);
+bool grpc_are_polling_islands_equal(void *p, void *q);
+#endif /* defined(GPR_LINUX_EPOLL) */
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H */
diff --git a/src/core/lib/iomgr/ev_poll_and_epoll_posix.c b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c
new file mode 100644
index 0000000000..9e306af5fa
--- /dev/null
+++ b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c
@@ -0,0 +1,2043 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* This file will be removed shortly: it's here to keep refactoring
+ * steps simple and auditable.
+ * It's the combination of the old files:
+ * - fd_posix.{h,c}
+ * - pollset_posix.{h,c}
+ * - pullset_multipoller_with_{poll,epoll}.{h,c}
+ * The new version will be split into:
+ * - ev_poll_posix.{h,c}
+ * - ev_epoll_posix.{h,c}
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/ev_poll_and_epoll_posix.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/tls.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/block_annotate.h"
+
+/*******************************************************************************
+ * FD declarations
+ */
+
+typedef struct grpc_fd_watcher {
+ struct grpc_fd_watcher *next;
+ struct grpc_fd_watcher *prev;
+ grpc_pollset *pollset;
+ grpc_pollset_worker *worker;
+ grpc_fd *fd;
+} grpc_fd_watcher;
+
+struct grpc_fd {
+ int fd;
+ /* refst format:
+ bit0: 1=active/0=orphaned
+ bit1-n: refcount
+ meaning that mostly we ref by two to avoid altering the orphaned bit,
+ and just unref by 1 when we're ready to flag the object as orphaned */
+ gpr_atm refst;
+
+ gpr_mu mu;
+ int shutdown;
+ int closed;
+ int released;
+
+ /* The watcher list.
+
+ The following watcher related fields are protected by watcher_mu.
+
+ An fd_watcher is an ephemeral object created when an fd wants to
+ begin polling, and destroyed after the poll.
+
+ It denotes the fd's interest in whether to read poll or write poll
+ or both or neither on this fd.
+
+ If a watcher is asked to poll for reads or writes, the read_watcher
+ or write_watcher fields are set respectively. A watcher may be asked
+ to poll for both, in which case both fields will be set.
+
+ read_watcher and write_watcher may be NULL if no watcher has been
+ asked to poll for reads or writes.
+
+ If an fd_watcher is not asked to poll for reads or writes, it's added
+ to a linked list of inactive watchers, rooted at inactive_watcher_root.
+ If at a later time there becomes need of a poller to poll, one of
+ the inactive pollers may be kicked out of their poll loops to take
+ that responsibility. */
+ grpc_fd_watcher inactive_watcher_root;
+ grpc_fd_watcher *read_watcher;
+ grpc_fd_watcher *write_watcher;
+
+ grpc_closure *read_closure;
+ grpc_closure *write_closure;
+
+ struct grpc_fd *freelist_next;
+
+ grpc_closure *on_done_closure;
+
+ grpc_iomgr_object iomgr_object;
+
+ /* The pollset that last noticed and notified that the fd is readable */
+ grpc_pollset *read_notifier_pollset;
+};
+
+/* Begin polling on an fd.
+ Registers that the given pollset is interested in this fd - so that if read
+ or writability interest changes, the pollset can be kicked to pick up that
+ new interest.
+ Return value is:
+ (fd_needs_read? read_mask : 0) | (fd_needs_write? write_mask : 0)
+ i.e. a combination of read_mask and write_mask determined by the fd's current
+ interest in said events.
+ Polling strategies that do not need to alter their behavior depending on the
+ fd's current interest (such as epoll) do not need to call this function.
+ MUST NOT be called with a pollset lock taken */
+static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
+ grpc_pollset_worker *worker, uint32_t read_mask,
+ uint32_t write_mask, grpc_fd_watcher *rec);
+/* Complete polling previously started with fd_begin_poll
+ MUST NOT be called with a pollset lock taken
+ if got_read or got_write are 1, also does the become_{readable,writable} as
+ appropriate. */
+static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *rec,
+ int got_read, int got_write,
+ grpc_pollset *read_notifier_pollset);
+
+/* Return 1 if this fd is orphaned, 0 otherwise */
+static bool fd_is_orphaned(grpc_fd *fd);
+
+/* Reference counting for fds */
+/*#define GRPC_FD_REF_COUNT_DEBUG*/
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line);
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__)
+#else
+static void fd_ref(grpc_fd *fd);
+static void fd_unref(grpc_fd *fd);
+#define GRPC_FD_REF(fd, reason) fd_ref(fd)
+#define GRPC_FD_UNREF(fd, reason) fd_unref(fd)
+#endif
+
+static void fd_global_init(void);
+static void fd_global_shutdown(void);
+
+#define CLOSURE_NOT_READY ((grpc_closure *)0)
+#define CLOSURE_READY ((grpc_closure *)1)
+
+/*******************************************************************************
+ * pollset declarations
+ */
+
+typedef struct grpc_pollset_vtable grpc_pollset_vtable;
+
+typedef struct grpc_cached_wakeup_fd {
+ grpc_wakeup_fd fd;
+ struct grpc_cached_wakeup_fd *next;
+} grpc_cached_wakeup_fd;
+
+struct grpc_pollset_worker {
+ grpc_cached_wakeup_fd *wakeup_fd;
+ int reevaluate_polling_on_wakeup;
+ int kicked_specifically;
+ struct grpc_pollset_worker *next;
+ struct grpc_pollset_worker *prev;
+};
+
+struct grpc_pollset {
+ /* pollsets under posix can mutate representation as fds are added and
+ removed.
+ For example, we may choose a poll() based implementation on linux for
+ few fds, and an epoll() based implementation for many fds */
+ const grpc_pollset_vtable *vtable;
+ gpr_mu mu;
+ grpc_pollset_worker root_worker;
+ int in_flight_cbs;
+ int shutting_down;
+ int called_shutdown;
+ int kicked_without_pollers;
+ grpc_closure *shutdown_done;
+ grpc_closure_list idle_jobs;
+ union {
+ int fd;
+ void *ptr;
+ } data;
+ /* Local cache of eventfds for workers */
+ grpc_cached_wakeup_fd *local_wakeup_cache;
+};
+
+struct grpc_pollset_vtable {
+ void (*add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ struct grpc_fd *fd, int and_unlock_pollset);
+ grpc_error *(*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ grpc_pollset_worker *worker,
+ gpr_timespec deadline, gpr_timespec now);
+ void (*finish_shutdown)(grpc_pollset *pollset);
+ void (*destroy)(grpc_pollset *pollset);
+};
+
+/* Add an fd to a pollset */
+static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ struct grpc_fd *fd);
+
+static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd);
+
+/* Convert a timespec to milliseconds:
+ - very small or negative poll times are clamped to zero to do a
+ non-blocking poll (which becomes spin polling)
+ - other small values are rounded up to one millisecond
+ - longer than a millisecond polls are rounded up to the next nearest
+ millisecond to avoid spinning
+ - infinite timeouts are converted to -1 */
+static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
+ gpr_timespec now);
+
+/* Allow kick to wakeup the currently polling worker */
+#define GRPC_POLLSET_CAN_KICK_SELF 1
+/* Force the wakee to repoll when awoken */
+#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2
+/* As per pollset_kick, with an extended set of flags (defined above)
+ -- mostly for fd_posix's use. */
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker,
+ uint32_t flags) GRPC_MUST_USE_RESULT;
+
+/* turn a pollset into a multipoller: platform specific */
+typedef void (*platform_become_multipoller_type)(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ struct grpc_fd **fds,
+ size_t fd_count);
+static platform_become_multipoller_type platform_become_multipoller;
+
+/* Return 1 if the pollset has active threads in pollset_work (pollset must
+ * be locked) */
+static int pollset_has_workers(grpc_pollset *pollset);
+
+static void remove_fd_from_all_epoll_sets(int fd);
+
+/*******************************************************************************
+ * pollset_set definitions
+ */
+
+struct grpc_pollset_set {
+ gpr_mu mu;
+
+ size_t pollset_count;
+ size_t pollset_capacity;
+ grpc_pollset **pollsets;
+
+ size_t pollset_set_count;
+ size_t pollset_set_capacity;
+ struct grpc_pollset_set **pollset_sets;
+
+ size_t fd_count;
+ size_t fd_capacity;
+ grpc_fd **fds;
+};
+
+/*******************************************************************************
+ * fd_posix.c
+ */
+
+/* We need to keep a freelist not because of any concerns of malloc performance
+ * but instead so that implementations with multiple threads in (for example)
+ * epoll_wait deal with the race between pollset removal and incoming poll
+ * notifications.
+ *
+ * The problem is that the poller ultimately holds a reference to this
+ * object, so it is very difficult to know when is safe to free it, at least
+ * without some expensive synchronization.
+ *
+ * If we keep the object freelisted, in the worst case losing this race just
+ * becomes a spurious read notification on a reused fd.
+ */
+/* TODO(klempner): We could use some form of polling generation count to know
+ * when these are safe to free. */
+/* TODO(klempner): Consider disabling freelisting if we don't have multiple
+ * threads in poll on the same fd */
+/* TODO(klempner): Batch these allocations to reduce fragmentation */
+static grpc_fd *fd_freelist = NULL;
+static gpr_mu fd_freelist_mu;
+
+static void freelist_fd(grpc_fd *fd) {
+ gpr_mu_lock(&fd_freelist_mu);
+ fd->freelist_next = fd_freelist;
+ fd_freelist = fd;
+ grpc_iomgr_unregister_object(&fd->iomgr_object);
+ gpr_mu_unlock(&fd_freelist_mu);
+}
+
+static grpc_fd *alloc_fd(int fd) {
+ grpc_fd *r = NULL;
+ gpr_mu_lock(&fd_freelist_mu);
+ if (fd_freelist != NULL) {
+ r = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ }
+ gpr_mu_unlock(&fd_freelist_mu);
+ if (r == NULL) {
+ r = gpr_malloc(sizeof(grpc_fd));
+ gpr_mu_init(&r->mu);
+ }
+
+ gpr_mu_lock(&r->mu);
+ gpr_atm_rel_store(&r->refst, 1);
+ r->shutdown = 0;
+ r->read_closure = CLOSURE_NOT_READY;
+ r->write_closure = CLOSURE_NOT_READY;
+ r->fd = fd;
+ r->inactive_watcher_root.next = r->inactive_watcher_root.prev =
+ &r->inactive_watcher_root;
+ r->freelist_next = NULL;
+ r->read_watcher = r->write_watcher = NULL;
+ r->on_done_closure = NULL;
+ r->closed = 0;
+ r->released = 0;
+ r->read_notifier_pollset = NULL;
+ gpr_mu_unlock(&r->mu);
+ return r;
+}
+
+static void destroy(grpc_fd *fd) {
+ gpr_mu_destroy(&fd->mu);
+ gpr_free(fd);
+}
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
+#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_atm_no_barrier_load(&fd->refst),
+ gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
+#else
+#define REF_BY(fd, n, reason) ref_by(fd, n)
+#define UNREF_BY(fd, n, reason) unref_by(fd, n)
+static void ref_by(grpc_fd *fd, int n) {
+#endif
+ GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
+}
+
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file,
+ int line) {
+ gpr_atm old;
+ gpr_log(GPR_DEBUG, "FD %d %p unref %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
+static void unref_by(grpc_fd *fd, int n) {
+ gpr_atm old;
+#endif
+ old = gpr_atm_full_fetch_add(&fd->refst, -n);
+ if (old == n) {
+ freelist_fd(fd);
+ } else {
+ GPR_ASSERT(old > n);
+ }
+}
+
+static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
+
+static void fd_global_shutdown(void) {
+ gpr_mu_lock(&fd_freelist_mu);
+ gpr_mu_unlock(&fd_freelist_mu);
+ while (fd_freelist != NULL) {
+ grpc_fd *fd = fd_freelist;
+ fd_freelist = fd_freelist->freelist_next;
+ destroy(fd);
+ }
+ gpr_mu_destroy(&fd_freelist_mu);
+}
+
+static grpc_fd *fd_create(int fd, const char *name) {
+ grpc_fd *r = alloc_fd(fd);
+ char *name2;
+ gpr_asprintf(&name2, "%s fd=%d", name, fd);
+ grpc_iomgr_register_object(&r->iomgr_object, name2);
+ gpr_free(name2);
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+ gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, r, name);
+#endif
+ return r;
+}
+
+static bool fd_is_orphaned(grpc_fd *fd) {
+ return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
+}
+
+static grpc_error *pollset_kick_locked(grpc_fd_watcher *watcher) {
+ gpr_mu_lock(&watcher->pollset->mu);
+ GPR_ASSERT(watcher->worker);
+ grpc_error *err = pollset_kick_ext(watcher->pollset, watcher->worker,
+ GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+ gpr_mu_unlock(&watcher->pollset->mu);
+ return err;
+}
+
+static void maybe_wake_one_watcher_locked(grpc_fd *fd) {
+ if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) {
+ pollset_kick_locked(fd->inactive_watcher_root.next);
+ } else if (fd->read_watcher) {
+ pollset_kick_locked(fd->read_watcher);
+ } else if (fd->write_watcher) {
+ pollset_kick_locked(fd->write_watcher);
+ }
+}
+
+static void wake_all_watchers_locked(grpc_fd *fd) {
+ grpc_fd_watcher *watcher;
+ for (watcher = fd->inactive_watcher_root.next;
+ watcher != &fd->inactive_watcher_root; watcher = watcher->next) {
+ pollset_kick_locked(watcher);
+ }
+ if (fd->read_watcher) {
+ pollset_kick_locked(fd->read_watcher);
+ }
+ if (fd->write_watcher && fd->write_watcher != fd->read_watcher) {
+ pollset_kick_locked(fd->write_watcher);
+ }
+}
+
+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;
+}
+
+static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ fd->closed = 1;
+ if (!fd->released) {
+ close(fd->fd);
+ } else {
+ remove_fd_from_all_epoll_sets(fd->fd);
+ }
+ grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL);
+}
+
+static int fd_wrapped_fd(grpc_fd *fd) {
+ if (fd->released || fd->closed) {
+ return -1;
+ } else {
+ return fd->fd;
+ }
+}
+
+static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *on_done, int *release_fd,
+ const char *reason) {
+ fd->on_done_closure = on_done;
+ fd->released = release_fd != NULL;
+ if (!fd->released) {
+ shutdown(fd->fd, SHUT_RDWR);
+ } else {
+ *release_fd = fd->fd;
+ }
+ gpr_mu_lock(&fd->mu);
+ REF_BY(fd, 1, reason); /* remove active status, but keep referenced */
+ if (!has_watchers(fd)) {
+ close_fd_locked(exec_ctx, fd);
+ } else {
+ wake_all_watchers_locked(fd);
+ }
+ gpr_mu_unlock(&fd->mu);
+ UNREF_BY(fd, 2, reason); /* drop the reference */
+}
+
+/* increment refcount by two to avoid changing the orphan bit */
+#ifdef GRPC_FD_REF_COUNT_DEBUG
+static void fd_ref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ ref_by(fd, 2, reason, file, line);
+}
+
+static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
+ int line) {
+ unref_by(fd, 2, reason, file, line);
+}
+#else
+static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); }
+
+static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
+#endif
+
+static grpc_error *fd_shutdown_error(bool shutdown) {
+ if (!shutdown) {
+ return GRPC_ERROR_NONE;
+ } else {
+ return GRPC_ERROR_CREATE("FD shutdown");
+ }
+}
+
+static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st, grpc_closure *closure) {
+ if (fd->shutdown) {
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
+ NULL);
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready ==> switch to a waiting state by setting the closure */
+ *st = closure;
+ } else if (*st == CLOSURE_READY) {
+ /* already ready ==> queue the closure to run immediately */
+ *st = CLOSURE_NOT_READY;
+ grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+ NULL);
+ maybe_wake_one_watcher_locked(fd);
+ } else {
+ /* upcallptr was set to a different closure. This is an error! */
+ gpr_log(GPR_ERROR,
+ "User called a notify_on function with a previous callback still "
+ "pending");
+ abort();
+ }
+}
+
+/* returns 1 if state becomes not ready */
+static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure **st) {
+ if (*st == CLOSURE_READY) {
+ /* duplicate ready ==> ignore */
+ return 0;
+ } else if (*st == CLOSURE_NOT_READY) {
+ /* not ready, and not waiting ==> flag ready */
+ *st = CLOSURE_READY;
+ return 0;
+ } else {
+ /* waiting ==> queue closure */
+ grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
+ *st = CLOSURE_NOT_READY;
+ return 1;
+ }
+}
+
+static void set_read_notifier_pollset_locked(
+ grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_pollset *read_notifier_pollset) {
+ fd->read_notifier_pollset = read_notifier_pollset;
+}
+
+static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ /* only shutdown once */
+ if (!fd->shutdown) {
+ fd->shutdown = 1;
+ /* signal read/write closed to OS so that future operations fail */
+ shutdown(fd->fd, SHUT_RDWR);
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ }
+ gpr_mu_unlock(&fd->mu);
+}
+
+static bool fd_is_shutdown(grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ bool r = fd->shutdown;
+ gpr_mu_unlock(&fd->mu);
+ return r;
+}
+
+static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->read_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_closure *closure) {
+ gpr_mu_lock(&fd->mu);
+ notify_on_locked(exec_ctx, fd, &fd->write_closure, closure);
+ gpr_mu_unlock(&fd->mu);
+}
+
+/* Return the read-notifier pollset */
+static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_fd *fd) {
+ grpc_pollset *notifier = NULL;
+
+ gpr_mu_lock(&fd->mu);
+ notifier = fd->read_notifier_pollset;
+ gpr_mu_unlock(&fd->mu);
+
+ return notifier;
+}
+
+static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
+ grpc_pollset_worker *worker, uint32_t read_mask,
+ uint32_t write_mask, grpc_fd_watcher *watcher) {
+ uint32_t mask = 0;
+ grpc_closure *cur;
+ int requested;
+ /* keep track of pollers that have requested our events, in case they change
+ */
+ GRPC_FD_REF(fd, "poll");
+
+ gpr_mu_lock(&fd->mu);
+
+ /* if we are shutdown, then don't add to the watcher set */
+ if (fd->shutdown) {
+ watcher->fd = NULL;
+ watcher->pollset = NULL;
+ watcher->worker = NULL;
+ gpr_mu_unlock(&fd->mu);
+ GRPC_FD_UNREF(fd, "poll");
+ return 0;
+ }
+
+ /* if there is nobody polling for read, but we need to, then start doing so */
+ cur = fd->read_closure;
+ requested = cur != CLOSURE_READY;
+ if (read_mask && fd->read_watcher == NULL && requested) {
+ fd->read_watcher = watcher;
+ mask |= read_mask;
+ }
+ /* if there is nobody polling for write, but we need to, then start doing so
+ */
+ cur = fd->write_closure;
+ requested = cur != CLOSURE_READY;
+ if (write_mask && fd->write_watcher == NULL && requested) {
+ fd->write_watcher = watcher;
+ mask |= write_mask;
+ }
+ /* if not polling, remember this watcher in case we need someone to later */
+ if (mask == 0 && worker != NULL) {
+ watcher->next = &fd->inactive_watcher_root;
+ watcher->prev = watcher->next->prev;
+ watcher->next->prev = watcher->prev->next = watcher;
+ }
+ watcher->pollset = pollset;
+ watcher->worker = worker;
+ watcher->fd = fd;
+ gpr_mu_unlock(&fd->mu);
+
+ return mask;
+}
+
+static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *watcher,
+ int got_read, int got_write,
+ grpc_pollset *read_notifier_pollset) {
+ int was_polling = 0;
+ int kick = 0;
+ grpc_fd *fd = watcher->fd;
+
+ if (fd == NULL) {
+ return;
+ }
+
+ gpr_mu_lock(&fd->mu);
+
+ if (watcher == fd->read_watcher) {
+ /* remove read watcher, kick if we still need a read */
+ was_polling = 1;
+ if (!got_read) {
+ kick = 1;
+ }
+ fd->read_watcher = NULL;
+ }
+ if (watcher == fd->write_watcher) {
+ /* remove write watcher, kick if we still need a write */
+ was_polling = 1;
+ if (!got_write) {
+ kick = 1;
+ }
+ fd->write_watcher = NULL;
+ }
+ if (!was_polling && watcher->worker != NULL) {
+ /* remove from inactive list */
+ watcher->next->prev = watcher->prev;
+ watcher->prev->next = watcher->next;
+ }
+ if (got_read) {
+ if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) {
+ kick = 1;
+ }
+
+ if (read_notifier_pollset != NULL) {
+ set_read_notifier_pollset_locked(exec_ctx, fd, read_notifier_pollset);
+ }
+ }
+ if (got_write) {
+ if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) {
+ kick = 1;
+ }
+ }
+ if (kick) {
+ maybe_wake_one_watcher_locked(fd);
+ }
+ if (fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) {
+ close_fd_locked(exec_ctx, fd);
+ }
+ gpr_mu_unlock(&fd->mu);
+
+ GRPC_FD_UNREF(fd, "poll");
+}
+
+/*******************************************************************************
+ * pollset_posix.c
+ */
+
+GPR_TLS_DECL(g_current_thread_poller);
+GPR_TLS_DECL(g_current_thread_worker);
+
+/** The alarm system needs to be able to wakeup 'some poller' sometimes
+ * (specifically when a new alarm needs to be triggered earlier than the next
+ * alarm 'epoch').
+ * This wakeup_fd gives us something to alert on when such a case occurs. */
+grpc_wakeup_fd grpc_global_wakeup_fd;
+
+static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev->next = worker->next;
+ worker->next->prev = worker->prev;
+}
+
+static int pollset_has_workers(grpc_pollset *p) {
+ return p->root_worker.next != &p->root_worker;
+}
+
+static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) {
+ if (pollset_has_workers(p)) {
+ grpc_pollset_worker *w = p->root_worker.next;
+ remove_worker(p, w);
+ return w;
+ } else {
+ return NULL;
+ }
+}
+
+static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->next = &p->root_worker;
+ worker->prev = worker->next->prev;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
+ worker->prev = &p->root_worker;
+ worker->next = worker->prev->next;
+ worker->prev->next = worker->next->prev = worker;
+}
+
+static void kick_append_error(grpc_error **composite, grpc_error *error) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE("Kick Failure");
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker,
+ uint32_t flags) {
+ GPR_TIMER_BEGIN("pollset_kick_ext", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ /* pollset->mu already held */
+ if (specific_worker != NULL) {
+ if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
+ GPR_TIMER_BEGIN("pollset_kick_ext.broadcast", 0);
+ GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
+ for (specific_worker = p->root_worker.next;
+ specific_worker != &p->root_worker;
+ specific_worker = specific_worker->next) {
+ kick_append_error(
+ &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+ }
+ p->kicked_without_pollers = true;
+ GPR_TIMER_END("pollset_kick_ext.broadcast", 0);
+ } else if (gpr_tls_get(&g_current_thread_worker) !=
+ (intptr_t)specific_worker) {
+ GPR_TIMER_MARK("different_thread_worker", 0);
+ if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
+ specific_worker->reevaluate_polling_on_wakeup = true;
+ }
+ specific_worker->kicked_specifically = true;
+ kick_append_error(&error,
+ grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+ } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
+ GPR_TIMER_MARK("kick_yoself", 0);
+ if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
+ specific_worker->reevaluate_polling_on_wakeup = true;
+ }
+ specific_worker->kicked_specifically = true;
+ kick_append_error(&error,
+ grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+ }
+ } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) {
+ GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
+ GPR_TIMER_MARK("kick_anonymous", 0);
+ specific_worker = pop_front_worker(p);
+ if (specific_worker != NULL) {
+ if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
+ GPR_TIMER_MARK("kick_anonymous_not_self", 0);
+ push_back_worker(p, specific_worker);
+ specific_worker = pop_front_worker(p);
+ if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 &&
+ gpr_tls_get(&g_current_thread_worker) ==
+ (intptr_t)specific_worker) {
+ push_back_worker(p, specific_worker);
+ specific_worker = NULL;
+ }
+ }
+ if (specific_worker != NULL) {
+ GPR_TIMER_MARK("finally_kick", 0);
+ push_back_worker(p, specific_worker);
+ kick_append_error(
+ &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
+ }
+ } else {
+ GPR_TIMER_MARK("kicked_no_pollers", 0);
+ p->kicked_without_pollers = true;
+ }
+ }
+
+ GPR_TIMER_END("pollset_kick_ext", 0);
+ return error;
+}
+
+static grpc_error *pollset_kick(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker) {
+ return pollset_kick_ext(p, specific_worker, 0);
+}
+
+/* global state management */
+
+static grpc_error *pollset_global_init(void) {
+ gpr_tls_init(&g_current_thread_poller);
+ gpr_tls_init(&g_current_thread_worker);
+ return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+}
+
+static void pollset_global_shutdown(void) {
+ grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd);
+ gpr_tls_destroy(&g_current_thread_poller);
+ gpr_tls_destroy(&g_current_thread_worker);
+}
+
+static grpc_error *kick_poller(void) {
+ return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
+
+/* main interface */
+
+static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null);
+
+static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
+ gpr_mu_init(&pollset->mu);
+ *mu = &pollset->mu;
+ pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
+ pollset->in_flight_cbs = 0;
+ pollset->shutting_down = 0;
+ pollset->called_shutdown = 0;
+ pollset->kicked_without_pollers = 0;
+ pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL;
+ pollset->local_wakeup_cache = NULL;
+ pollset->kicked_without_pollers = 0;
+ become_basic_pollset(pollset, NULL);
+}
+
+static void pollset_destroy(grpc_pollset *pollset) {
+ GPR_ASSERT(pollset->in_flight_cbs == 0);
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail);
+ pollset->vtable->destroy(pollset);
+ while (pollset->local_wakeup_cache) {
+ grpc_cached_wakeup_fd *next = pollset->local_wakeup_cache->next;
+ grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd);
+ gpr_free(pollset->local_wakeup_cache);
+ pollset->local_wakeup_cache = next;
+ }
+ gpr_mu_destroy(&pollset->mu);
+}
+
+static void pollset_reset(grpc_pollset *pollset) {
+ GPR_ASSERT(pollset->shutting_down);
+ GPR_ASSERT(pollset->in_flight_cbs == 0);
+ GPR_ASSERT(!pollset_has_workers(pollset));
+ GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail);
+ pollset->vtable->destroy(pollset);
+ pollset->shutting_down = 0;
+ pollset->called_shutdown = 0;
+ pollset->kicked_without_pollers = 0;
+ become_basic_pollset(pollset, NULL);
+}
+
+static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_fd *fd) {
+ gpr_mu_lock(&pollset->mu);
+ pollset->vtable->add_fd(exec_ctx, pollset, fd, 1);
+/* the following (enabled only in debug) will reacquire and then release
+ our lock - meaning that if the unlocking flag passed to add_fd above is
+ not respected, the code will deadlock (in a way that we have a chance of
+ debugging) */
+#ifndef NDEBUG
+ gpr_mu_lock(&pollset->mu);
+ gpr_mu_unlock(&pollset->mu);
+#endif
+}
+
+static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
+ GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs));
+ pollset->vtable->finish_shutdown(pollset);
+ grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
+}
+
+static void work_combine_error(grpc_error **composite, grpc_error *error) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE("pollset_work");
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker_hdl,
+ gpr_timespec now, gpr_timespec deadline) {
+ grpc_pollset_worker worker;
+ *worker_hdl = &worker;
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ /* pollset->mu already held */
+ int added_worker = 0;
+ int locked = 1;
+ int queued_work = 0;
+ int keep_polling = 0;
+ GPR_TIMER_BEGIN("pollset_work", 0);
+ /* this must happen before we (potentially) drop pollset->mu */
+ worker.next = worker.prev = NULL;
+ worker.reevaluate_polling_on_wakeup = 0;
+ if (pollset->local_wakeup_cache != NULL) {
+ worker.wakeup_fd = pollset->local_wakeup_cache;
+ pollset->local_wakeup_cache = worker.wakeup_fd->next;
+ } else {
+ worker.wakeup_fd = gpr_malloc(sizeof(*worker.wakeup_fd));
+ error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+ if (error != GRPC_ERROR_NONE) {
+ return error;
+ }
+ }
+ worker.kicked_specifically = 0;
+ /* If there's work waiting for the pollset to be idle, and the
+ pollset is idle, then do that work */
+ if (!pollset_has_workers(pollset) &&
+ !grpc_closure_list_empty(pollset->idle_jobs)) {
+ GPR_TIMER_MARK("pollset_work.idle_jobs", 0);
+ grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+ goto done;
+ }
+ /* If we're shutting down then we don't execute any extended work */
+ if (pollset->shutting_down) {
+ GPR_TIMER_MARK("pollset_work.shutting_down", 0);
+ goto done;
+ }
+ /* Give do_promote priority so we don't starve it out */
+ if (pollset->in_flight_cbs) {
+ GPR_TIMER_MARK("pollset_work.in_flight_cbs", 0);
+ gpr_mu_unlock(&pollset->mu);
+ locked = 0;
+ goto done;
+ }
+ /* Start polling, and keep doing so while we're being asked to
+ re-evaluate our pollers (this allows poll() based pollers to
+ ensure they don't miss wakeups) */
+ keep_polling = 1;
+ while (keep_polling) {
+ keep_polling = 0;
+ if (!pollset->kicked_without_pollers) {
+ if (!added_worker) {
+ push_front_worker(pollset, &worker);
+ added_worker = 1;
+ gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
+ }
+ gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset);
+ GPR_TIMER_BEGIN("maybe_work_and_unlock", 0);
+ work_combine_error(&error,
+ pollset->vtable->maybe_work_and_unlock(
+ exec_ctx, pollset, &worker, deadline, now));
+ GPR_TIMER_END("maybe_work_and_unlock", 0);
+ locked = 0;
+ gpr_tls_set(&g_current_thread_poller, 0);
+ } else {
+ GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0);
+ pollset->kicked_without_pollers = 0;
+ }
+ /* Finished execution - start cleaning up.
+ Note that we may arrive here from outside the enclosing while() loop.
+ In that case we won't loop though as we haven't added worker to the
+ worker list, which means nobody could ask us to re-evaluate polling). */
+ done:
+ if (!locked) {
+ queued_work |= grpc_exec_ctx_flush(exec_ctx);
+ gpr_mu_lock(&pollset->mu);
+ locked = 1;
+ }
+ /* If we're forced to re-evaluate polling (via pollset_kick with
+ GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
+ a loop */
+ if (worker.reevaluate_polling_on_wakeup) {
+ worker.reevaluate_polling_on_wakeup = 0;
+ pollset->kicked_without_pollers = 0;
+ if (queued_work || worker.kicked_specifically) {
+ /* If there's queued work on the list, then set the deadline to be
+ immediate so we get back out of the polling loop quickly */
+ deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+ }
+ keep_polling = 1;
+ }
+ }
+ if (added_worker) {
+ remove_worker(pollset, &worker);
+ gpr_tls_set(&g_current_thread_worker, 0);
+ }
+ /* release wakeup fd to the local pool */
+ worker.wakeup_fd->next = pollset->local_wakeup_cache;
+ pollset->local_wakeup_cache = worker.wakeup_fd;
+ /* check shutdown conditions */
+ if (pollset->shutting_down) {
+ if (pollset_has_workers(pollset)) {
+ pollset_kick(pollset, NULL);
+ } else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) {
+ pollset->called_shutdown = 1;
+ gpr_mu_unlock(&pollset->mu);
+ finish_shutdown(exec_ctx, pollset);
+ grpc_exec_ctx_flush(exec_ctx);
+ /* Continuing to access pollset here is safe -- it is the caller's
+ * responsibility to not destroy when it has outstanding calls to
+ * pollset_work.
+ * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */
+ gpr_mu_lock(&pollset->mu);
+ } else if (!grpc_closure_list_empty(pollset->idle_jobs)) {
+ grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+ gpr_mu_unlock(&pollset->mu);
+ grpc_exec_ctx_flush(exec_ctx);
+ gpr_mu_lock(&pollset->mu);
+ }
+ }
+ *worker_hdl = NULL;
+ GPR_TIMER_END("pollset_work", 0);
+ return error;
+}
+
+static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_closure *closure) {
+ GPR_ASSERT(!pollset->shutting_down);
+ pollset->shutting_down = 1;
+ pollset->shutdown_done = closure;
+ pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+ if (!pollset_has_workers(pollset)) {
+ grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL);
+ }
+ if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 &&
+ !pollset_has_workers(pollset)) {
+ pollset->called_shutdown = 1;
+ finish_shutdown(exec_ctx, pollset);
+ }
+}
+
+static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
+ gpr_timespec now) {
+ gpr_timespec timeout;
+ static const int64_t max_spin_polling_us = 10;
+ if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
+ return -1;
+ }
+ if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
+ max_spin_polling_us,
+ GPR_TIMESPAN))) <= 0) {
+ return 0;
+ }
+ timeout = gpr_time_sub(deadline, now);
+ return gpr_time_to_millis(gpr_time_add(
+ timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
+}
+
+/*
+ * basic_pollset - a vtable that provides polling for zero or one file
+ * descriptor via poll()
+ */
+
+typedef struct grpc_unary_promote_args {
+ const grpc_pollset_vtable *original_vtable;
+ grpc_pollset *pollset;
+ grpc_fd *fd;
+ grpc_closure promotion_closure;
+} grpc_unary_promote_args;
+
+static void basic_do_promote(grpc_exec_ctx *exec_ctx, void *args,
+ grpc_error *error) {
+ grpc_unary_promote_args *up_args = args;
+ const grpc_pollset_vtable *original_vtable = up_args->original_vtable;
+ grpc_pollset *pollset = up_args->pollset;
+ grpc_fd *fd = up_args->fd;
+
+ /*
+ * This is quite tricky. There are a number of cases to keep in mind here:
+ * 1. fd may have been orphaned
+ * 2. The pollset may no longer be a unary poller (and we can't let case #1
+ * leak to other pollset types!)
+ * 3. pollset's fd (which may have changed) may have been orphaned
+ * 4. The pollset may be shutting down.
+ */
+
+ gpr_mu_lock(&pollset->mu);
+ /* First we need to ensure that nobody is polling concurrently */
+ GPR_ASSERT(!pollset_has_workers(pollset));
+
+ gpr_free(up_args);
+ /* At this point the pollset may no longer be a unary poller. In that case
+ * we should just call the right add function and be done. */
+ /* TODO(klempner): If we're not careful this could cause infinite recursion.
+ * That's not a problem for now because empty_pollset has a trivial poller
+ * and we don't have any mechanism to unbecome multipoller. */
+ pollset->in_flight_cbs--;
+ if (pollset->shutting_down) {
+ /* We don't care about this pollset anymore. */
+ if (pollset->in_flight_cbs == 0 && !pollset->called_shutdown) {
+ pollset->called_shutdown = 1;
+ finish_shutdown(exec_ctx, pollset);
+ }
+ } else if (fd_is_orphaned(fd)) {
+ /* Don't try to add it to anything, we'll drop our ref on it below */
+ } else if (pollset->vtable != original_vtable) {
+ pollset->vtable->add_fd(exec_ctx, pollset, fd, 0);
+ } else if (fd != pollset->data.ptr) {
+ grpc_fd *fds[2];
+ fds[0] = pollset->data.ptr;
+ fds[1] = fd;
+
+ if (fds[0] && !fd_is_orphaned(fds[0])) {
+ platform_become_multipoller(exec_ctx, pollset, fds, GPR_ARRAY_SIZE(fds));
+ GRPC_FD_UNREF(fds[0], "basicpoll");
+ } else {
+ /* old fd is orphaned and we haven't cleaned it up until now, so remain a
+ * unary poller */
+ /* Note that it is possible that fds[1] is also orphaned at this point.
+ * That's okay, we'll correct it at the next add or poll. */
+ if (fds[0]) GRPC_FD_UNREF(fds[0], "basicpoll");
+ pollset->data.ptr = fd;
+ GRPC_FD_REF(fd, "basicpoll");
+ }
+ }
+
+ gpr_mu_unlock(&pollset->mu);
+
+ /* Matching ref in basic_pollset_add_fd */
+ GRPC_FD_UNREF(fd, "basicpoll_add");
+}
+
+static void basic_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_fd *fd, int and_unlock_pollset) {
+ grpc_unary_promote_args *up_args;
+ GPR_ASSERT(fd);
+ if (fd == pollset->data.ptr) goto exit;
+
+ if (!pollset_has_workers(pollset)) {
+ /* Fast path -- no in flight cbs */
+ /* TODO(klempner): Comment this out and fix any test failures or establish
+ * they are due to timing issues */
+ grpc_fd *fds[2];
+ fds[0] = pollset->data.ptr;
+ fds[1] = fd;
+
+ if (fds[0] == NULL) {
+ pollset->data.ptr = fd;
+ GRPC_FD_REF(fd, "basicpoll");
+ } else if (!fd_is_orphaned(fds[0])) {
+ platform_become_multipoller(exec_ctx, pollset, fds, GPR_ARRAY_SIZE(fds));
+ GRPC_FD_UNREF(fds[0], "basicpoll");
+ } else {
+ /* old fd is orphaned and we haven't cleaned it up until now, so remain a
+ * unary poller */
+ GRPC_FD_UNREF(fds[0], "basicpoll");
+ pollset->data.ptr = fd;
+ GRPC_FD_REF(fd, "basicpoll");
+ }
+ goto exit;
+ }
+
+ /* Now we need to promote. This needs to happen when we're not polling. Since
+ * this may be called from poll, the wait needs to happen asynchronously. */
+ GRPC_FD_REF(fd, "basicpoll_add");
+ pollset->in_flight_cbs++;
+ up_args = gpr_malloc(sizeof(*up_args));
+ up_args->fd = fd;
+ up_args->original_vtable = pollset->vtable;
+ up_args->pollset = pollset;
+ up_args->promotion_closure.cb = basic_do_promote;
+ up_args->promotion_closure.cb_arg = up_args;
+
+ grpc_closure_list_append(&pollset->idle_jobs, &up_args->promotion_closure,
+ GRPC_ERROR_NONE);
+ pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
+
+exit:
+ if (and_unlock_pollset) {
+ gpr_mu_unlock(&pollset->mu);
+ }
+}
+
+static grpc_error *basic_pollset_maybe_work_and_unlock(
+ grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
+ gpr_timespec deadline, gpr_timespec now) {
+#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR)
+#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR)
+
+ struct pollfd pfd[3];
+ grpc_fd *fd;
+ grpc_fd_watcher fd_watcher;
+ int timeout;
+ int r;
+ nfds_t nfds;
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ fd = pollset->data.ptr;
+ if (fd && fd_is_orphaned(fd)) {
+ GRPC_FD_UNREF(fd, "basicpoll");
+ fd = pollset->data.ptr = NULL;
+ }
+ timeout = poll_deadline_to_millis_timeout(deadline, now);
+ pfd[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd);
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ pfd[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd->fd);
+ pfd[1].events = POLLIN;
+ pfd[1].revents = 0;
+ nfds = 2;
+ if (fd) {
+ pfd[2].fd = fd->fd;
+ pfd[2].revents = 0;
+ GRPC_FD_REF(fd, "basicpoll_begin");
+ gpr_mu_unlock(&pollset->mu);
+ pfd[2].events =
+ (short)fd_begin_poll(fd, pollset, worker, POLLIN, POLLOUT, &fd_watcher);
+ if (pfd[2].events != 0) {
+ nfds++;
+ }
+ } else {
+ gpr_mu_unlock(&pollset->mu);
+ }
+
+ /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid
+ even going into the blocking annotation if possible */
+ /* poll fd count (argument 2) is shortened by one if we have no events
+ to poll on - such that it only includes the kicker */
+ GPR_TIMER_BEGIN("poll", 0);
+ GRPC_SCHEDULING_START_BLOCKING_REGION;
+ r = grpc_poll_function(pfd, nfds, timeout);
+ GRPC_SCHEDULING_END_BLOCKING_REGION;
+ GPR_TIMER_END("poll", 0);
+
+ if (r < 0) {
+ if (errno != EINTR) {
+ work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
+ }
+ if (fd) {
+ fd_end_poll(exec_ctx, &fd_watcher, 0, 0, NULL);
+ }
+ } else if (r == 0) {
+ if (fd) {
+ fd_end_poll(exec_ctx, &fd_watcher, 0, 0, NULL);
+ }
+ } else {
+ if (pfd[0].revents & POLLIN_CHECK) {
+ work_combine_error(&error,
+ grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
+ }
+ if (pfd[1].revents & POLLIN_CHECK) {
+ work_combine_error(&error,
+ grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
+ }
+ if (nfds > 2) {
+ fd_end_poll(exec_ctx, &fd_watcher, pfd[2].revents & POLLIN_CHECK,
+ pfd[2].revents & POLLOUT_CHECK, pollset);
+ } else if (fd) {
+ fd_end_poll(exec_ctx, &fd_watcher, 0, 0, NULL);
+ }
+ }
+
+ if (fd) {
+ GRPC_FD_UNREF(fd, "basicpoll_begin");
+ }
+
+ return error;
+}
+
+static void basic_pollset_destroy(grpc_pollset *pollset) {
+ if (pollset->data.ptr != NULL) {
+ GRPC_FD_UNREF(pollset->data.ptr, "basicpoll");
+ pollset->data.ptr = NULL;
+ }
+}
+
+static const grpc_pollset_vtable basic_pollset = {
+ basic_pollset_add_fd, basic_pollset_maybe_work_and_unlock,
+ basic_pollset_destroy, basic_pollset_destroy};
+
+static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null) {
+ pollset->vtable = &basic_pollset;
+ pollset->data.ptr = fd_or_null;
+ if (fd_or_null != NULL) {
+ GRPC_FD_REF(fd_or_null, "basicpoll");
+ }
+}
+
+/*******************************************************************************
+ * pollset_multipoller_with_poll_posix.c
+ */
+
+#ifndef GPR_LINUX_MULTIPOLL_WITH_EPOLL
+
+typedef struct {
+ /* all polled fds */
+ size_t fd_count;
+ size_t fd_capacity;
+ grpc_fd **fds;
+ /* fds that have been removed from the pollset explicitly */
+ size_t del_count;
+ size_t del_capacity;
+ grpc_fd **dels;
+} poll_hdr;
+
+static void multipoll_with_poll_pollset_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ grpc_fd *fd,
+ int and_unlock_pollset) {
+ size_t i;
+ poll_hdr *h = pollset->data.ptr;
+ /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */
+ for (i = 0; i < h->fd_count; i++) {
+ if (h->fds[i] == fd) goto exit;
+ }
+ if (h->fd_count == h->fd_capacity) {
+ h->fd_capacity = GPR_MAX(h->fd_capacity + 8, h->fd_count * 3 / 2);
+ h->fds = gpr_realloc(h->fds, sizeof(grpc_fd *) * h->fd_capacity);
+ }
+ h->fds[h->fd_count++] = fd;
+ GRPC_FD_REF(fd, "multipoller");
+exit:
+ if (and_unlock_pollset) {
+ gpr_mu_unlock(&pollset->mu);
+ }
+}
+
+static grpc_error *multipoll_with_poll_pollset_maybe_work_and_unlock(
+ grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
+ gpr_timespec deadline, gpr_timespec now) {
+#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR)
+#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR)
+
+ int timeout;
+ int r;
+ size_t i, j, fd_count;
+ nfds_t pfd_count;
+ poll_hdr *h;
+ /* TODO(ctiller): inline some elements to avoid an allocation */
+ grpc_fd_watcher *watchers;
+ struct pollfd *pfds;
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ h = pollset->data.ptr;
+ timeout = poll_deadline_to_millis_timeout(deadline, now);
+ /* TODO(ctiller): perform just one malloc here if we exceed the inline case */
+ pfds = gpr_malloc(sizeof(*pfds) * (h->fd_count + 2));
+ watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 2));
+ fd_count = 0;
+ pfd_count = 2;
+ pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd);
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+ pfds[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd->fd);
+ pfds[1].events = POLLIN;
+ pfds[1].revents = 0;
+ for (i = 0; i < h->fd_count; i++) {
+ int remove = fd_is_orphaned(h->fds[i]);
+ for (j = 0; !remove && j < h->del_count; j++) {
+ if (h->fds[i] == h->dels[j]) remove = 1;
+ }
+ if (remove) {
+ GRPC_FD_UNREF(h->fds[i], "multipoller");
+ } else {
+ h->fds[fd_count++] = h->fds[i];
+ watchers[pfd_count].fd = h->fds[i];
+ GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start");
+ pfds[pfd_count].fd = h->fds[i]->fd;
+ pfds[pfd_count].revents = 0;
+ pfd_count++;
+ }
+ }
+ for (j = 0; j < h->del_count; j++) {
+ GRPC_FD_UNREF(h->dels[j], "multipoller_del");
+ }
+ h->del_count = 0;
+ h->fd_count = fd_count;
+ gpr_mu_unlock(&pollset->mu);
+
+ for (i = 2; i < pfd_count; i++) {
+ grpc_fd *fd = watchers[i].fd;
+ pfds[i].events = (short)fd_begin_poll(fd, pollset, worker, POLLIN, POLLOUT,
+ &watchers[i]);
+ GRPC_FD_UNREF(fd, "multipoller_start");
+ }
+
+ /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid
+ even going into the blocking annotation if possible */
+ GRPC_SCHEDULING_START_BLOCKING_REGION;
+ r = grpc_poll_function(pfds, pfd_count, timeout);
+ GRPC_SCHEDULING_END_BLOCKING_REGION;
+
+ if (r < 0) {
+ if (errno != EINTR) {
+ work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
+ }
+ for (i = 2; i < pfd_count; i++) {
+ fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL);
+ }
+ } else if (r == 0) {
+ for (i = 2; i < pfd_count; i++) {
+ fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL);
+ }
+ } else {
+ if (pfds[0].revents & POLLIN_CHECK) {
+ work_combine_error(&error,
+ grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
+ }
+ if (pfds[1].revents & POLLIN_CHECK) {
+ work_combine_error(&error,
+ grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
+ }
+ for (i = 2; i < pfd_count; i++) {
+ if (watchers[i].fd == NULL) {
+ fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL);
+ continue;
+ }
+ fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN_CHECK,
+ pfds[i].revents & POLLOUT_CHECK, pollset);
+ }
+ }
+
+ gpr_free(pfds);
+ gpr_free(watchers);
+
+ return error;
+}
+
+static void multipoll_with_poll_pollset_finish_shutdown(grpc_pollset *pollset) {
+ size_t i;
+ poll_hdr *h = pollset->data.ptr;
+ for (i = 0; i < h->fd_count; i++) {
+ GRPC_FD_UNREF(h->fds[i], "multipoller");
+ }
+ for (i = 0; i < h->del_count; i++) {
+ GRPC_FD_UNREF(h->dels[i], "multipoller_del");
+ }
+ h->fd_count = 0;
+ h->del_count = 0;
+}
+
+static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) {
+ poll_hdr *h = pollset->data.ptr;
+ multipoll_with_poll_pollset_finish_shutdown(pollset);
+ gpr_free(h->fds);
+ gpr_free(h->dels);
+ gpr_free(h);
+}
+
+static const grpc_pollset_vtable multipoll_with_poll_pollset = {
+ multipoll_with_poll_pollset_add_fd,
+ multipoll_with_poll_pollset_maybe_work_and_unlock,
+ multipoll_with_poll_pollset_finish_shutdown,
+ multipoll_with_poll_pollset_destroy};
+
+static void poll_become_multipoller(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset, grpc_fd **fds,
+ size_t nfds) {
+ size_t i;
+ poll_hdr *h = gpr_malloc(sizeof(poll_hdr));
+ pollset->vtable = &multipoll_with_poll_pollset;
+ pollset->data.ptr = h;
+ h->fd_count = nfds;
+ h->fd_capacity = nfds;
+ h->fds = gpr_malloc(nfds * sizeof(grpc_fd *));
+ h->del_count = 0;
+ h->del_capacity = 0;
+ h->dels = NULL;
+ for (i = 0; i < nfds; i++) {
+ h->fds[i] = fds[i];
+ GRPC_FD_REF(fds[i], "multipoller");
+ }
+}
+
+#endif /* !GPR_LINUX_MULTIPOLL_WITH_EPOLL */
+
+/*******************************************************************************
+ * pollset_multipoller_with_epoll_posix.c
+ */
+
+#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
+
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/block_annotate.h"
+
+static void set_ready(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure **st,
+ grpc_pollset *read_notifier_pollset) {
+ /* only one set_ready can be active at once (but there may be a racing
+ notify_on) */
+ gpr_mu_lock(&fd->mu);
+ set_ready_locked(exec_ctx, fd, st);
+
+ /* A non-NULL read_notifier_pollset means that the fd is readable. */
+ if (read_notifier_pollset != NULL) {
+ /* Note: Since the fd might be a part of multiple pollsets, this might be
+ * called multiple times (for each time the fd becomes readable) and it is
+ * okay to set the fd's read-notifier pollset to anyone of these pollsets */
+ set_read_notifier_pollset_locked(exec_ctx, fd, read_notifier_pollset);
+ }
+
+ gpr_mu_unlock(&fd->mu);
+}
+
+static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+ grpc_pollset *notifier_pollset) {
+ set_ready(exec_ctx, fd, &fd->read_closure, notifier_pollset);
+}
+
+static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
+ set_ready(exec_ctx, fd, &fd->write_closure, NULL);
+}
+
+struct epoll_fd_list {
+ int *epoll_fds;
+ size_t count;
+ size_t capacity;
+};
+
+static struct epoll_fd_list epoll_fd_global_list;
+static gpr_once init_epoll_fd_list_mu = GPR_ONCE_INIT;
+static gpr_mu epoll_fd_list_mu;
+
+static void init_mu(void) { gpr_mu_init(&epoll_fd_list_mu); }
+
+static void add_epoll_fd_to_global_list(int epoll_fd) {
+ gpr_once_init(&init_epoll_fd_list_mu, init_mu);
+
+ gpr_mu_lock(&epoll_fd_list_mu);
+ if (epoll_fd_global_list.count == epoll_fd_global_list.capacity) {
+ epoll_fd_global_list.capacity =
+ GPR_MAX((size_t)8, epoll_fd_global_list.capacity * 2);
+ epoll_fd_global_list.epoll_fds =
+ gpr_realloc(epoll_fd_global_list.epoll_fds,
+ epoll_fd_global_list.capacity * sizeof(int));
+ }
+ epoll_fd_global_list.epoll_fds[epoll_fd_global_list.count++] = epoll_fd;
+ gpr_mu_unlock(&epoll_fd_list_mu);
+}
+
+static void remove_epoll_fd_from_global_list(int epoll_fd) {
+ gpr_mu_lock(&epoll_fd_list_mu);
+ GPR_ASSERT(epoll_fd_global_list.count > 0);
+ for (size_t i = 0; i < epoll_fd_global_list.count; i++) {
+ if (epoll_fd == epoll_fd_global_list.epoll_fds[i]) {
+ epoll_fd_global_list.epoll_fds[i] =
+ epoll_fd_global_list.epoll_fds[--(epoll_fd_global_list.count)];
+ break;
+ }
+ }
+ gpr_mu_unlock(&epoll_fd_list_mu);
+}
+
+static void remove_fd_from_all_epoll_sets(int fd) {
+ int err;
+ gpr_once_init(&init_epoll_fd_list_mu, init_mu);
+ gpr_mu_lock(&epoll_fd_list_mu);
+ if (epoll_fd_global_list.count == 0) {
+ gpr_mu_unlock(&epoll_fd_list_mu);
+ return;
+ }
+ for (size_t i = 0; i < epoll_fd_global_list.count; i++) {
+ err = epoll_ctl(epoll_fd_global_list.epoll_fds[i], EPOLL_CTL_DEL, fd, NULL);
+ if (err < 0 && errno != ENOENT) {
+ gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd,
+ strerror(errno));
+ }
+ }
+ gpr_mu_unlock(&epoll_fd_list_mu);
+}
+
+typedef struct {
+ grpc_pollset *pollset;
+ grpc_fd *fd;
+ grpc_closure closure;
+} delayed_add;
+
+typedef struct { int epoll_fd; } epoll_hdr;
+
+static void finally_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_fd *fd) {
+ epoll_hdr *h = pollset->data.ptr;
+ struct epoll_event ev;
+ int err;
+ grpc_fd_watcher watcher;
+
+ /* We pretend to be polling whilst adding an fd to keep the fd from being
+ closed during the add. This may result in a spurious wakeup being assigned
+ to this pollset whilst adding, but that should be benign. */
+ GPR_ASSERT(fd_begin_poll(fd, pollset, NULL, 0, 0, &watcher) == 0);
+ if (watcher.fd != NULL) {
+ ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET);
+ ev.data.ptr = fd;
+ err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev);
+ if (err < 0) {
+ /* FDs may be added to a pollset multiple times, so EEXIST is normal. */
+ if (errno != EEXIST) {
+ gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd,
+ strerror(errno));
+ }
+ }
+ }
+ fd_end_poll(exec_ctx, &watcher, 0, 0, NULL);
+}
+
+static void perform_delayed_add(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
+ delayed_add *da = arg;
+
+ if (!fd_is_orphaned(da->fd)) {
+ finally_add_fd(exec_ctx, da->pollset, da->fd);
+ }
+
+ gpr_mu_lock(&da->pollset->mu);
+ da->pollset->in_flight_cbs--;
+ if (da->pollset->shutting_down) {
+ /* We don't care about this pollset anymore. */
+ if (da->pollset->in_flight_cbs == 0 && !da->pollset->called_shutdown) {
+ da->pollset->called_shutdown = 1;
+ grpc_exec_ctx_sched(exec_ctx, da->pollset->shutdown_done, GRPC_ERROR_NONE,
+ NULL);
+ }
+ }
+ gpr_mu_unlock(&da->pollset->mu);
+
+ GRPC_FD_UNREF(da->fd, "delayed_add");
+
+ gpr_free(da);
+}
+
+static void multipoll_with_epoll_pollset_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset,
+ grpc_fd *fd,
+ int and_unlock_pollset) {
+ if (and_unlock_pollset) {
+ gpr_mu_unlock(&pollset->mu);
+ finally_add_fd(exec_ctx, pollset, fd);
+ } else {
+ delayed_add *da = gpr_malloc(sizeof(*da));
+ da->pollset = pollset;
+ da->fd = fd;
+ GRPC_FD_REF(fd, "delayed_add");
+ grpc_closure_init(&da->closure, perform_delayed_add, da);
+ pollset->in_flight_cbs++;
+ grpc_exec_ctx_sched(exec_ctx, &da->closure, GRPC_ERROR_NONE, NULL);
+ }
+}
+
+/* TODO(klempner): We probably want to turn this down a bit */
+#define GRPC_EPOLL_MAX_EVENTS 1000
+
+static grpc_error *multipoll_with_epoll_pollset_maybe_work_and_unlock(
+ grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
+ gpr_timespec deadline, gpr_timespec now) {
+ struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
+ int ep_rv;
+ int poll_rv;
+ epoll_hdr *h = pollset->data.ptr;
+ int timeout_ms;
+ struct pollfd pfds[2];
+ grpc_error *error = GRPC_ERROR_NONE;
+
+ /* If you want to ignore epoll's ability to sanely handle parallel pollers,
+ * for a more apples-to-apples performance comparison with poll, add a
+ * if (pollset->counter != 0) { return 0; }
+ * here.
+ */
+
+ gpr_mu_unlock(&pollset->mu);
+
+ timeout_ms = poll_deadline_to_millis_timeout(deadline, now);
+
+ pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd->fd);
+ pfds[0].events = POLLIN;
+ pfds[0].revents = 0;
+ pfds[1].fd = h->epoll_fd;
+ pfds[1].events = POLLIN;
+ pfds[1].revents = 0;
+
+ /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid
+ even going into the blocking annotation if possible */
+ GPR_TIMER_BEGIN("poll", 0);
+ GRPC_SCHEDULING_START_BLOCKING_REGION;
+ poll_rv = grpc_poll_function(pfds, 2, timeout_ms);
+ GRPC_SCHEDULING_END_BLOCKING_REGION;
+ GPR_TIMER_END("poll", 0);
+
+ if (poll_rv < 0) {
+ if (errno != EINTR) {
+ work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
+ }
+ } else if (poll_rv == 0) {
+ /* do nothing */
+ } else {
+ if (pfds[0].revents) {
+ work_combine_error(&error,
+ grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
+ }
+ if (pfds[1].revents) {
+ do {
+ /* The following epoll_wait never blocks; it has a timeout of 0 */
+ ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0);
+ if (ep_rv < 0) {
+ if (errno != EINTR) {
+ work_combine_error(&error, GRPC_OS_ERROR(errno, "epoll_wait"));
+ }
+ } else {
+ int i;
+ for (i = 0; i < ep_rv; ++i) {
+ grpc_fd *fd = ep_ev[i].data.ptr;
+ /* TODO(klempner): We might want to consider making err and pri
+ * separate events */
+ int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP);
+ int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
+ int write_ev = ep_ev[i].events & EPOLLOUT;
+ if (fd == NULL) {
+ work_combine_error(&error, grpc_wakeup_fd_consume_wakeup(
+ &grpc_global_wakeup_fd));
+ } else {
+ if (read_ev || cancel) {
+ fd_become_readable(exec_ctx, fd, pollset);
+ }
+ if (write_ev || cancel) {
+ fd_become_writable(exec_ctx, fd);
+ }
+ }
+ }
+ }
+ } while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
+ }
+ }
+ return error;
+}
+
+static void multipoll_with_epoll_pollset_finish_shutdown(
+ grpc_pollset *pollset) {}
+
+static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) {
+ epoll_hdr *h = pollset->data.ptr;
+ close(h->epoll_fd);
+ remove_epoll_fd_from_global_list(h->epoll_fd);
+ gpr_free(h);
+}
+
+static const grpc_pollset_vtable multipoll_with_epoll_pollset = {
+ multipoll_with_epoll_pollset_add_fd,
+ multipoll_with_epoll_pollset_maybe_work_and_unlock,
+ multipoll_with_epoll_pollset_finish_shutdown,
+ multipoll_with_epoll_pollset_destroy};
+
+static void epoll_become_multipoller(grpc_exec_ctx *exec_ctx,
+ grpc_pollset *pollset, grpc_fd **fds,
+ size_t nfds) {
+ size_t i;
+ epoll_hdr *h = gpr_malloc(sizeof(epoll_hdr));
+ struct epoll_event ev;
+ int err;
+
+ pollset->vtable = &multipoll_with_epoll_pollset;
+ pollset->data.ptr = h;
+ h->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (h->epoll_fd < 0) {
+ /* TODO(klempner): Fall back to poll here, especially on ENOSYS */
+ gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno));
+ abort();
+ }
+ add_epoll_fd_to_global_list(h->epoll_fd);
+
+ ev.events = (uint32_t)(EPOLLIN | EPOLLET);
+ ev.data.ptr = NULL;
+ err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD,
+ GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), &ev);
+ if (err < 0) {
+ gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s",
+ GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd),
+ strerror(errno));
+ }
+
+ for (i = 0; i < nfds; i++) {
+ multipoll_with_epoll_pollset_add_fd(exec_ctx, pollset, fds[i], 0);
+ }
+}
+
+#else /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */
+
+static void remove_fd_from_all_epoll_sets(int fd) {}
+
+#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */
+
+/*******************************************************************************
+ * pollset_set_posix.c
+ */
+
+static grpc_pollset_set *pollset_set_create(void) {
+ grpc_pollset_set *pollset_set = gpr_malloc(sizeof(*pollset_set));
+ memset(pollset_set, 0, sizeof(*pollset_set));
+ gpr_mu_init(&pollset_set->mu);
+ return pollset_set;
+}
+
+static void pollset_set_destroy(grpc_pollset_set *pollset_set) {
+ size_t i;
+ gpr_mu_destroy(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ }
+ gpr_free(pollset_set->pollsets);
+ gpr_free(pollset_set->pollset_sets);
+ gpr_free(pollset_set->fds);
+ gpr_free(pollset_set);
+}
+
+static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i, j;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->pollset_count == pollset_set->pollset_capacity) {
+ pollset_set->pollset_capacity =
+ GPR_MAX(8, 2 * pollset_set->pollset_capacity);
+ pollset_set->pollsets =
+ gpr_realloc(pollset_set->pollsets, pollset_set->pollset_capacity *
+ sizeof(*pollset_set->pollsets));
+ }
+ pollset_set->pollsets[pollset_set->pollset_count++] = pollset;
+ for (i = 0, j = 0; i < pollset_set->fd_count; i++) {
+ if (fd_is_orphaned(pollset_set->fds[i])) {
+ GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set");
+ } else {
+ pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]);
+ pollset_set->fds[j++] = pollset_set->fds[i];
+ }
+ }
+ pollset_set->fd_count = j;
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set,
+ grpc_pollset *pollset) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ if (pollset_set->pollsets[i] == pollset) {
+ pollset_set->pollset_count--;
+ GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i],
+ pollset_set->pollsets[pollset_set->pollset_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i, j;
+ gpr_mu_lock(&bag->mu);
+ if (bag->pollset_set_count == bag->pollset_set_capacity) {
+ bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity);
+ bag->pollset_sets =
+ gpr_realloc(bag->pollset_sets,
+ bag->pollset_set_capacity * sizeof(*bag->pollset_sets));
+ }
+ bag->pollset_sets[bag->pollset_set_count++] = item;
+ for (i = 0, j = 0; i < bag->fd_count; i++) {
+ if (fd_is_orphaned(bag->fds[i])) {
+ GRPC_FD_UNREF(bag->fds[i], "pollset_set");
+ } else {
+ pollset_set_add_fd(exec_ctx, item, bag->fds[i]);
+ bag->fds[j++] = bag->fds[i];
+ }
+ }
+ bag->fd_count = j;
+ gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *bag,
+ grpc_pollset_set *item) {
+ size_t i;
+ gpr_mu_lock(&bag->mu);
+ for (i = 0; i < bag->pollset_set_count; i++) {
+ if (bag->pollset_sets[i] == item) {
+ bag->pollset_set_count--;
+ GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i],
+ bag->pollset_sets[bag->pollset_set_count]);
+ break;
+ }
+ }
+ gpr_mu_unlock(&bag->mu);
+}
+
+static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ if (pollset_set->fd_count == pollset_set->fd_capacity) {
+ pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity);
+ pollset_set->fds = gpr_realloc(
+ pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds));
+ }
+ GRPC_FD_REF(fd, "pollset_set");
+ pollset_set->fds[pollset_set->fd_count++] = fd;
+ for (i = 0; i < pollset_set->pollset_count; i++) {
+ pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd);
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx,
+ grpc_pollset_set *pollset_set, grpc_fd *fd) {
+ size_t i;
+ gpr_mu_lock(&pollset_set->mu);
+ for (i = 0; i < pollset_set->fd_count; i++) {
+ if (pollset_set->fds[i] == fd) {
+ pollset_set->fd_count--;
+ GPR_SWAP(grpc_fd *, pollset_set->fds[i],
+ pollset_set->fds[pollset_set->fd_count]);
+ GRPC_FD_UNREF(fd, "pollset_set");
+ break;
+ }
+ }
+ for (i = 0; i < pollset_set->pollset_set_count; i++) {
+ pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd);
+ }
+ gpr_mu_unlock(&pollset_set->mu);
+}
+
+/*******************************************************************************
+ * event engine binding
+ */
+
+static void shutdown_engine(void) {
+ fd_global_shutdown();
+ pollset_global_shutdown();
+}
+
+static const grpc_event_engine_vtable vtable = {
+ .pollset_size = sizeof(grpc_pollset),
+
+ .fd_create = fd_create,
+ .fd_wrapped_fd = fd_wrapped_fd,
+ .fd_orphan = fd_orphan,
+ .fd_shutdown = fd_shutdown,
+ .fd_is_shutdown = fd_is_shutdown,
+ .fd_notify_on_read = fd_notify_on_read,
+ .fd_notify_on_write = fd_notify_on_write,
+ .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset,
+
+ .pollset_init = pollset_init,
+ .pollset_shutdown = pollset_shutdown,
+ .pollset_reset = pollset_reset,
+ .pollset_destroy = pollset_destroy,
+ .pollset_work = pollset_work,
+ .pollset_kick = pollset_kick,
+ .pollset_add_fd = pollset_add_fd,
+
+ .pollset_set_create = pollset_set_create,
+ .pollset_set_destroy = pollset_set_destroy,
+ .pollset_set_add_pollset = pollset_set_add_pollset,
+ .pollset_set_del_pollset = pollset_set_del_pollset,
+ .pollset_set_add_pollset_set = pollset_set_add_pollset_set,
+ .pollset_set_del_pollset_set = pollset_set_del_pollset_set,
+ .pollset_set_add_fd = pollset_set_add_fd,
+ .pollset_set_del_fd = pollset_set_del_fd,
+
+ .kick_poller = kick_poller,
+
+ .shutdown_engine = shutdown_engine,
+};
+
+const grpc_event_engine_vtable *grpc_init_poll_and_epoll_posix(void) {
+#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
+ platform_become_multipoller = epoll_become_multipoller;
+#else
+ platform_become_multipoller = poll_become_multipoller;
+#endif
+ fd_global_init();
+ pollset_global_init();
+ return &vtable;
+}
+
+#endif
diff --git a/src/core/lib/iomgr/ev_poll_and_epoll_posix.h b/src/core/lib/iomgr/ev_poll_and_epoll_posix.h
new file mode 100644
index 0000000000..06d6dbf29d
--- /dev/null
+++ b/src/core/lib/iomgr/ev_poll_and_epoll_posix.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_EV_POLL_AND_EPOLL_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_EV_POLL_AND_EPOLL_POSIX_H
+
+#include "src/core/lib/iomgr/ev_posix.h"
+
+const grpc_event_engine_vtable *grpc_init_poll_and_epoll_posix(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_EV_POLL_AND_EPOLL_POSIX_H */
diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c
index e2a21230b9..45c0a5e954 100644
--- a/src/core/lib/iomgr/ev_poll_posix.c
+++ b/src/core/lib/iomgr/ev_poll_posix.c
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2015-2016, Google Inc.
+ * Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -59,8 +59,6 @@
* FD declarations
*/
-grpc_wakeup_fd grpc_global_wakeup_fd;
-
typedef struct grpc_fd_watcher {
struct grpc_fd_watcher *next;
struct grpc_fd_watcher *prev;
@@ -219,9 +217,9 @@ static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2
/* As per pollset_kick, with an extended set of flags (defined above)
-- mostly for fd_posix's use. */
-static void pollset_kick_ext(grpc_pollset *p,
- grpc_pollset_worker *specific_worker,
- uint32_t flags);
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker,
+ uint32_t flags) GRPC_MUST_USE_RESULT;
/* Return 1 if the pollset has active threads in pollset_work (pollset must
* be locked) */
@@ -330,12 +328,13 @@ static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
return notifier;
}
-static void pollset_kick_locked(grpc_fd_watcher *watcher) {
+static grpc_error *pollset_kick_locked(grpc_fd_watcher *watcher) {
gpr_mu_lock(&watcher->pollset->mu);
GPR_ASSERT(watcher->worker);
- pollset_kick_ext(watcher->pollset, watcher->worker,
- GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+ grpc_error *err = pollset_kick_ext(watcher->pollset, watcher->worker,
+ GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
gpr_mu_unlock(&watcher->pollset->mu);
+ return err;
}
static void maybe_wake_one_watcher_locked(grpc_fd *fd) {
@@ -372,7 +371,7 @@ static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
if (!fd->released) {
close(fd->fd);
}
- grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL);
}
static int fd_wrapped_fd(grpc_fd *fd) {
@@ -421,15 +420,27 @@ static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); }
static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
#endif
+static grpc_error *fd_shutdown_error(bool shutdown) {
+ if (!shutdown) {
+ return GRPC_ERROR_NONE;
+ } else {
+ return GRPC_ERROR_CREATE("FD shutdown");
+ }
+}
+
static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
grpc_closure **st, grpc_closure *closure) {
- if (*st == CLOSURE_NOT_READY) {
+ if (fd->shutdown) {
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_CREATE("FD shutdown"),
+ NULL);
+ } else if (*st == CLOSURE_NOT_READY) {
/* not ready ==> switch to a waiting state by setting the closure */
*st = closure;
} else if (*st == CLOSURE_READY) {
/* already ready ==> queue the closure to run immediately */
*st = CLOSURE_NOT_READY;
- grpc_exec_ctx_enqueue(exec_ctx, closure, !fd->shutdown, NULL);
+ grpc_exec_ctx_sched(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+ NULL);
maybe_wake_one_watcher_locked(fd);
} else {
/* upcallptr was set to a different closure. This is an error! */
@@ -452,7 +463,7 @@ static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
return 0;
} else {
/* waiting ==> queue closure */
- grpc_exec_ctx_enqueue(exec_ctx, *st, !fd->shutdown, NULL);
+ grpc_exec_ctx_sched(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
*st = CLOSURE_NOT_READY;
return 1;
}
@@ -465,11 +476,22 @@ static void set_read_notifier_pollset_locked(
static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
gpr_mu_lock(&fd->mu);
- GPR_ASSERT(!fd->shutdown);
- fd->shutdown = 1;
- set_ready_locked(exec_ctx, fd, &fd->read_closure);
- set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ /* only shutdown once */
+ if (!fd->shutdown) {
+ fd->shutdown = 1;
+ /* signal read/write closed to OS so that future operations fail */
+ shutdown(fd->fd, SHUT_RDWR);
+ set_ready_locked(exec_ctx, fd, &fd->read_closure);
+ set_ready_locked(exec_ctx, fd, &fd->write_closure);
+ }
+ gpr_mu_unlock(&fd->mu);
+}
+
+static bool fd_is_shutdown(grpc_fd *fd) {
+ gpr_mu_lock(&fd->mu);
+ bool r = fd->shutdown;
gpr_mu_unlock(&fd->mu);
+ return r;
}
static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
@@ -633,10 +655,19 @@ static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) {
worker->prev->next = worker->next->prev = worker;
}
-static void pollset_kick_ext(grpc_pollset *p,
- grpc_pollset_worker *specific_worker,
- uint32_t flags) {
+static void kick_append_error(grpc_error **composite, grpc_error *error) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE("Kick Failure");
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker,
+ uint32_t flags) {
GPR_TIMER_BEGIN("pollset_kick_ext", 0);
+ grpc_error *error = GRPC_ERROR_NONE;
/* pollset->mu already held */
if (specific_worker != NULL) {
@@ -646,25 +677,28 @@ static void pollset_kick_ext(grpc_pollset *p,
for (specific_worker = p->root_worker.next;
specific_worker != &p->root_worker;
specific_worker = specific_worker->next) {
- grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+ kick_append_error(
+ &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
}
- p->kicked_without_pollers = 1;
+ p->kicked_without_pollers = true;
GPR_TIMER_END("pollset_kick_ext.broadcast", 0);
} else if (gpr_tls_get(&g_current_thread_worker) !=
(intptr_t)specific_worker) {
GPR_TIMER_MARK("different_thread_worker", 0);
if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
- specific_worker->reevaluate_polling_on_wakeup = 1;
+ specific_worker->reevaluate_polling_on_wakeup = true;
}
- specific_worker->kicked_specifically = 1;
- grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+ specific_worker->kicked_specifically = true;
+ kick_append_error(&error,
+ grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
} else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
GPR_TIMER_MARK("kick_yoself", 0);
if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
- specific_worker->reevaluate_polling_on_wakeup = 1;
+ specific_worker->reevaluate_polling_on_wakeup = true;
}
- specific_worker->kicked_specifically = 1;
- grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+ specific_worker->kicked_specifically = true;
+ kick_append_error(&error,
+ grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
}
} else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) {
GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
@@ -685,28 +719,31 @@ static void pollset_kick_ext(grpc_pollset *p,
if (specific_worker != NULL) {
GPR_TIMER_MARK("finally_kick", 0);
push_back_worker(p, specific_worker);
- grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+ kick_append_error(
+ &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
}
} else {
GPR_TIMER_MARK("kicked_no_pollers", 0);
- p->kicked_without_pollers = 1;
+ p->kicked_without_pollers = true;
}
}
GPR_TIMER_END("pollset_kick_ext", 0);
+ GRPC_LOG_IF_ERROR("pollset_kick_ext", GRPC_ERROR_REF(error));
+ return error;
}
-static void pollset_kick(grpc_pollset *p,
- grpc_pollset_worker *specific_worker) {
- pollset_kick_ext(p, specific_worker, 0);
+static grpc_error *pollset_kick(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker) {
+ return pollset_kick_ext(p, specific_worker, 0);
}
/* global state management */
-static void pollset_global_init(void) {
+static grpc_error *pollset_global_init(void) {
gpr_tls_init(&g_current_thread_poller);
gpr_tls_init(&g_current_thread_worker);
- grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+ return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
}
static void pollset_global_shutdown(void) {
@@ -715,7 +752,9 @@ static void pollset_global_shutdown(void) {
gpr_tls_destroy(&g_current_thread_worker);
}
-static void kick_poller(void) { grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); }
+static grpc_error *kick_poller(void) {
+ return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
/* main interface */
@@ -785,14 +824,23 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
GRPC_FD_UNREF(pollset->fds[i], "multipoller");
}
pollset->fd_count = 0;
- grpc_exec_ctx_enqueue(exec_ctx, pollset->shutdown_done, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
}
-static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
- grpc_pollset_worker **worker_hdl, gpr_timespec now,
- gpr_timespec deadline) {
+static void work_combine_error(grpc_error **composite, grpc_error *error) {
+ if (error == GRPC_ERROR_NONE) return;
+ if (*composite == GRPC_ERROR_NONE) {
+ *composite = GRPC_ERROR_CREATE("pollset_work");
+ }
+ *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker_hdl,
+ gpr_timespec now, gpr_timespec deadline) {
grpc_pollset_worker worker;
*worker_hdl = &worker;
+ grpc_error *error = GRPC_ERROR_NONE;
/* pollset->mu already held */
int added_worker = 0;
@@ -808,7 +856,11 @@ static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
pollset->local_wakeup_cache = worker.wakeup_fd->next;
} else {
worker.wakeup_fd = gpr_malloc(sizeof(*worker.wakeup_fd));
- grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+ error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+ if (error != GRPC_ERROR_NONE) {
+ GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+ return error;
+ }
}
worker.kicked_specifically = 0;
/* If there's work waiting for the pollset to be idle, and the
@@ -892,7 +944,7 @@ static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
if (r < 0) {
if (errno != EINTR) {
- gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
+ work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
}
for (i = 2; i < pfd_count; i++) {
fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL);
@@ -903,10 +955,12 @@ static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
}
} else {
if (pfds[0].revents & POLLIN_CHECK) {
- grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+ work_combine_error(
+ &error, grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
}
if (pfds[1].revents & POLLIN_CHECK) {
- grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd);
+ work_combine_error(
+ &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd));
}
for (i = 2; i < pfd_count; i++) {
if (watchers[i].fd == NULL) {
@@ -939,7 +993,7 @@ static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
/* If we're forced to re-evaluate polling (via pollset_kick with
GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
a loop */
- if (worker.reevaluate_polling_on_wakeup) {
+ if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) {
worker.reevaluate_polling_on_wakeup = 0;
pollset->kicked_without_pollers = 0;
if (queued_work || worker.kicked_specifically) {
@@ -984,6 +1038,8 @@ static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
}
*worker_hdl = NULL;
GPR_TIMER_END("pollset_work", 0);
+ GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error));
+ return error;
}
static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
@@ -1174,6 +1230,7 @@ static const grpc_event_engine_vtable vtable = {
.fd_wrapped_fd = fd_wrapped_fd,
.fd_orphan = fd_orphan,
.fd_shutdown = fd_shutdown,
+ .fd_is_shutdown = fd_is_shutdown,
.fd_notify_on_read = fd_notify_on_read,
.fd_notify_on_write = fd_notify_on_write,
.fd_get_read_notifier_pollset = fd_get_read_notifier_pollset,
@@ -1201,7 +1258,9 @@ static const grpc_event_engine_vtable vtable = {
};
const grpc_event_engine_vtable *grpc_init_poll_posix(void) {
- pollset_global_init();
+ if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
+ return NULL;
+ }
return &vtable;
}
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index 95520b01d3..a3c1e9db9a 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -44,6 +44,8 @@
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/ev_epoll_linux.h"
+#include "src/core/lib/iomgr/ev_poll_and_epoll_posix.h"
#include "src/core/lib/iomgr/ev_poll_posix.h"
#include "src/core/lib/support/env.h"
@@ -52,6 +54,7 @@
grpc_poll_function_type grpc_poll_function = poll;
static const grpc_event_engine_vtable *g_event_engine;
+static const char *g_poll_strategy_name = NULL;
typedef const grpc_event_engine_vtable *(*event_engine_factory_fn)(void);
@@ -61,7 +64,9 @@ typedef struct {
} event_engine_factory;
static const event_engine_factory g_factories[] = {
+ {"epoll", grpc_init_epoll_linux},
{"poll", grpc_init_poll_posix},
+ {"legacy", grpc_init_poll_and_epoll_posix},
};
static void add(const char *beg, const char *end, char ***ss, size_t *ns) {
@@ -97,6 +102,7 @@ static void try_engine(const char *engine) {
for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
if (is(engine, g_factories[i].name)) {
if ((g_event_engine = g_factories[i].factory())) {
+ g_poll_strategy_name = g_factories[i].name;
gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name);
return;
}
@@ -104,6 +110,9 @@ static void try_engine(const char *engine) {
}
}
+/* Call this only after calling grpc_event_engine_init() */
+const char *grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
+
void grpc_event_engine_init(void) {
char *s = gpr_getenv("GRPC_POLL_STRATEGY");
if (s == NULL) {
@@ -152,6 +161,10 @@ void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
g_event_engine->fd_shutdown(exec_ctx, fd);
}
+bool grpc_fd_is_shutdown(grpc_fd *fd) {
+ return g_event_engine->fd_is_shutdown(fd);
+}
+
void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
grpc_closure *closure) {
g_event_engine->fd_notify_on_read(exec_ctx, fd, closure);
@@ -162,11 +175,6 @@ void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
g_event_engine->fd_notify_on_write(exec_ctx, fd, closure);
}
-grpc_pollset *grpc_fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
- grpc_fd *fd) {
- return g_event_engine->fd_get_read_notifier_pollset(exec_ctx, fd);
-}
-
size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; }
void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
@@ -186,15 +194,15 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {
g_event_engine->pollset_destroy(pollset);
}
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
- grpc_pollset_worker **worker, gpr_timespec now,
- gpr_timespec deadline) {
- g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline);
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker, gpr_timespec now,
+ gpr_timespec deadline) {
+ return g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline);
}
-void grpc_pollset_kick(grpc_pollset *pollset,
- grpc_pollset_worker *specific_worker) {
- g_event_engine->pollset_kick(pollset, specific_worker);
+grpc_error *grpc_pollset_kick(grpc_pollset *pollset,
+ grpc_pollset_worker *specific_worker) {
+ return g_event_engine->pollset_kick(pollset, specific_worker);
}
void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
@@ -244,6 +252,6 @@ void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx,
g_event_engine->pollset_set_del_fd(exec_ctx, pollset_set, fd);
}
-void grpc_kick_poller(void) { g_event_engine->kick_poller(); }
+grpc_error *grpc_kick_poller(void) { return g_event_engine->kick_poller(); }
#endif // GPR_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 344bf63438..579c84ef70 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -55,6 +55,7 @@ typedef struct grpc_event_engine_vtable {
grpc_closure *closure);
void (*fd_notify_on_write)(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
grpc_closure *closure);
+ bool (*fd_is_shutdown)(grpc_fd *fd);
grpc_pollset *(*fd_get_read_notifier_pollset)(grpc_exec_ctx *exec_ctx,
grpc_fd *fd);
@@ -63,11 +64,11 @@ typedef struct grpc_event_engine_vtable {
grpc_closure *closure);
void (*pollset_reset)(grpc_pollset *pollset);
void (*pollset_destroy)(grpc_pollset *pollset);
- void (*pollset_work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
- grpc_pollset_worker **worker, gpr_timespec now,
- gpr_timespec deadline);
- void (*pollset_kick)(grpc_pollset *pollset,
- grpc_pollset_worker *specific_worker);
+ grpc_error *(*pollset_work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker, gpr_timespec now,
+ gpr_timespec deadline);
+ grpc_error *(*pollset_kick)(grpc_pollset *pollset,
+ grpc_pollset_worker *specific_worker);
void (*pollset_add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
struct grpc_fd *fd);
@@ -90,7 +91,7 @@ typedef struct grpc_event_engine_vtable {
void (*pollset_set_del_fd)(grpc_exec_ctx *exec_ctx,
grpc_pollset_set *pollset_set, grpc_fd *fd);
- void (*kick_poller)(void);
+ grpc_error *(*kick_poller)(void);
void (*shutdown_engine)(void);
} grpc_event_engine_vtable;
@@ -98,6 +99,9 @@ typedef struct grpc_event_engine_vtable {
void grpc_event_engine_init(void);
void grpc_event_engine_shutdown(void);
+/* Return the name of the poll strategy */
+const char *grpc_get_poll_strategy_name();
+
/* Create a wrapped file descriptor.
Requires fd is a non-blocking file descriptor.
This takes ownership of closing fd. */
@@ -116,7 +120,10 @@ int grpc_fd_wrapped_fd(grpc_fd *fd);
void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done,
int *release_fd, const char *reason);
-/* Cause any current callbacks to error out with GRPC_CALLBACK_CANCELLED. */
+/* Has grpc_fd_shutdown been called on an fd? */
+bool grpc_fd_is_shutdown(grpc_fd *fd);
+
+/* Cause any current and future callbacks to fail. */
void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd);
/* Register read interest, causing read_cb to be called once when fd becomes
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
index e451479073..c44aafcddf 100644
--- a/src/core/lib/iomgr/exec_ctx.c
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -63,11 +63,12 @@ bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) {
grpc_closure *c = exec_ctx->closure_list.head;
exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL;
while (c != NULL) {
- bool success = (bool)(c->final_data & 1);
- grpc_closure *next = (grpc_closure *)(c->final_data & ~(uintptr_t)1);
+ grpc_closure *next = c->next_data.next;
+ grpc_error *error = c->error;
did_something = true;
GPR_TIMER_BEGIN("grpc_exec_ctx_flush.cb", 0);
- c->cb(exec_ctx, c->cb_arg, success);
+ c->cb(exec_ctx, c->cb_arg, error);
+ GRPC_ERROR_UNREF(error);
GPR_TIMER_END("grpc_exec_ctx_flush.cb", 0);
c = next;
}
@@ -81,11 +82,11 @@ void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) {
grpc_exec_ctx_flush(exec_ctx);
}
-void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
- bool success,
- grpc_workqueue *offload_target_or_null) {
+void grpc_exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+ grpc_error *error,
+ grpc_workqueue *offload_target_or_null) {
GPR_ASSERT(offload_target_or_null == NULL);
- grpc_closure_list_add(&exec_ctx->closure_list, closure, success);
+ grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
}
void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 9d47a262f8..38f27d9b13 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -94,9 +94,9 @@ bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx);
* the instance is destroyed, or work may be lost. */
void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx);
/** Add a closure to be executed at the next flush/finish point */
-void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
- bool success,
- grpc_workqueue *offload_target_or_null);
+void grpc_exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+ grpc_error *error,
+ grpc_workqueue *offload_target_or_null);
/** Returns true if we'd like to leave this execution context as soon as
possible: useful for deciding whether to do something more or not depending
on outside context */
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
index 36e22e4271..8d7535d6fe 100644
--- a/src/core/lib/iomgr/executor.c
+++ b/src/core/lib/iomgr/executor.c
@@ -112,10 +112,10 @@ static void maybe_spawn_locked() {
g_executor.pending_join = 1;
}
-void grpc_executor_enqueue(grpc_closure *closure, bool success) {
+void grpc_executor_push(grpc_closure *closure, grpc_error *error) {
gpr_mu_lock(&g_executor.mu);
if (g_executor.shutting_down == 0) {
- grpc_closure_list_add(&g_executor.closures, closure, success);
+ grpc_closure_list_append(&g_executor.closures, closure, error);
maybe_spawn_locked();
}
gpr_mu_unlock(&g_executor.mu);
diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h
index b7e6f51aa5..da9dcd07d0 100644
--- a/src/core/lib/iomgr/executor.h
+++ b/src/core/lib/iomgr/executor.h
@@ -45,7 +45,7 @@ void grpc_executor_init();
/** Enqueue \a closure for its eventual execution of \a f(arg) on a separate
* thread */
-void grpc_executor_enqueue(grpc_closure *closure, bool success);
+void grpc_executor_push(grpc_closure *closure, grpc_error *error);
/** Shutdown the executor, running all pending work as part of the call */
void grpc_executor_shutdown();
diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c
index d46558ab1b..2532e52e48 100644
--- a/src/core/lib/iomgr/iocp_windows.c
+++ b/src/core/lib/iomgr/iocp_windows.c
@@ -39,7 +39,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/thd.h>
#include "src/core/lib/iomgr/iocp_windows.h"
@@ -104,7 +104,6 @@ grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx,
} else if (overlapped == &socket->read_info.overlapped) {
info = &socket->read_info;
} else {
- gpr_log(GPR_ERROR, "Unknown IOCP operation");
abort();
}
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes,
@@ -112,16 +111,7 @@ grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx,
info->bytes_transfered = bytes;
info->wsa_error = success ? 0 : WSAGetLastError();
GPR_ASSERT(overlapped == &info->overlapped);
- GPR_ASSERT(!info->has_pending_iocp);
- gpr_mu_lock(&socket->state_mu);
- if (info->closure) {
- closure = info->closure;
- info->closure = NULL;
- } else {
- info->has_pending_iocp = 1;
- }
- gpr_mu_unlock(&socket->state_mu);
- grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+ grpc_socket_become_ready(exec_ctx, socket, info);
return GRPC_IOCP_WORK_WORK;
}
@@ -176,33 +166,4 @@ void grpc_iocp_add_socket(grpc_winsocket *socket) {
GPR_ASSERT(ret == g_iocp);
}
-/* Calling notify_on_read or write means either of two things:
- -) The IOCP already completed in the background, and we need to call
- the callback now.
- -) The IOCP hasn't completed yet, and we're queuing it for later. */
-static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx,
- grpc_winsocket *socket, grpc_closure *closure,
- grpc_winsocket_callback_info *info) {
- GPR_ASSERT(info->closure == NULL);
- gpr_mu_lock(&socket->state_mu);
- if (info->has_pending_iocp) {
- info->has_pending_iocp = 0;
- grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
- } else {
- info->closure = closure;
- }
- gpr_mu_unlock(&socket->state_mu);
-}
-
-void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx,
- grpc_winsocket *socket,
- grpc_closure *closure) {
- socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info);
-}
-
-void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket,
- grpc_closure *closure) {
- socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info);
-}
-
#endif /* GPR_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h
index ae210fa7d7..011fee38ff 100644
--- a/src/core/lib/iomgr/iocp_windows.h
+++ b/src/core/lib/iomgr/iocp_windows.h
@@ -52,12 +52,4 @@ void grpc_iocp_flush(void);
void grpc_iocp_shutdown(void);
void grpc_iocp_add_socket(grpc_winsocket *);
-void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx,
- grpc_winsocket *winsocket,
- grpc_closure *closure);
-
-void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx,
- grpc_winsocket *winsocket,
- grpc_closure *closure);
-
#endif /* GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H */
diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c
index 60cef8ba77..89292a153e 100644
--- a/src/core/lib/iomgr/iomgr.c
+++ b/src/core/lib/iomgr/iomgr.c
@@ -96,7 +96,8 @@ void grpc_iomgr_shutdown(void) {
gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time),
gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) {
if (g_root_object.next != &g_root_object) {
- gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed",
+ gpr_log(GPR_DEBUG,
+ "Waiting for %" PRIuPTR " iomgr objects to be destroyed",
count_objects());
}
last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
@@ -114,9 +115,9 @@ void grpc_iomgr_shutdown(void) {
if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) {
if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) {
if (g_root_object.next != &g_root_object) {
- gpr_log(GPR_DEBUG,
- "Failed to free %d iomgr objects before shutdown deadline: "
- "memory leaks are likely",
+ gpr_log(GPR_DEBUG, "Failed to free %" PRIuPTR
+ " iomgr objects before shutdown deadline: "
+ "memory leaks are likely",
count_objects());
dump_objects("LEAKED");
if (grpc_iomgr_abort_on_leaks()) {
diff --git a/src/core/lib/iomgr/iomgr_windows.c b/src/core/lib/iomgr/iomgr_windows.c
index 398517fc75..7653f6e635 100644
--- a/src/core/lib/iomgr/iomgr_windows.c
+++ b/src/core/lib/iomgr/iomgr_windows.c
@@ -35,7 +35,7 @@
#ifdef GPR_WINSOCK_SOCKET
-#include "src/core/lib/iomgr/sockaddr_win32.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
#include <grpc/support/log.h>
diff --git a/src/core/lib/support/load_file.c b/src/core/lib/iomgr/load_file.c
index f30aacdd4f..b62ecbc534 100644
--- a/src/core/lib/support/load_file.c
+++ b/src/core/lib/iomgr/load_file.c
@@ -31,7 +31,7 @@
*
*/
-#include "src/core/lib/support/load_file.h"
+#include "src/core/lib/iomgr/load_file.h"
#include <errno.h>
#include <string.h>
@@ -43,21 +43,19 @@
#include "src/core/lib/support/block_annotate.h"
#include "src/core/lib/support/string.h"
-gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
- int *success) {
+grpc_error *grpc_load_file(const char *filename, int add_null_terminator,
+ gpr_slice *output) {
unsigned char *contents = NULL;
size_t contents_size = 0;
- char *error_msg = NULL;
gpr_slice result = gpr_empty_slice();
FILE *file;
size_t bytes_read = 0;
+ grpc_error *error = GRPC_ERROR_NONE;
GRPC_SCHEDULING_START_BLOCKING_REGION;
file = fopen(filename, "rb");
if (file == NULL) {
- gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename,
- strerror(errno));
- GPR_ASSERT(error_msg != NULL);
+ error = GRPC_OS_ERROR(errno, "fopen");
goto end;
}
fseek(file, 0, SEEK_END);
@@ -67,25 +65,25 @@ gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
contents = gpr_malloc(contents_size + (add_null_terminator ? 1 : 0));
bytes_read = fread(contents, 1, contents_size, file);
if (bytes_read < contents_size) {
+ error = GRPC_OS_ERROR(errno, "fread");
GPR_ASSERT(ferror(file));
- gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
- strerror(errno), filename);
- GPR_ASSERT(error_msg != NULL);
goto end;
}
- if (success != NULL) *success = 1;
if (add_null_terminator) {
contents[contents_size++] = 0;
}
result = gpr_slice_new(contents, contents_size, gpr_free);
end:
- if (error_msg != NULL) {
- gpr_log(GPR_ERROR, "%s", error_msg);
- gpr_free(error_msg);
- if (success != NULL) *success = 0;
- }
+ *output = result;
if (file != NULL) fclose(file);
+ if (error != GRPC_ERROR_NONE) {
+ grpc_error *error_out = grpc_error_set_str(
+ GRPC_ERROR_CREATE_REFERENCING("Failed to load file", &error, 1),
+ GRPC_ERROR_STR_FILENAME, filename);
+ GRPC_ERROR_UNREF(error);
+ error = error_out;
+ }
GRPC_SCHEDULING_END_BLOCKING_REGION;
- return result;
+ return error;
}
diff --git a/src/core/lib/support/load_file.h b/src/core/lib/iomgr/load_file.h
index 9a4b27942e..9aac2225d1 100644
--- a/src/core/lib/support/load_file.h
+++ b/src/core/lib/iomgr/load_file.h
@@ -31,25 +31,26 @@
*
*/
-#ifndef GRPC_CORE_LIB_SUPPORT_LOAD_FILE_H
-#define GRPC_CORE_LIB_SUPPORT_LOAD_FILE_H
+#ifndef GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
+#define GRPC_CORE_LIB_IOMGR_LOAD_FILE_H
#include <stdio.h>
#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/error.h"
+
#ifdef __cplusplus
extern "C" {
#endif
/* Loads the content of a file into a slice. add_null_terminator will add
- a NULL terminator if non-zero. The success parameter, if not NULL,
- will be set to 1 in case of success and 0 in case of failure. */
-gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
- int *success);
+ a NULL terminator if non-zero. */
+grpc_error *grpc_load_file(const char *filename, int add_null_terminator,
+ gpr_slice *slice);
#ifdef __cplusplus
}
#endif
-#endif /* GRPC_CORE_LIB_SUPPORT_LOAD_FILE_H */
+#endif /* GRPC_CORE_LIB_IOMGR_LOAD_FILE_H */
diff --git a/src/core/lib/iomgr/network_status_tracker.c b/src/core/lib/iomgr/network_status_tracker.c
new file mode 100644
index 0000000000..38a1c9b7d4
--- /dev/null
+++ b/src/core/lib/iomgr/network_status_tracker.c
@@ -0,0 +1,121 @@
+/*
+ *
+ * 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/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/iomgr/endpoint.h"
+
+typedef struct endpoint_ll_node {
+ grpc_endpoint *ep;
+ struct endpoint_ll_node *next;
+} endpoint_ll_node;
+
+static endpoint_ll_node *head = NULL;
+static gpr_mu g_endpoint_mutex;
+static bool g_init_done = false;
+
+void grpc_initialize_network_status_monitor() {
+ g_init_done = true;
+ gpr_mu_init(&g_endpoint_mutex);
+ // TODO(makarandd): Install callback with OS to monitor network status.
+}
+
+void grpc_destroy_network_status_monitor() {
+ for (endpoint_ll_node *curr = head; curr != NULL;) {
+ endpoint_ll_node *next = curr->next;
+ gpr_free(curr);
+ curr = next;
+ }
+ gpr_mu_destroy(&g_endpoint_mutex);
+}
+
+void grpc_network_status_register_endpoint(grpc_endpoint *ep) {
+ if (!g_init_done) {
+ grpc_initialize_network_status_monitor();
+ }
+ gpr_mu_lock(&g_endpoint_mutex);
+ if (head == NULL) {
+ head = (endpoint_ll_node *)gpr_malloc(sizeof(endpoint_ll_node));
+ head->ep = ep;
+ head->next = NULL;
+ } else {
+ endpoint_ll_node *prev_head = head;
+ head = (endpoint_ll_node *)gpr_malloc(sizeof(endpoint_ll_node));
+ head->ep = ep;
+ head->next = prev_head;
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+}
+
+void grpc_network_status_unregister_endpoint(grpc_endpoint *ep) {
+ gpr_mu_lock(&g_endpoint_mutex);
+ GPR_ASSERT(head);
+ bool found = false;
+ endpoint_ll_node *prev = head;
+ // if we're unregistering the head, just move head to the next
+ if (ep == head->ep) {
+ head = head->next;
+ gpr_free(prev);
+ found = true;
+ } else {
+ for (endpoint_ll_node *curr = head->next; curr != NULL; curr = curr->next) {
+ if (ep == curr->ep) {
+ prev->next = curr->next;
+ gpr_free(curr);
+ found = true;
+ break;
+ }
+ prev = curr;
+ }
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+ GPR_ASSERT(found);
+}
+
+// Walk the linked-list from head and execute shutdown. It is possible that
+// other threads might be in the process of shutdown as well, but that has
+// no side effect since endpoint shutdown is idempotent.
+void grpc_network_status_shutdown_all_endpoints() {
+ gpr_mu_lock(&g_endpoint_mutex);
+ if (head == NULL) {
+ gpr_mu_unlock(&g_endpoint_mutex);
+ return;
+ }
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ for (endpoint_ll_node *curr = head; curr != NULL; curr = curr->next) {
+ curr->ep->vtable->shutdown(&exec_ctx, curr->ep);
+ }
+ gpr_mu_unlock(&g_endpoint_mutex);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
diff --git a/src/core/lib/iomgr/network_status_tracker.h b/src/core/lib/iomgr/network_status_tracker.h
new file mode 100644
index 0000000000..74a1aa8135
--- /dev/null
+++ b/src/core/lib/iomgr/network_status_tracker.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H
+#include "src/core/lib/iomgr/endpoint.h"
+
+void grpc_network_status_register_endpoint(grpc_endpoint *ep);
+void grpc_network_status_unregister_endpoint(grpc_endpoint *ep);
+void grpc_network_status_shutdown_all_endpoints();
+#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */
diff --git a/src/core/lib/iomgr/polling_entity.c b/src/core/lib/iomgr/polling_entity.c
new file mode 100644
index 0000000000..d1686aa12f
--- /dev/null
+++ b/src/core/lib/iomgr/polling_entity.c
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2016, 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/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/polling_entity.h"
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset_set(
+ grpc_pollset_set *pollset_set) {
+ grpc_polling_entity pollent;
+ pollent.pollent.pollset_set = pollset_set;
+ pollent.tag = POPS_POLLSET_SET;
+ return pollent;
+}
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset(
+ grpc_pollset *pollset) {
+ grpc_polling_entity pollent;
+ pollent.pollent.pollset = pollset;
+ pollent.tag = POPS_POLLSET;
+ return pollent;
+}
+
+grpc_pollset *grpc_polling_entity_pollset(grpc_polling_entity *pollent) {
+ if (pollent->tag == POPS_POLLSET) {
+ return pollent->pollent.pollset;
+ }
+ return NULL;
+}
+
+grpc_pollset_set *grpc_polling_entity_pollset_set(
+ grpc_polling_entity *pollent) {
+ if (pollent->tag == POPS_POLLSET_SET) {
+ return pollent->pollent.pollset_set;
+ }
+ return NULL;
+}
+
+bool grpc_polling_entity_is_empty(const grpc_polling_entity *pollent) {
+ return pollent->tag == POPS_NONE;
+}
+
+void grpc_polling_entity_add_to_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_polling_entity *pollent,
+ grpc_pollset_set *pss_dst) {
+ if (pollent->tag == POPS_POLLSET) {
+ GPR_ASSERT(pollent->pollent.pollset != NULL);
+ grpc_pollset_set_add_pollset(exec_ctx, pss_dst, pollent->pollent.pollset);
+ } else if (pollent->tag == POPS_POLLSET_SET) {
+ GPR_ASSERT(pollent->pollent.pollset_set != NULL);
+ grpc_pollset_set_add_pollset_set(exec_ctx, pss_dst,
+ pollent->pollent.pollset_set);
+ } else {
+ gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag);
+ abort();
+ }
+}
+
+void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_polling_entity *pollent,
+ grpc_pollset_set *pss_dst) {
+ if (pollent->tag == POPS_POLLSET) {
+ GPR_ASSERT(pollent->pollent.pollset != NULL);
+ grpc_pollset_set_del_pollset(exec_ctx, pss_dst, pollent->pollent.pollset);
+ } else if (pollent->tag == POPS_POLLSET_SET) {
+ GPR_ASSERT(pollent->pollent.pollset_set != NULL);
+ grpc_pollset_set_del_pollset_set(exec_ctx, pss_dst,
+ pollent->pollent.pollset_set);
+ } else {
+ gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag);
+ abort();
+ }
+}
diff --git a/src/core/lib/iomgr/polling_entity.h b/src/core/lib/iomgr/polling_entity.h
new file mode 100644
index 0000000000..e81531053c
--- /dev/null
+++ b/src/core/lib/iomgr/polling_entity.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLING_ENTITY_H
+#define GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H
+
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+/* A grpc_polling_entity is a pollset-or-pollset_set container. It allows
+ * functions that
+ * accept a pollset XOR a pollset_set to do so through an abstract interface.
+ * No ownership is taken. */
+
+typedef struct grpc_polling_entity {
+ union {
+ grpc_pollset *pollset;
+ grpc_pollset_set *pollset_set;
+ } pollent;
+ enum pops_tag { POPS_NONE, POPS_POLLSET, POPS_POLLSET_SET } tag;
+} grpc_polling_entity;
+
+grpc_polling_entity grpc_polling_entity_create_from_pollset_set(
+ grpc_pollset_set *pollset_set);
+grpc_polling_entity grpc_polling_entity_create_from_pollset(
+ grpc_pollset *pollset);
+
+/** If \a pollent contains a pollset, return it. Otherwise, return NULL */
+grpc_pollset *grpc_polling_entity_pollset(grpc_polling_entity *pollent);
+
+/** If \a pollent contains a pollset_set, return it. Otherwise, return NULL */
+grpc_pollset_set *grpc_polling_entity_pollset_set(grpc_polling_entity *pollent);
+
+bool grpc_polling_entity_is_empty(const grpc_polling_entity *pollent);
+
+/** Add the pollset or pollset_set in \a pollent to the destination pollset_set
+ * \a
+ * pss_dst */
+void grpc_polling_entity_add_to_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_polling_entity *pollent,
+ grpc_pollset_set *pss_dst);
+
+/** Delete the pollset or pollset_set in \a pollent from the destination
+ * pollset_set \a
+ * pss_dst */
+void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_polling_entity *pollent,
+ grpc_pollset_set *pss_dst);
+/* pollset_set specific */
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H */
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index c40a474877..8d9edc8406 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -81,14 +81,15 @@ void grpc_pollset_destroy(grpc_pollset *pollset);
May call grpc_closure_list_run on grpc_closure_list, without holding the
pollset
lock */
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
- grpc_pollset_worker **worker, gpr_timespec now,
- gpr_timespec deadline);
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker, gpr_timespec now,
+ gpr_timespec deadline) GRPC_MUST_USE_RESULT;
/* Break one polling thread out of polling work for this pollset.
If specific_worker is GRPC_POLLSET_KICK_BROADCAST, kick ALL the workers.
Otherwise, if specific_worker is non-NULL, then kick that worker. */
-void grpc_pollset_kick(grpc_pollset *pollset,
- grpc_pollset_worker *specific_worker);
+grpc_error *grpc_pollset_kick(grpc_pollset *pollset,
+ grpc_pollset_worker *specific_worker)
+ GRPC_MUST_USE_RESULT;
#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_H */
diff --git a/src/core/lib/iomgr/pollset_set_windows.c b/src/core/lib/iomgr/pollset_set_windows.c
index 89f60b92fb..a35a9766fc 100644
--- a/src/core/lib/iomgr/pollset_set_windows.c
+++ b/src/core/lib/iomgr/pollset_set_windows.c
@@ -32,12 +32,15 @@
*/
#include <grpc/support/port_platform.h>
+#include <stdint.h>
#ifdef GPR_WINSOCK_SOCKET
#include "src/core/lib/iomgr/pollset_set_windows.h"
-grpc_pollset_set* grpc_pollset_set_create(void) { return NULL; }
+grpc_pollset_set* grpc_pollset_set_create(void) {
+ return (grpc_pollset_set*)((intptr_t)0xdeafbeef);
+}
void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {}
diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c
index bff5c586f8..626dd784b3 100644
--- a/src/core/lib/iomgr/pollset_windows.c
+++ b/src/core/lib/iomgr/pollset_windows.c
@@ -109,7 +109,7 @@ void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
pollset->shutting_down = 1;
grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
if (!pollset->is_iocp_worker) {
- grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
} else {
pollset->on_shutdown = closure;
}
@@ -127,9 +127,9 @@ void grpc_pollset_reset(grpc_pollset *pollset) {
pollset->on_shutdown = NULL;
}
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
- grpc_pollset_worker **worker_hdl, gpr_timespec now,
- gpr_timespec deadline) {
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+ grpc_pollset_worker **worker_hdl,
+ gpr_timespec now, gpr_timespec deadline) {
grpc_pollset_worker worker;
*worker_hdl = &worker;
@@ -167,7 +167,8 @@ void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
}
if (pollset->shutting_down && pollset->on_shutdown != NULL) {
- grpc_exec_ctx_enqueue(exec_ctx, pollset->on_shutdown, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE,
+ NULL);
pollset->on_shutdown = NULL;
}
goto done;
@@ -197,9 +198,11 @@ done:
}
gpr_cv_destroy(&worker.cv);
*worker_hdl = NULL;
+ return GRPC_ERROR_NONE;
}
-void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) {
+grpc_error *grpc_pollset_kick(grpc_pollset *p,
+ grpc_pollset_worker *specific_worker) {
if (specific_worker != NULL) {
if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
for (specific_worker =
@@ -233,6 +236,7 @@ void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) {
p->kicked_without_pollers = 1;
}
}
+ return GRPC_ERROR_NONE;
}
void grpc_kick_poller(void) { grpc_iocp_kick(); }
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index ef198fe0f6..ddbe375755 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -50,24 +50,20 @@ typedef struct {
grpc_resolved_address *addrs;
} grpc_resolved_addresses;
-/* Async result callback:
- On success: addresses is the result, and the callee must call
- grpc_resolved_addresses_destroy when it's done with them
- On failure: addresses is NULL */
-typedef void (*grpc_resolve_cb)(grpc_exec_ctx *exec_ctx, void *arg,
- grpc_resolved_addresses *addresses);
/* Asynchronously resolve addr. Use default_port if a port isn't designated
in addr, otherwise use the port in addr. */
/* TODO(ctiller): add a timeout here */
extern void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
const char *default_port,
- grpc_resolve_cb cb, void *arg);
+ grpc_closure *on_done,
+ grpc_resolved_addresses **addresses);
/* Destroy resolved addresses */
void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addresses);
/* Resolve addr in a blocking fashion. Returns NULL on failure. On success,
result must be freed with grpc_resolved_addresses_destroy. */
-extern grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
- const char *name, const char *default_port);
+extern grpc_error *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port,
+ grpc_resolved_addresses **addresses);
#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index cae91eec20..4e9f978584 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -54,38 +54,33 @@
#include "src/core/lib/support/block_annotate.h"
#include "src/core/lib/support/string.h"
-typedef struct {
- char *name;
- char *default_port;
- grpc_resolve_cb cb;
- grpc_closure request_closure;
- void *arg;
-} request;
-
-static grpc_resolved_addresses *blocking_resolve_address_impl(
- const char *name, const char *default_port) {
+static grpc_error *blocking_resolve_address_impl(
+ const char *name, const char *default_port,
+ grpc_resolved_addresses **addresses) {
struct addrinfo hints;
struct addrinfo *result = NULL, *resp;
char *host;
char *port;
int s;
size_t i;
- grpc_resolved_addresses *addrs = NULL;
+ grpc_error *err;
if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
name[4] == ':' && name[5] != 0) {
- return grpc_resolve_unix_domain_address(name + 5);
+ return grpc_resolve_unix_domain_address(name + 5, addresses);
}
/* parse name, splitting it into host and port parts */
gpr_split_host_port(name, &host, &port);
if (host == NULL) {
- gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name);
+ err = grpc_error_set_str(GRPC_ERROR_CREATE("unparseable host:port"),
+ GRPC_ERROR_STR_TARGET_ADDRESS, name);
goto done;
}
if (port == NULL) {
if (default_port == NULL) {
- gpr_log(GPR_ERROR, "no port in name '%s'", name);
+ err = grpc_error_set_str(GRPC_ERROR_CREATE("no port in name"),
+ GRPC_ERROR_STR_TARGET_ADDRESS, name);
goto done;
}
port = gpr_strdup(default_port);
@@ -115,23 +110,31 @@ static grpc_resolved_addresses *blocking_resolve_address_impl(
}
if (s != 0) {
- gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s));
+ err = grpc_error_set_str(
+ grpc_error_set_str(
+ grpc_error_set_str(grpc_error_set_int(GRPC_ERROR_CREATE("OS Error"),
+ GRPC_ERROR_INT_ERRNO, s),
+ GRPC_ERROR_STR_OS_ERROR, gai_strerror(s)),
+ GRPC_ERROR_STR_SYSCALL, "getaddrinfo"),
+ GRPC_ERROR_STR_TARGET_ADDRESS, name);
goto done;
}
/* Success path: set addrs non-NULL, fill it in */
- addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
- addrs->naddrs = 0;
+ *addresses = gpr_malloc(sizeof(grpc_resolved_addresses));
+ (*addresses)->naddrs = 0;
for (resp = result; resp != NULL; resp = resp->ai_next) {
- addrs->naddrs++;
+ (*addresses)->naddrs++;
}
- addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs);
+ (*addresses)->addrs =
+ gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs);
i = 0;
for (resp = result; resp != NULL; resp = resp->ai_next) {
- memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
- addrs->addrs[i].len = resp->ai_addrlen;
+ memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+ (*addresses)->addrs[i].len = resp->ai_addrlen;
i++;
}
+ err = GRPC_ERROR_NONE;
done:
gpr_free(host);
@@ -139,45 +142,59 @@ done:
if (result) {
freeaddrinfo(result);
}
- return addrs;
+ return err;
}
-grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
- const char *name, const char *default_port) = blocking_resolve_address_impl;
+grpc_error *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port,
+ grpc_resolved_addresses **addresses) = blocking_resolve_address_impl;
+
+typedef struct {
+ char *name;
+ char *default_port;
+ grpc_closure *on_done;
+ grpc_resolved_addresses **addrs_out;
+ grpc_closure request_closure;
+ void *arg;
+} request;
/* Callback to be passed to grpc_executor to asynch-ify
* grpc_blocking_resolve_address */
-static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) {
+static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
+ grpc_error *error) {
request *r = rp;
- grpc_resolved_addresses *resolved =
- grpc_blocking_resolve_address(r->name, r->default_port);
- void *arg = r->arg;
- grpc_resolve_cb cb = r->cb;
+ grpc_exec_ctx_sched(
+ exec_ctx, r->on_done,
+ grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out),
+ NULL);
gpr_free(r->name);
gpr_free(r->default_port);
- cb(exec_ctx, arg, resolved);
gpr_free(r);
}
void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
- gpr_free(addrs->addrs);
+ if (addrs != NULL) {
+ gpr_free(addrs->addrs);
+ }
gpr_free(addrs);
}
static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
- const char *default_port, grpc_resolve_cb cb,
- void *arg) {
+ const char *default_port,
+ grpc_closure *on_done,
+ grpc_resolved_addresses **addrs) {
request *r = gpr_malloc(sizeof(request));
grpc_closure_init(&r->request_closure, do_request_thread, r);
r->name = gpr_strdup(name);
r->default_port = gpr_strdup(default_port);
- r->cb = cb;
- r->arg = arg;
- grpc_executor_enqueue(&r->request_closure, 1);
+ r->on_done = on_done;
+ r->addrs_out = addrs;
+ grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
}
void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
- const char *default_port, grpc_resolve_cb cb,
- void *arg) = resolve_address_impl;
+ const char *default_port, grpc_closure *on_done,
+ grpc_resolved_addresses **addrs) =
+ resolve_address_impl;
#endif
diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
index 914736234d..2af8af82dc 100644
--- a/src/core/lib/iomgr/resolve_address_windows.c
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -43,7 +43,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/string_util.h>
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
@@ -56,30 +56,37 @@
typedef struct {
char *name;
char *default_port;
- grpc_resolve_cb cb;
grpc_closure request_closure;
- void *arg;
+ grpc_closure *on_done;
+ grpc_resolved_addresses **addresses;
} request;
-static grpc_resolved_addresses *blocking_resolve_address_impl(
- const char *name, const char *default_port) {
+static grpc_error *blocking_resolve_address_impl(
+ const char *name, const char *default_port,
+ grpc_resolved_addresses **addresses) {
struct addrinfo hints;
struct addrinfo *result = NULL, *resp;
char *host;
char *port;
int s;
size_t i;
- grpc_resolved_addresses *addrs = NULL;
+ grpc_error *error = GRPC_ERROR_NONE;
/* parse name, splitting it into host and port parts */
gpr_split_host_port(name, &host, &port);
if (host == NULL) {
- gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name);
+ char *msg;
+ gpr_asprintf(&msg, "unparseable host:port: '%s'", name);
+ error = GRPC_ERROR_CREATE(msg);
+ gpr_free(msg);
goto done;
}
if (port == NULL) {
if (default_port == NULL) {
- gpr_log(GPR_ERROR, "no port in name '%s'", name);
+ char *msg;
+ gpr_asprintf(&msg, "no port in name '%s'", name);
+ error = GRPC_ERROR_CREATE(msg);
+ gpr_free(msg);
goto done;
}
port = gpr_strdup(default_port);
@@ -95,31 +102,30 @@ static grpc_resolved_addresses *blocking_resolve_address_impl(
s = getaddrinfo(host, port, &hints, &result);
GRPC_SCHEDULING_END_BLOCKING_REGION;
if (s != 0) {
- char *error_message = gpr_format_message(s);
- gpr_log(GPR_ERROR, "getaddrinfo: %s", error_message);
- gpr_free(error_message);
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo");
goto done;
}
/* Success path: set addrs non-NULL, fill it in */
- addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
- addrs->naddrs = 0;
+ (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses));
+ (*addresses)->naddrs = 0;
for (resp = result; resp != NULL; resp = resp->ai_next) {
- addrs->naddrs++;
+ (*addresses)->naddrs++;
}
- addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs);
+ (*addresses)->addrs =
+ gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs);
i = 0;
for (resp = result; resp != NULL; resp = resp->ai_next) {
- memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
- addrs->addrs[i].len = resp->ai_addrlen;
+ memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+ (*addresses)->addrs[i].len = resp->ai_addrlen;
i++;
}
{
- for (i = 0; i < addrs->naddrs; i++) {
+ for (i = 0; i < (*addresses)->naddrs; i++) {
char *buf;
- grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr,
- 0);
+ grpc_sockaddr_to_string(
+ &buf, (struct sockaddr *)&(*addresses)->addrs[i].addr, 0);
gpr_free(buf);
}
}
@@ -130,45 +136,53 @@ done:
if (result) {
freeaddrinfo(result);
}
- return addrs;
+ return error;
}
-grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
- const char *name, const char *default_port) = blocking_resolve_address_impl;
+grpc_error *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port,
+ grpc_resolved_addresses **addresses) = blocking_resolve_address_impl;
/* Callback to be passed to grpc_executor to asynch-ify
* grpc_blocking_resolve_address */
-static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) {
+static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
+ grpc_error *error) {
request *r = rp;
- grpc_resolved_addresses *resolved =
- grpc_blocking_resolve_address(r->name, r->default_port);
- void *arg = r->arg;
- grpc_resolve_cb cb = r->cb;
+ if (error == GRPC_ERROR_NONE) {
+ error =
+ grpc_blocking_resolve_address(r->name, r->default_port, r->addresses);
+ } else {
+ GRPC_ERROR_REF(error);
+ }
+ grpc_exec_ctx_sched(exec_ctx, r->on_done, error, NULL);
gpr_free(r->name);
gpr_free(r->default_port);
- cb(exec_ctx, arg, resolved);
gpr_free(r);
}
void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
- gpr_free(addrs->addrs);
+ if (addrs != NULL) {
+ gpr_free(addrs->addrs);
+ }
gpr_free(addrs);
}
static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
- const char *default_port, grpc_resolve_cb cb,
- void *arg) {
+ const char *default_port,
+ grpc_closure *on_done,
+ grpc_resolved_addresses **addresses) {
request *r = gpr_malloc(sizeof(request));
grpc_closure_init(&r->request_closure, do_request_thread, r);
r->name = gpr_strdup(name);
r->default_port = gpr_strdup(default_port);
- r->cb = cb;
- r->arg = arg;
- grpc_executor_enqueue(&r->request_closure, 1);
+ r->on_done = on_done;
+ r->addresses = addresses;
+ grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
}
void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
- const char *default_port, grpc_resolve_cb cb,
- void *arg) = resolve_address_impl;
+ const char *default_port, grpc_closure *on_done,
+ grpc_resolved_addresses **addresses) =
+ resolve_address_impl;
#endif
diff --git a/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h
index 891a2f094f..5563d0b8a6 100644
--- a/src/core/lib/iomgr/sockaddr.h
+++ b/src/core/lib/iomgr/sockaddr.h
@@ -36,8 +36,8 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
-#include "src/core/lib/iomgr/sockaddr_win32.h"
+#ifdef GPR_WINDOWS
+#include "src/core/lib/iomgr/sockaddr_windows.h"
#endif
#ifdef GPR_POSIX_SOCKETADDR
diff --git a/src/core/lib/iomgr/sockaddr_win32.h b/src/core/lib/iomgr/sockaddr_windows.h
index 02aeae7619..971db5b32b 100644
--- a/src/core/lib/iomgr/sockaddr_win32.h
+++ b/src/core/lib/iomgr/sockaddr_windows.h
@@ -31,8 +31,8 @@
*
*/
-#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WIN32_H
-#define GRPC_CORE_LIB_IOMGR_SOCKADDR_WIN32_H
+#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -40,4 +40,4 @@
// must be included after the above
#include <mswsock.h>
-#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WIN32_H */
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H */
diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c
index fa83ceef30..d2f6261e2a 100644
--- a/src/core/lib/iomgr/socket_utils_common_posix.c
+++ b/src/core/lib/iomgr/socket_utils_common_posix.c
@@ -49,6 +49,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
@@ -57,10 +58,10 @@
#include "src/core/lib/support/string.h"
/* set a socket to non blocking mode */
-int grpc_set_socket_nonblocking(int fd, int non_blocking) {
+grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking) {
int oldflags = fcntl(fd, F_GETFL, 0);
if (oldflags < 0) {
- return 0;
+ return GRPC_OS_ERROR(errno, "fcntl");
}
if (non_blocking) {
@@ -70,52 +71,71 @@ int grpc_set_socket_nonblocking(int fd, int non_blocking) {
}
if (fcntl(fd, F_SETFL, oldflags) != 0) {
- return 0;
+ return GRPC_OS_ERROR(errno, "fcntl");
}
- return 1;
+ return GRPC_ERROR_NONE;
}
-int grpc_set_socket_no_sigpipe_if_possible(int fd) {
+grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) {
#ifdef GPR_HAVE_SO_NOSIGPIPE
int val = 1;
int newval;
socklen_t intlen = sizeof(newval);
- return 0 == setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) &&
- 0 == getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen) &&
- (newval != 0) == val;
-#else
- return 1;
+ if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)");
+ }
+ if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) {
+ return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)");
+ }
+ if ((newval != 0) != (val != 0)) {
+ return GRPC_ERROR_CREATE("Failed to set SO_NOSIGPIPE");
+ }
#endif
+ return GRPC_ERROR_NONE;
}
-int grpc_set_socket_ip_pktinfo_if_possible(int fd) {
+grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) {
#ifdef GPR_HAVE_IP_PKTINFO
int get_local_ip = 1;
- return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
- sizeof(get_local_ip));
-#else
- (void)fd;
- return 1;
+ if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
+ sizeof(get_local_ip))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)");
+ }
#endif
+ return GRPC_ERROR_NONE;
}
-int grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
+grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
#ifdef GPR_HAVE_IPV6_RECVPKTINFO
int get_local_ip = 1;
- return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
- sizeof(get_local_ip));
-#else
- (void)fd;
- return 1;
+ if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
+ sizeof(get_local_ip))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)");
+ }
#endif
+ return GRPC_ERROR_NONE;
+}
+
+grpc_error *grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) {
+ return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes,
+ sizeof(buffer_size_bytes))
+ ? GRPC_ERROR_NONE
+ : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)");
+}
+
+grpc_error *grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) {
+ return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes,
+ sizeof(buffer_size_bytes))
+ ? GRPC_ERROR_NONE
+ : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)");
}
/* set a socket to close on exec */
-int grpc_set_socket_cloexec(int fd, int close_on_exec) {
+grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec) {
int oldflags = fcntl(fd, F_GETFD, 0);
if (oldflags < 0) {
- return 0;
+ return GRPC_OS_ERROR(errno, "fcntl");
}
if (close_on_exec) {
@@ -125,30 +145,67 @@ int grpc_set_socket_cloexec(int fd, int close_on_exec) {
}
if (fcntl(fd, F_SETFD, oldflags) != 0) {
- return 0;
+ return GRPC_OS_ERROR(errno, "fcntl");
}
- return 1;
+ return GRPC_ERROR_NONE;
}
/* set a socket to reuse old addresses */
-int grpc_set_socket_reuse_addr(int fd, int reuse) {
+grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse) {
int val = (reuse != 0);
int newval;
socklen_t intlen = sizeof(newval);
- return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) &&
- 0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) &&
- (newval != 0) == val;
+ if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)");
+ }
+ if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) {
+ return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)");
+ }
+ if ((newval != 0) != val) {
+ return GRPC_ERROR_CREATE("Failed to set SO_REUSEADDR");
+ }
+
+ return GRPC_ERROR_NONE;
+}
+
+/* set a socket to reuse old addresses */
+grpc_error *grpc_set_socket_reuse_port(int fd, int reuse) {
+#ifndef SO_REUSEPORT
+ return GRPC_ERROR_CREATE("SO_REUSEPORT unavailable on compiling system");
+#else
+ int val = (reuse != 0);
+ int newval;
+ socklen_t intlen = sizeof(newval);
+ if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)");
+ }
+ if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) {
+ return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)");
+ }
+ if ((newval != 0) != val) {
+ return GRPC_ERROR_CREATE("Failed to set SO_REUSEPORT");
+ }
+
+ return GRPC_ERROR_NONE;
+#endif
}
/* disable nagle */
-int grpc_set_socket_low_latency(int fd, int low_latency) {
+grpc_error *grpc_set_socket_low_latency(int fd, int low_latency) {
int val = (low_latency != 0);
int newval;
socklen_t intlen = sizeof(newval);
- return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) &&
- 0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
- (newval != 0) == val;
+ if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
+ return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)");
+ }
+ if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) {
+ return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)");
+ }
+ if ((newval != 0) != val) {
+ return GRPC_ERROR_CREATE("Failed to set TCP_NODELAY");
+ }
+ return GRPC_ERROR_NONE;
}
static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
@@ -196,35 +253,47 @@ static int set_socket_dualstack(int fd) {
}
}
-int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
- int protocol, grpc_dualstack_mode *dsmode) {
+static grpc_error *error_for_fd(int fd, const struct sockaddr *addr) {
+ if (fd >= 0) return GRPC_ERROR_NONE;
+ char *addr_str;
+ grpc_sockaddr_to_string(&addr_str, addr, 0);
+ grpc_error *err = grpc_error_set_str(GRPC_OS_ERROR(errno, "socket"),
+ GRPC_ERROR_STR_TARGET_ADDRESS, addr_str);
+ gpr_free(addr_str);
+ return err;
+}
+
+grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+ int protocol,
+ grpc_dualstack_mode *dsmode,
+ int *newfd) {
int family = addr->sa_family;
if (family == AF_INET6) {
- int fd;
if (grpc_ipv6_loopback_available()) {
- fd = socket(family, type, protocol);
+ *newfd = socket(family, type, protocol);
} else {
- fd = -1;
+ *newfd = -1;
errno = EAFNOSUPPORT;
}
/* Check if we've got a valid dualstack socket. */
- if (fd >= 0 && set_socket_dualstack(fd)) {
+ if (*newfd >= 0 && set_socket_dualstack(*newfd)) {
*dsmode = GRPC_DSMODE_DUALSTACK;
- return fd;
+ return GRPC_ERROR_NONE;
}
/* If this isn't an IPv4 address, then return whatever we've got. */
if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
*dsmode = GRPC_DSMODE_IPV6;
- return fd;
+ return error_for_fd(*newfd, addr);
}
/* Fall back to AF_INET. */
- if (fd >= 0) {
- close(fd);
+ if (*newfd >= 0) {
+ close(*newfd);
}
family = AF_INET;
}
*dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
- return socket(family, type, protocol);
+ *newfd = socket(family, type, protocol);
+ return error_for_fd(*newfd, addr);
}
#endif
diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h
index a8f6e5e658..7bcc2219ae 100644
--- a/src/core/lib/iomgr/socket_utils_posix.h
+++ b/src/core/lib/iomgr/socket_utils_posix.h
@@ -37,21 +37,26 @@
#include <sys/socket.h>
#include <unistd.h>
+#include "src/core/lib/iomgr/error.h"
+
/* a wrapper for accept or accept4 */
int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
int nonblock, int cloexec);
/* set a socket to non blocking mode */
-int grpc_set_socket_nonblocking(int fd, int non_blocking);
+grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking);
/* set a socket to close on exec */
-int grpc_set_socket_cloexec(int fd, int close_on_exec);
+grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec);
/* set a socket to reuse old addresses */
-int grpc_set_socket_reuse_addr(int fd, int reuse);
+grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse);
/* disable nagle */
-int grpc_set_socket_low_latency(int fd, int low_latency);
+grpc_error *grpc_set_socket_low_latency(int fd, int low_latency);
+
+/* set SO_REUSEPORT */
+grpc_error *grpc_set_socket_reuse_port(int fd, int reuse);
/* Returns true if this system can create AF_INET6 sockets bound to ::1.
The value is probed once, and cached for the life of the process.
@@ -64,19 +69,22 @@ int grpc_set_socket_low_latency(int fd, int low_latency);
int grpc_ipv6_loopback_available(void);
/* Tries to set SO_NOSIGPIPE if available on this platform.
- Returns 1 on success, 0 on failure.
If SO_NO_SIGPIPE is not available, returns 1. */
-int grpc_set_socket_no_sigpipe_if_possible(int fd);
+grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd);
/* Tries to set IP_PKTINFO if available on this platform.
- Returns 1 on success, 0 on failure.
If IP_PKTINFO is not available, returns 1. */
-int grpc_set_socket_ip_pktinfo_if_possible(int fd);
+grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd);
/* Tries to set IPV6_RECVPKTINFO if available on this platform.
- Returns 1 on success, 0 on failure.
If IPV6_RECVPKTINFO is not available, returns 1. */
-int grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd);
+grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd);
+
+/* Tries to set the socket's send buffer to given size. */
+grpc_error *grpc_set_socket_sndbuf(int fd, int buffer_size_bytes);
+
+/* Tries to set the socket's receive buffer to given size. */
+grpc_error *grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes);
/* An enum to keep track of IPv4/IPv6 socket modes.
@@ -117,7 +125,9 @@ extern int grpc_forbid_dualstack_sockets_for_testing;
IPv4, so that bind() or connect() see the correct family.
Also, it's important to distinguish between DUALSTACK and IPV6 when
listening on the [::] wildcard address. */
-int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
- int protocol, grpc_dualstack_mode *dsmode);
+grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+ int protocol,
+ grpc_dualstack_mode *dsmode,
+ int *newfd);
#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */
diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c
index ebd77e0372..d7d5f6f157 100644
--- a/src/core/lib/iomgr/socket_windows.c
+++ b/src/core/lib/iomgr/socket_windows.c
@@ -42,7 +42,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/iomgr/iocp_windows.h"
@@ -91,10 +91,69 @@ void grpc_winsocket_shutdown(grpc_winsocket *winsocket) {
closesocket(winsocket->socket);
}
-void grpc_winsocket_destroy(grpc_winsocket *winsocket) {
+static void destroy(grpc_winsocket *winsocket) {
grpc_iomgr_unregister_object(&winsocket->iomgr_object);
gpr_mu_destroy(&winsocket->state_mu);
gpr_free(winsocket);
}
+static bool check_destroyable(grpc_winsocket *winsocket) {
+ return winsocket->destroy_called == true &&
+ winsocket->write_info.closure == NULL &&
+ winsocket->read_info.closure == NULL;
+}
+
+void grpc_winsocket_destroy(grpc_winsocket *winsocket) {
+ gpr_mu_lock(&winsocket->state_mu);
+ GPR_ASSERT(!winsocket->destroy_called);
+ winsocket->destroy_called = true;
+ bool should_destroy = check_destroyable(winsocket);
+ gpr_mu_unlock(&winsocket->state_mu);
+ if (should_destroy) destroy(winsocket);
+}
+
+/* Calling notify_on_read or write means either of two things:
+-) The IOCP already completed in the background, and we need to call
+the callback now.
+-) The IOCP hasn't completed yet, and we're queuing it for later. */
+static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx,
+ grpc_winsocket *socket, grpc_closure *closure,
+ grpc_winsocket_callback_info *info) {
+ GPR_ASSERT(info->closure == NULL);
+ gpr_mu_lock(&socket->state_mu);
+ if (info->has_pending_iocp) {
+ info->has_pending_iocp = 0;
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
+ } else {
+ info->closure = closure;
+ }
+ gpr_mu_unlock(&socket->state_mu);
+}
+
+void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx,
+ grpc_winsocket *socket,
+ grpc_closure *closure) {
+ socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info);
+}
+
+void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket,
+ grpc_closure *closure) {
+ socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info);
+}
+
+void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket,
+ grpc_winsocket_callback_info *info) {
+ GPR_ASSERT(!info->has_pending_iocp);
+ gpr_mu_lock(&socket->state_mu);
+ if (info->closure) {
+ grpc_exec_ctx_sched(exec_ctx, info->closure, GRPC_ERROR_NONE, NULL);
+ info->closure = NULL;
+ } else {
+ info->has_pending_iocp = 1;
+ }
+ bool should_destroy = check_destroyable(socket);
+ gpr_mu_unlock(&socket->state_mu);
+ if (should_destroy) destroy(socket);
+}
+
#endif /* GPR_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/socket_windows.h b/src/core/lib/iomgr/socket_windows.h
index 73c4384987..490d0e0a06 100644
--- a/src/core/lib/iomgr/socket_windows.h
+++ b/src/core/lib/iomgr/socket_windows.h
@@ -81,6 +81,7 @@ typedef struct grpc_winsocket_callback_info {
is closer to what happens in posix world. */
typedef struct grpc_winsocket {
SOCKET socket;
+ bool destroy_called;
grpc_winsocket_callback_info write_info;
grpc_winsocket_callback_info read_info;
@@ -108,4 +109,16 @@ void grpc_winsocket_shutdown(grpc_winsocket *socket);
/* Destroy a socket. Should only be called if there's no pending operation. */
void grpc_winsocket_destroy(grpc_winsocket *socket);
+void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx,
+ grpc_winsocket *winsocket,
+ grpc_closure *closure);
+
+void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx,
+ grpc_winsocket *winsocket,
+ grpc_closure *closure);
+
+void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx,
+ grpc_winsocket *winsocket,
+ grpc_winsocket_callback_info *ci);
+
#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index e93d5734a0..80c7a3f128 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -71,33 +71,39 @@ typedef struct {
grpc_closure *closure;
} async_connect;
-static int prepare_socket(const struct sockaddr *addr, int fd) {
- if (fd < 0) {
- goto error;
+static grpc_error *prepare_socket(const struct sockaddr *addr, int fd) {
+ grpc_error *err = GRPC_ERROR_NONE;
+
+ GPR_ASSERT(fd >= 0);
+
+ err = grpc_set_socket_nonblocking(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ err = grpc_set_socket_cloexec(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ if (!grpc_is_unix_socket(addr)) {
+ err = grpc_set_socket_low_latency(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
}
-
- if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
- (!grpc_is_unix_socket(addr) && !grpc_set_socket_low_latency(fd, 1)) ||
- !grpc_set_socket_no_sigpipe_if_possible(fd)) {
- gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
- strerror(errno));
- goto error;
- }
- return 1;
+ err = grpc_set_socket_no_sigpipe_if_possible(fd);
+ if (err != GRPC_ERROR_NONE) goto error;
+ goto done;
error:
if (fd >= 0) {
close(fd);
}
- return 0;
+done:
+ return err;
}
-static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
+static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
int done;
async_connect *ac = acp;
if (grpc_tcp_trace) {
- gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: success=%d", ac->addr_str,
- success);
+ const char *str = grpc_error_string(error);
+ gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
+ str);
+ grpc_error_free_string(str);
}
gpr_mu_lock(&ac->mu);
if (ac->fd != NULL) {
@@ -112,7 +118,7 @@ static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
}
}
-static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
+static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
async_connect *ac = acp;
int so_error = 0;
socklen_t so_error_size;
@@ -122,9 +128,13 @@ static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
grpc_closure *closure = ac->closure;
grpc_fd *fd;
+ GRPC_ERROR_REF(error);
+
if (grpc_tcp_trace) {
- gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: success=%d",
- ac->addr_str, success);
+ const char *str = grpc_error_string(error);
+ gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s",
+ ac->addr_str, str);
+ grpc_error_free_string(str);
}
gpr_mu_lock(&ac->mu);
@@ -136,15 +146,14 @@ static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
grpc_timer_cancel(exec_ctx, &ac->alarm);
gpr_mu_lock(&ac->mu);
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
do {
so_error_size = sizeof(so_error);
err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error,
&so_error_size);
} while (err < 0 && errno == EINTR);
if (err < 0) {
- gpr_log(GPR_ERROR, "failed to connect to '%s': getsockopt(ERROR): %s",
- ac->addr_str, strerror(errno));
+ error = GRPC_OS_ERROR(errno, "getsockopt");
goto finish;
} else if (so_error != 0) {
if (so_error == ENOBUFS) {
@@ -169,14 +178,12 @@ static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
} else {
switch (so_error) {
case ECONNREFUSED:
- gpr_log(
- GPR_ERROR,
- "failed to connect to '%s': socket error: connection refused",
- ac->addr_str);
+ error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, errno);
+ error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
+ "Connection refused");
break;
default:
- gpr_log(GPR_ERROR, "failed to connect to '%s': socket error: %d",
- ac->addr_str, so_error);
+ error = GRPC_OS_ERROR(errno, "getsockopt(SO_ERROR)");
break;
}
goto finish;
@@ -188,8 +195,8 @@ static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
goto finish;
}
} else {
- gpr_log(GPR_ERROR, "failed to connect to '%s': timeout occurred",
- ac->addr_str);
+ error =
+ grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, "Timeout occurred");
goto finish;
}
@@ -203,12 +210,18 @@ finish:
}
done = (--ac->refs == 0);
gpr_mu_unlock(&ac->mu);
+ if (error != GRPC_ERROR_NONE) {
+ error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION,
+ "Failed to connect to remote host");
+ error =
+ grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, ac->addr_str);
+ }
if (done) {
gpr_mu_destroy(&ac->mu);
gpr_free(ac->addr_str);
gpr_free(ac);
}
- grpc_exec_ctx_enqueue(exec_ctx, closure, *ep != NULL, NULL);
+ grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
}
static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
@@ -225,6 +238,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
grpc_fd *fdobj;
char *name;
char *addr_str;
+ grpc_error *error;
*ep = NULL;
@@ -234,9 +248,10 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
addr_len = sizeof(addr6_v4mapped);
}
- fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
- if (fd < 0) {
- gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+ error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+ if (error != GRPC_ERROR_NONE) {
+ grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
+ return;
}
if (dsmode == GRPC_DSMODE_IPV4) {
/* If we got an AF_INET socket, map the address back to IPv4. */
@@ -244,8 +259,8 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
addr = (struct sockaddr *)&addr4_copy;
addr_len = sizeof(addr4_copy);
}
- if (!prepare_socket(addr, fd)) {
- grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+ if ((error = prepare_socket(addr, fd)) != GRPC_ERROR_NONE) {
+ grpc_exec_ctx_sched(exec_ctx, closure, error, NULL);
return;
}
@@ -261,14 +276,14 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
if (err >= 0) {
*ep = grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str);
- grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
goto done;
}
if (errno != EWOULDBLOCK && errno != EINPROGRESS) {
- gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno));
grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, "tcp_client_connect_error");
- grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect"),
+ NULL);
goto done;
}
diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
index 66f9ff7a46..562cb9c6bf 100644
--- a/src/core/lib/iomgr/tcp_client_windows.c
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -35,11 +35,11 @@
#ifdef GPR_WINSOCK_SOCKET
-#include "src/core/lib/iomgr/sockaddr_win32.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/useful.h>
@@ -75,23 +75,25 @@ static void async_connect_unlock_and_cleanup(async_connect *ac,
if (socket != NULL) grpc_winsocket_destroy(socket);
}
-static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, bool occured) {
+static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
async_connect *ac = acp;
gpr_mu_lock(&ac->mu);
- if (ac->socket != NULL) {
- grpc_winsocket_shutdown(ac->socket);
+ grpc_winsocket *socket = ac->socket;
+ ac->socket = NULL;
+ if (socket != NULL) {
+ grpc_winsocket_shutdown(socket);
}
- async_connect_unlock_and_cleanup(ac, ac->socket);
+ async_connect_unlock_and_cleanup(ac, socket);
}
-static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, bool from_iocp) {
+static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
async_connect *ac = acp;
- SOCKET sock = ac->socket->socket;
grpc_endpoint **ep = ac->endpoint;
GPR_ASSERT(*ep == NULL);
- grpc_winsocket_callback_info *info = &ac->socket->write_info;
grpc_closure *on_done = ac->on_done;
+ GRPC_ERROR_REF(error);
+
gpr_mu_lock(&ac->mu);
grpc_winsocket *socket = ac->socket;
ac->socket = NULL;
@@ -101,17 +103,15 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, bool from_iocp) {
gpr_mu_lock(&ac->mu);
- if (from_iocp && socket != NULL) {
+ if (error == GRPC_ERROR_NONE && socket != NULL) {
DWORD transfered_bytes = 0;
DWORD flags;
- BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
- &transfered_bytes, FALSE, &flags);
+ BOOL wsa_success =
+ WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped,
+ &transfered_bytes, FALSE, &flags);
GPR_ASSERT(transfered_bytes == 0);
if (!wsa_success) {
- char *utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, "on_connect error connecting to '%s': %s",
- ac->addr_name, utf8_message);
- gpr_free(utf8_message);
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx");
} else {
*ep = grpc_tcp_create(socket, ac->addr_name);
socket = NULL;
@@ -121,7 +121,7 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, bool from_iocp) {
async_connect_unlock_and_cleanup(ac, socket);
/* If the connection was aborted, the callback was already called when
the deadline was met. */
- on_done->cb(exec_ctx, on_done->cb_arg, *ep != NULL);
+ grpc_exec_ctx_sched(exec_ctx, on_done, error, NULL);
}
/* Tries to issue one async connection, then schedules both an IOCP
@@ -141,10 +141,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
LPFN_CONNECTEX ConnectEx;
GUID guid = WSAID_CONNECTEX;
DWORD ioctl_num_bytes;
- const char *message = NULL;
- char *utf8_message;
grpc_winsocket_callback_info *info;
- int last_error;
+ grpc_error *error = GRPC_ERROR_NONE;
*endpoint = NULL;
@@ -157,12 +155,12 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
- message = "Unable to create socket: %s";
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
goto failure;
}
- if (!grpc_tcp_prepare_socket(sock)) {
- message = "Unable to set socket options: %s";
+ error = grpc_tcp_prepare_socket(sock);
+ if (error != GRPC_ERROR_NONE) {
goto failure;
}
@@ -173,7 +171,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
&ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
if (status != 0) {
- message = "Unable to retrieve ConnectEx pointer: %s";
+ error = GRPC_WSA_ERROR(WSAGetLastError(),
+ "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)");
goto failure;
}
@@ -181,7 +180,7 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address));
if (status != 0) {
- message = "Unable to bind socket: %s";
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
goto failure;
}
@@ -193,9 +192,9 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
/* It wouldn't be unusual to get a success immediately. But we'll still get
an IOCP notification, so let's ignore it. */
if (!success) {
- int error = WSAGetLastError();
- if (error != ERROR_IO_PENDING) {
- message = "ConnectEx failed: %s";
+ int last_error = WSAGetLastError();
+ if (last_error != ERROR_IO_PENDING) {
+ error = GRPC_WSA_ERROR(last_error, "ConnectEx");
goto failure;
}
}
@@ -215,17 +214,18 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
return;
failure:
- last_error = WSAGetLastError();
- utf8_message = gpr_format_message(last_error);
- gpr_log(GPR_ERROR, message, utf8_message);
- gpr_log(GPR_ERROR, "last error = %d", last_error);
- gpr_free(utf8_message);
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
+ char *target_uri = grpc_sockaddr_to_uri(addr);
+ grpc_error *final_error = grpc_error_set_str(
+ GRPC_ERROR_CREATE_REFERENCING("Failed to connect", &error, 1),
+ GRPC_ERROR_STR_TARGET_ADDRESS, target_uri);
+ GRPC_ERROR_UNREF(error);
if (socket != NULL) {
grpc_winsocket_destroy(socket);
} else if (sock != INVALID_SOCKET) {
closesocket(sock);
}
- grpc_exec_ctx_enqueue(exec_ctx, on_done, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, on_done, final_error, NULL);
}
#endif /* GPR_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index e2869224f1..2ab45e33ce 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -35,9 +35,11 @@
#ifdef GPR_POSIX_SOCKET
+#include "src/core/lib/iomgr/network_status_tracker.h"
#include "src/core/lib/iomgr/tcp_posix.h"
#include <errno.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -74,7 +76,7 @@ typedef struct {
grpc_endpoint base;
grpc_fd *em_fd;
int fd;
- int finished_edge;
+ bool finished_edge;
msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */
size_t slice_size;
gpr_refcount refcount;
@@ -101,9 +103,9 @@ typedef struct {
} grpc_tcp;
static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
- bool success);
+ grpc_error *error);
static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
- bool success);
+ grpc_error *error);
static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *)ep;
@@ -151,16 +153,20 @@ static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
#endif
static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
+ grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
TCP_UNREF(exec_ctx, tcp, "destroy");
}
-static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, int success) {
+static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp,
+ grpc_error *error) {
grpc_closure *cb = tcp->read_cb;
if (grpc_tcp_trace) {
size_t i;
- gpr_log(GPR_DEBUG, "read: success=%d", success);
+ const char *str = grpc_error_string(error);
+ gpr_log(GPR_DEBUG, "read: error=%s", str);
+ grpc_error_free_string(str);
for (i = 0; i < tcp->incoming_buffer->count; i++) {
char *dump = gpr_dump_slice(tcp->incoming_buffer->slices[i],
GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -171,7 +177,7 @@ static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, int success) {
tcp->read_cb = NULL;
tcp->incoming_buffer = NULL;
- cb->cb(exec_ctx, cb->cb_arg, success);
+ grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
}
#define MAX_READ_IOVEC 4
@@ -219,15 +225,14 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
/* We've consumed the edge, request a new one */
grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
} else {
- /* TODO(klempner): Log interesting errors */
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
- call_read_cb(exec_ctx, tcp, 0);
+ call_read_cb(exec_ctx, tcp, GRPC_OS_ERROR(errno, "recvmsg"));
TCP_UNREF(exec_ctx, tcp, "read");
}
} else if (read_bytes == 0) {
/* 0 read size ==> end of stream */
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
- call_read_cb(exec_ctx, tcp, 0);
+ call_read_cb(exec_ctx, tcp, GRPC_ERROR_CREATE("EOF"));
TCP_UNREF(exec_ctx, tcp, "read");
} else {
GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length);
@@ -240,7 +245,7 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
++tcp->iov_size;
}
GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length);
- call_read_cb(exec_ctx, tcp, 1);
+ call_read_cb(exec_ctx, tcp, GRPC_ERROR_NONE);
TCP_UNREF(exec_ctx, tcp, "read");
}
@@ -248,13 +253,13 @@ static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) {
}
static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
- bool success) {
+ grpc_error *error) {
grpc_tcp *tcp = (grpc_tcp *)arg;
GPR_ASSERT(!tcp->finished_edge);
- if (!success) {
+ if (error != GRPC_ERROR_NONE) {
gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
- call_read_cb(exec_ctx, tcp, 0);
+ call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
TCP_UNREF(exec_ctx, tcp, "read");
} else {
tcp_continue_read(exec_ctx, tcp);
@@ -271,17 +276,16 @@ static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
gpr_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer);
TCP_REF(tcp, "read");
if (tcp->finished_edge) {
- tcp->finished_edge = 0;
+ tcp->finished_edge = false;
grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
} else {
- grpc_exec_ctx_enqueue(exec_ctx, &tcp->read_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &tcp->read_closure, GRPC_ERROR_NONE, NULL);
}
}
-typedef enum { FLUSH_DONE, FLUSH_PENDING, FLUSH_ERROR } flush_result;
-
+/* returns true if done, false if pending; if returning true, *error is set */
#define MAX_WRITE_IOVEC 16
-static flush_result tcp_flush(grpc_tcp *tcp) {
+static bool tcp_flush(grpc_tcp *tcp, grpc_error **error) {
struct msghdr msg;
struct iovec iov[MAX_WRITE_IOVEC];
msg_iovlen_type iov_size;
@@ -331,10 +335,10 @@ static flush_result tcp_flush(grpc_tcp *tcp) {
if (errno == EAGAIN) {
tcp->outgoing_slice_idx = unwind_slice_idx;
tcp->outgoing_byte_idx = unwind_byte_idx;
- return FLUSH_PENDING;
+ return false;
} else {
- /* TODO(klempner): Log some of these */
- return FLUSH_ERROR;
+ *error = GRPC_OS_ERROR(errno, "sendmsg");
+ return true;
}
}
@@ -355,42 +359,42 @@ static flush_result tcp_flush(grpc_tcp *tcp) {
}
if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) {
- return FLUSH_DONE;
+ *error = GRPC_ERROR_NONE;
+ return true;
}
};
}
static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
- bool success) {
+ grpc_error *error) {
grpc_tcp *tcp = (grpc_tcp *)arg;
- flush_result status;
grpc_closure *cb;
- if (!success) {
+ if (error != GRPC_ERROR_NONE) {
cb = tcp->write_cb;
tcp->write_cb = NULL;
- cb->cb(exec_ctx, cb->cb_arg, 0);
+ cb->cb(exec_ctx, cb->cb_arg, error);
TCP_UNREF(exec_ctx, tcp, "write");
return;
}
- status = tcp_flush(tcp);
- if (status == FLUSH_PENDING) {
+ if (!tcp_flush(tcp, &error)) {
grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
} else {
cb = tcp->write_cb;
tcp->write_cb = NULL;
GPR_TIMER_BEGIN("tcp_handle_write.cb", 0);
- cb->cb(exec_ctx, cb->cb_arg, status == FLUSH_DONE);
+ cb->cb(exec_ctx, cb->cb_arg, error);
GPR_TIMER_END("tcp_handle_write.cb", 0);
TCP_UNREF(exec_ctx, tcp, "write");
+ GRPC_ERROR_UNREF(error);
}
}
static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
gpr_slice_buffer *buf, grpc_closure *cb) {
grpc_tcp *tcp = (grpc_tcp *)ep;
- flush_result status;
+ grpc_error *error = GRPC_ERROR_NONE;
if (grpc_tcp_trace) {
size_t i;
@@ -408,20 +412,22 @@ static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
if (buf->length == 0) {
GPR_TIMER_END("tcp_write", 0);
- grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, cb, grpc_fd_is_shutdown(tcp->em_fd)
+ ? GRPC_ERROR_CREATE("EOF")
+ : GRPC_ERROR_NONE,
+ NULL);
return;
}
tcp->outgoing_buffer = buf;
tcp->outgoing_slice_idx = 0;
tcp->outgoing_byte_idx = 0;
- status = tcp_flush(tcp);
- if (status == FLUSH_PENDING) {
+ if (!tcp_flush(tcp, &error)) {
TCP_REF(tcp, "write");
tcp->write_cb = cb;
grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
} else {
- grpc_exec_ctx_enqueue(exec_ctx, cb, status == FLUSH_DONE, NULL);
+ grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
}
GPR_TIMER_END("tcp_write", 0);
@@ -461,7 +467,7 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
tcp->incoming_buffer = NULL;
tcp->slice_size = slice_size;
tcp->iov_size = 1;
- tcp->finished_edge = 1;
+ tcp->finished_edge = true;
/* paired with unref in grpc_tcp_destroy */
gpr_ref_init(&tcp->refcount, 1);
tcp->em_fd = em_fd;
@@ -470,6 +476,8 @@ grpc_endpoint *grpc_tcp_create(grpc_fd *em_fd, size_t slice_size,
tcp->write_closure.cb = tcp_handle_write;
tcp->write_closure.cb_arg = tcp;
gpr_slice_buffer_init(&tcp->last_read_buffer);
+ /* Tell network status tracker about new endpoint */
+ grpc_network_status_register_endpoint(&tcp->base);
return &tcp->base;
}
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
index fee14ae661..875a6ca14a 100644
--- a/src/core/lib/iomgr/tcp_server.h
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -34,6 +34,8 @@
#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
+#include <grpc/grpc.h>
+
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/endpoint.h"
@@ -58,7 +60,9 @@ typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg,
/* Create a server, initially not bound to any ports. The caller owns one ref.
If shutdown_complete is not NULL, it will be used by
grpc_tcp_server_unref() when the ref count reaches zero. */
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete);
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
+ grpc_tcp_server **server);
/* Start listening to bound ports */
void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server,
@@ -74,8 +78,8 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server,
but not dualstack sockets. */
/* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
all of the multiple socket port matching logic in one place */
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
- size_t addr_len);
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+ size_t addr_len, int *out_port);
/* Number of fds at the given port_index, or 0 if port_index is out of
bounds. */
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
index c695621de8..d3803c3bd0 100644
--- a/src/core/lib/iomgr/tcp_server_posix.c
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -59,6 +59,8 @@
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+
#include "src/core/lib/iomgr/resolve_address.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "src/core/lib/iomgr/socket_utils_posix.h"
@@ -110,8 +112,10 @@ struct grpc_tcp_server {
/* destroyed port count: how many ports are completely destroyed */
size_t destroyed_ports;
- /* is this server shutting down? (boolean) */
- int shutdown;
+ /* is this server shutting down? */
+ bool shutdown;
+ /* use SO_REUSEPORT */
+ bool so_reuseport;
/* linked list of server ports */
grpc_tcp_listener *head;
@@ -128,15 +132,47 @@ struct grpc_tcp_server {
grpc_pollset **pollsets;
/* number of pollsets in the pollsets array */
size_t pollset_count;
+
+ /* next pollset to assign a channel to */
+ gpr_atm next_pollset_to_assign;
};
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
+static gpr_once check_init = GPR_ONCE_INIT;
+static bool has_so_reuseport;
+
+static void init(void) {
+ int s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s >= 0) {
+ has_so_reuseport = GRPC_LOG_IF_ERROR("check for SO_REUSEPORT",
+ grpc_set_socket_reuse_port(s, 1));
+ close(s);
+ }
+}
+
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
+ grpc_tcp_server **server) {
+ gpr_once_init(&check_init, init);
+
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
+ s->so_reuseport = has_so_reuseport;
+ for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) {
+ if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) {
+ if (args->args[i].type == GRPC_ARG_INTEGER) {
+ s->so_reuseport =
+ has_so_reuseport && (args->args[i].value.integer != 0);
+ } else {
+ gpr_free(s);
+ return GRPC_ERROR_CREATE(GRPC_ARG_ALLOW_REUSEPORT
+ " must be an integer");
+ }
+ }
+ }
gpr_ref_init(&s->refs, 1);
gpr_mu_init(&s->mu);
s->active_ports = 0;
s->destroyed_ports = 0;
- s->shutdown = 0;
+ s->shutdown = false;
s->shutdown_starting.head = NULL;
s->shutdown_starting.tail = NULL;
s->shutdown_complete = shutdown_complete;
@@ -145,12 +181,14 @@ grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
s->head = NULL;
s->tail = NULL;
s->nports = 0;
- return s;
+ gpr_atm_no_barrier_store(&s->next_pollset_to_assign, 0);
+ *server = s;
+ return GRPC_ERROR_NONE;
}
static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
if (s->shutdown_complete != NULL) {
- grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
}
gpr_mu_destroy(&s->mu);
@@ -165,7 +203,7 @@ static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
}
static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server,
- bool success) {
+ grpc_error *error) {
grpc_tcp_server *s = server;
gpr_mu_lock(&s->mu);
s->destroyed_ports++;
@@ -210,7 +248,7 @@ static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
gpr_mu_lock(&s->mu);
GPR_ASSERT(!s->shutdown);
- s->shutdown = 1;
+ s->shutdown = true;
/* shutdown all fd's */
if (s->active_ports) {
@@ -259,65 +297,81 @@ static int get_max_accept_queue_size(void) {
}
/* Prepare a recently-created socket for listening. */
-static int prepare_socket(int fd, const struct sockaddr *addr,
- size_t addr_len) {
+static grpc_error *prepare_socket(int fd, const struct sockaddr *addr,
+ size_t addr_len, bool so_reuseport,
+ int *port) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
+ grpc_error *err = GRPC_ERROR_NONE;
- if (fd < 0) {
- goto error;
+ GPR_ASSERT(fd >= 0);
+
+ if (so_reuseport) {
+ err = grpc_set_socket_reuse_port(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
}
- if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
- (!grpc_is_unix_socket(addr) && (!grpc_set_socket_low_latency(fd, 1) ||
- !grpc_set_socket_reuse_addr(fd, 1))) ||
- !grpc_set_socket_no_sigpipe_if_possible(fd)) {
- gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
- strerror(errno));
- goto error;
+ err = grpc_set_socket_nonblocking(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ err = grpc_set_socket_cloexec(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ if (!grpc_is_unix_socket(addr)) {
+ err = grpc_set_socket_low_latency(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
+ err = grpc_set_socket_reuse_addr(fd, 1);
+ if (err != GRPC_ERROR_NONE) goto error;
}
+ err = grpc_set_socket_no_sigpipe_if_possible(fd);
+ if (err != GRPC_ERROR_NONE) goto error;
GPR_ASSERT(addr_len < ~(socklen_t)0);
if (bind(fd, addr, (socklen_t)addr_len) < 0) {
- char *addr_str;
- grpc_sockaddr_to_string(&addr_str, addr, 0);
- gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
- gpr_free(addr_str);
+ err = GRPC_OS_ERROR(errno, "bind");
goto error;
}
if (listen(fd, get_max_accept_queue_size()) < 0) {
- gpr_log(GPR_ERROR, "listen: %s", strerror(errno));
+ err = GRPC_OS_ERROR(errno, "listen");
goto error;
}
sockname_len = sizeof(sockname_temp);
if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) {
+ err = GRPC_OS_ERROR(errno, "getsockname");
goto error;
}
- return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+ *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+ return GRPC_ERROR_NONE;
error:
+ GPR_ASSERT(err != GRPC_ERROR_NONE);
if (fd >= 0) {
close(fd);
}
- return -1;
+ grpc_error *ret = grpc_error_set_int(
+ GRPC_ERROR_CREATE_REFERENCING("Unable to configure socket", &err, 1),
+ GRPC_ERROR_INT_FD, fd);
+ GRPC_ERROR_UNREF(err);
+ return ret;
}
/* event manager callback when reads are ready */
-static void on_read(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
grpc_tcp_listener *sp = arg;
grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index,
sp->fd_index};
grpc_pollset *read_notifier_pollset = NULL;
grpc_fd *fdobj;
- if (!success) {
+ if (err != GRPC_ERROR_NONE) {
goto error;
}
- read_notifier_pollset = grpc_fd_get_read_notifier_pollset(exec_ctx, sp->emfd);
+ read_notifier_pollset =
+ sp->server->pollsets[(size_t)gpr_atm_no_barrier_fetch_add(
+ &sp->server->next_pollset_to_assign, 1) %
+ sp->server->pollset_count];
/* loop until accept4 returns EAGAIN, and then re-arm notification */
for (;;) {
@@ -380,18 +434,19 @@ error:
}
}
-static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
- const struct sockaddr *addr,
- size_t addr_len,
- unsigned port_index,
- unsigned fd_index) {
+static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd,
+ const struct sockaddr *addr,
+ size_t addr_len, unsigned port_index,
+ unsigned fd_index,
+ grpc_tcp_listener **listener) {
grpc_tcp_listener *sp = NULL;
- int port;
+ int port = -1;
char *addr_str;
char *name;
- port = prepare_socket(fd, addr, addr_len);
- if (port >= 0) {
+ grpc_error *err = prepare_socket(fd, addr, addr_len, s->so_reuseport, &port);
+ if (err == GRPC_ERROR_NONE) {
+ GPR_ASSERT(port > 0);
grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
gpr_mu_lock(&s->mu);
@@ -421,11 +476,58 @@ static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
gpr_free(name);
}
- return sp;
+ *listener = sp;
+ return err;
+}
+
+static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) {
+ grpc_tcp_listener *sp = NULL;
+ char *addr_str;
+ char *name;
+ grpc_error *err;
+
+ for (grpc_tcp_listener *l = listener->next; l && l->is_sibling; l = l->next) {
+ l->fd_index += count;
+ }
+
+ for (unsigned i = 0; i < count; i++) {
+ int fd, port;
+ grpc_dualstack_mode dsmode;
+ err = grpc_create_dualstack_socket(&listener->addr.sockaddr, SOCK_STREAM, 0,
+ &dsmode, &fd);
+ if (err != GRPC_ERROR_NONE) return err;
+ err = prepare_socket(fd, &listener->addr.sockaddr, listener->addr_len, true,
+ &port);
+ if (err != GRPC_ERROR_NONE) return err;
+ listener->server->nports++;
+ grpc_sockaddr_to_string(&addr_str, &listener->addr.sockaddr, 1);
+ gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i);
+ sp = gpr_malloc(sizeof(grpc_tcp_listener));
+ sp->next = listener->next;
+ listener->next = sp;
+ sp->server = listener->server;
+ sp->fd = fd;
+ sp->emfd = grpc_fd_create(fd, name);
+ memcpy(sp->addr.untyped, listener->addr.untyped, listener->addr_len);
+ sp->addr_len = listener->addr_len;
+ sp->port = port;
+ sp->port_index = listener->port_index;
+ sp->fd_index = listener->fd_index + count - i;
+ sp->is_sibling = 1;
+ sp->sibling = listener->is_sibling ? listener->sibling : listener;
+ GPR_ASSERT(sp->emfd);
+ while (listener->server->tail->next != NULL) {
+ listener->server->tail = listener->server->tail->next;
+ }
+ gpr_free(addr_str);
+ gpr_free(name);
+ }
+
+ return GRPC_ERROR_NONE;
}
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
- size_t addr_len) {
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+ size_t addr_len, int *out_port) {
grpc_tcp_listener *sp;
grpc_tcp_listener *sp2 = NULL;
int fd;
@@ -440,6 +542,7 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
int port;
unsigned port_index = 0;
unsigned fd_index = 0;
+ grpc_error *errs[2] = {GRPC_ERROR_NONE, GRPC_ERROR_NONE};
if (s->tail != NULL) {
port_index = s->tail->port_index + 1;
}
@@ -478,33 +581,35 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
/* Try listening on IPv6 first. */
addr = (struct sockaddr *)&wild6;
addr_len = sizeof(wild6);
- fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
- sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index);
- if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
- goto done;
- }
- if (sp != NULL) {
- ++fd_index;
- }
- /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
- if (port == 0 && sp != NULL) {
- grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port);
+ errs[0] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+ if (errs[0] == GRPC_ERROR_NONE) {
+ errs[0] = add_socket_to_server(s, fd, addr, addr_len, port_index,
+ fd_index, &sp);
+ if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
+ goto done;
+ }
+ if (sp != NULL) {
+ ++fd_index;
+ }
+ /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
+ if (port == 0 && sp != NULL) {
+ grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port);
+ }
}
addr = (struct sockaddr *)&wild4;
addr_len = sizeof(wild4);
}
- fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
- if (fd < 0) {
- gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
- } else {
+ errs[1] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+ if (errs[1] == GRPC_ERROR_NONE) {
if (dsmode == GRPC_DSMODE_IPV4 &&
grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
addr = (struct sockaddr *)&addr4_copy;
addr_len = sizeof(addr4_copy);
}
sp2 = sp;
- sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index);
+ errs[1] =
+ add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index, &sp);
if (sp2 != NULL && sp != NULL) {
sp2->sibling = sp;
sp->is_sibling = 1;
@@ -514,9 +619,21 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
done:
gpr_free(allocated_addr);
if (sp != NULL) {
- return sp->port;
+ *out_port = sp->port;
+ GRPC_ERROR_UNREF(errs[0]);
+ GRPC_ERROR_UNREF(errs[1]);
+ return GRPC_ERROR_NONE;
} else {
- return -1;
+ *out_port = -1;
+ char *addr_str = grpc_sockaddr_to_uri(addr);
+ grpc_error *err = grpc_error_set_str(
+ GRPC_ERROR_CREATE_REFERENCING("Failed to add port to server", errs,
+ GPR_ARRAY_SIZE(errs)),
+ GRPC_ERROR_STR_TARGET_ADDRESS, addr_str);
+ GRPC_ERROR_UNREF(errs[0]);
+ GRPC_ERROR_UNREF(errs[1]);
+ gpr_free(addr_str);
+ return err;
}
}
@@ -565,14 +682,29 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
s->on_accept_cb_arg = on_accept_cb_arg;
s->pollsets = pollsets;
s->pollset_count = pollset_count;
- for (sp = s->head; sp; sp = sp->next) {
- for (i = 0; i < pollset_count; i++) {
- grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ sp = s->head;
+ while (sp != NULL) {
+ if (s->so_reuseport && pollset_count > 1) {
+ GPR_ASSERT(GRPC_LOG_IF_ERROR(
+ "clone_port", clone_port(sp, (unsigned)(pollset_count - 1))));
+ for (i = 0; i < pollset_count; i++) {
+ grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ sp->read_closure.cb = on_read;
+ sp->read_closure.cb_arg = sp;
+ grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
+ s->active_ports++;
+ sp = sp->next;
+ }
+ } else {
+ for (i = 0; i < pollset_count; i++) {
+ grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
+ }
+ sp->read_closure.cb = on_read;
+ sp->read_closure.cb_arg = sp;
+ grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
+ s->active_ports++;
+ sp = sp->next;
}
- sp->read_closure.cb = on_read;
- sp->read_closure.cb_arg = sp;
- grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
- s->active_ports++;
}
gpr_mu_unlock(&s->mu);
}
@@ -585,7 +717,8 @@ grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) {
void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
grpc_closure *shutdown_starting) {
gpr_mu_lock(&s->mu);
- grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1);
+ grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+ GRPC_ERROR_NONE);
gpr_mu_unlock(&s->mu);
}
diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
index e15f8b0cdf..7b0966704c 100644
--- a/src/core/lib/iomgr/tcp_server_windows.c
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -41,7 +41,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -102,7 +102,9 @@ struct grpc_tcp_server {
/* Public function. Allocates the proper data structures to hold a
grpc_tcp_server. */
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+ const grpc_channel_args *args,
+ grpc_tcp_server **server) {
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
gpr_ref_init(&s->refs, 1);
gpr_mu_init(&s->mu);
@@ -114,12 +116,13 @@ grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
s->shutdown_starting.head = NULL;
s->shutdown_starting.tail = NULL;
s->shutdown_complete = shutdown_complete;
- return s;
+ *server = s;
+ return GRPC_ERROR_NONE;
}
static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
if (s->shutdown_complete != NULL) {
- grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
}
/* Now that the accepts have been aborted, we can destroy the sockets.
@@ -143,7 +146,8 @@ grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) {
void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
grpc_closure *shutdown_starting) {
gpr_mu_lock(&s->mu);
- grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1);
+ grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+ GRPC_ERROR_NONE);
gpr_mu_unlock(&s->mu);
}
@@ -187,51 +191,49 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
}
/* Prepare (bind) a recently-created socket for listening. */
-static int prepare_socket(SOCKET sock, const struct sockaddr *addr,
- size_t addr_len) {
+static grpc_error *prepare_socket(SOCKET sock, const struct sockaddr *addr,
+ size_t addr_len, int *port) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
+ grpc_error *error = GRPC_ERROR_NONE;
- if (sock == INVALID_SOCKET) goto error;
-
- if (!grpc_tcp_prepare_socket(sock)) {
- char *utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message);
- gpr_free(utf8_message);
- goto error;
+ error = grpc_tcp_prepare_socket(sock);
+ if (error != GRPC_ERROR_NONE) {
+ goto failure;
}
if (bind(sock, addr, (int)addr_len) == SOCKET_ERROR) {
- char *addr_str;
- char *utf8_message = gpr_format_message(WSAGetLastError());
- grpc_sockaddr_to_string(&addr_str, addr, 0);
- gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message);
- gpr_free(utf8_message);
- gpr_free(addr_str);
- goto error;
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
+ goto failure;
}
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
- char *utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, "listen: %s", utf8_message);
- gpr_free(utf8_message);
- goto error;
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "listen");
+ goto failure;
}
sockname_len = sizeof(sockname_temp);
if (getsockname(sock, (struct sockaddr *)&sockname_temp, &sockname_len) ==
SOCKET_ERROR) {
- char *utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, "getsockname: %s", utf8_message);
- gpr_free(utf8_message);
- goto error;
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname");
+ goto failure;
}
- return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+ *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+ return GRPC_ERROR_NONE;
-error:
+failure:
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
+ char *tgtaddr = grpc_sockaddr_to_uri(addr);
+ grpc_error *final_error = grpc_error_set_int(
+ grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING(
+ "Failed to prepare server socket", &error, 1),
+ GRPC_ERROR_STR_TARGET_ADDRESS, tgtaddr),
+ GRPC_ERROR_INT_FD, (intptr_t)sock);
+ gpr_free(tgtaddr);
+ GRPC_ERROR_UNREF(error);
if (sock != INVALID_SOCKET) closesocket(sock);
- return -1;
+ return error;
}
static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
@@ -251,26 +253,23 @@ static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
/* In order to do an async accept, we need to create a socket first which
will be the one assigned to the new incoming connection. */
-static void start_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *port) {
+static grpc_error *start_accept(grpc_exec_ctx *exec_ctx,
+ grpc_tcp_listener *port) {
SOCKET sock = INVALID_SOCKET;
- char *message;
- char *utf8_message;
BOOL success;
DWORD addrlen = sizeof(struct sockaddr_in6) + 16;
DWORD bytes_received = 0;
+ grpc_error *error = GRPC_ERROR_NONE;
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
-
if (sock == INVALID_SOCKET) {
- message = "Unable to create socket: %s";
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
goto failure;
}
- if (!grpc_tcp_prepare_socket(sock)) {
- message = "Unable to prepare socket: %s";
- goto failure;
- }
+ error = grpc_tcp_prepare_socket(sock);
+ if (error != GRPC_ERROR_NONE) goto failure;
/* Start the "accept" asynchronously. */
success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0,
@@ -280,9 +279,9 @@ static void start_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *port) {
/* It is possible to get an accept immediately without delay. However, we
will still get an IOCP notification for it. So let's just ignore it. */
if (!success) {
- int error = WSAGetLastError();
- if (error != ERROR_IO_PENDING) {
- message = "AcceptEx failed: %s";
+ int last_error = WSAGetLastError();
+ if (last_error != ERROR_IO_PENDING) {
+ error = GRPC_WSA_ERROR(last_error, "AcceptEx");
goto failure;
}
}
@@ -291,9 +290,10 @@ static void start_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *port) {
immediately process an accept that happened in the meantime. */
port->new_socket = sock;
grpc_socket_notify_on_read(exec_ctx, port->socket, &port->on_accept);
- return;
+ return error;
failure:
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
if (port->shutting_down) {
/* We are abandoning the listener port, take that into account to prevent
occasional hangs on shutdown. The hang happens when sp->shutting_down
@@ -301,16 +301,15 @@ failure:
but we fail there because the listening port has been closed in the
meantime. */
decrement_active_ports_and_notify(exec_ctx, port);
- return;
+ GRPC_ERROR_UNREF(error);
+ return GRPC_ERROR_NONE;
}
- utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, message, utf8_message);
- gpr_free(utf8_message);
if (sock != INVALID_SOCKET) closesocket(sock);
+ return error;
}
/* Event manager callback when reads are ready. */
-static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, bool from_iocp) {
+static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_tcp_listener *sp = arg;
grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0};
SOCKET sock = sp->new_socket;
@@ -328,7 +327,10 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, bool from_iocp) {
/* The general mechanism for shutting down is to queue abortion calls. While
this is necessary in the read/write case, it's useless for the accept
case. We only need to adjust the pending callback count */
- if (!from_iocp) {
+ if (error != GRPC_ERROR_NONE) {
+ const char *msg = grpc_error_string(error);
+ gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg);
+ grpc_error_free_string(msg);
return;
}
@@ -387,21 +389,20 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, bool from_iocp) {
the former socked we created has now either been destroy or assigned
to the new connection. We need to create a new one for the next
connection. */
- start_accept(exec_ctx, sp);
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept(exec_ctx, sp)));
}
-static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
- const struct sockaddr *addr,
- size_t addr_len,
- unsigned port_index) {
+static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
+ const struct sockaddr *addr,
+ size_t addr_len, unsigned port_index,
+ grpc_tcp_listener **listener) {
grpc_tcp_listener *sp = NULL;
- int port;
+ int port = -1;
int status;
GUID guid = WSAID_ACCEPTEX;
DWORD ioctl_num_bytes;
LPFN_ACCEPTEX AcceptEx;
-
- if (sock == INVALID_SOCKET) return NULL;
+ grpc_error *error = GRPC_ERROR_NONE;
/* We need to grab the AcceptEx pointer for that port, as it may be
interface-dependent. We'll cache it to avoid doing that again. */
@@ -417,44 +418,49 @@ static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
return NULL;
}
- port = prepare_socket(sock, addr, addr_len);
- if (port >= 0) {
- gpr_mu_lock(&s->mu);
- GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
- sp = gpr_malloc(sizeof(grpc_tcp_listener));
- sp->next = NULL;
- if (s->head == NULL) {
- s->head = sp;
- } else {
- s->tail->next = sp;
- }
- s->tail = sp;
- sp->server = s;
- sp->socket = grpc_winsocket_create(sock, "listener");
- sp->shutting_down = 0;
- sp->AcceptEx = AcceptEx;
- sp->new_socket = INVALID_SOCKET;
- sp->port = port;
- sp->port_index = port_index;
- grpc_closure_init(&sp->on_accept, on_accept, sp);
- GPR_ASSERT(sp->socket);
- gpr_mu_unlock(&s->mu);
+ error = prepare_socket(sock, addr, addr_len, &port);
+ if (error != GRPC_ERROR_NONE) {
+ return error;
}
- return sp;
+ GPR_ASSERT(port >= 0);
+ gpr_mu_lock(&s->mu);
+ GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
+ sp = gpr_malloc(sizeof(grpc_tcp_listener));
+ sp->next = NULL;
+ if (s->head == NULL) {
+ s->head = sp;
+ } else {
+ s->tail->next = sp;
+ }
+ s->tail = sp;
+ sp->server = s;
+ sp->socket = grpc_winsocket_create(sock, "listener");
+ sp->shutting_down = 0;
+ sp->AcceptEx = AcceptEx;
+ sp->new_socket = INVALID_SOCKET;
+ sp->port = port;
+ sp->port_index = port_index;
+ grpc_closure_init(&sp->on_accept, on_accept, sp);
+ GPR_ASSERT(sp->socket);
+ gpr_mu_unlock(&s->mu);
+ *listener = sp;
+
+ return GRPC_ERROR_NONE;
}
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
- size_t addr_len) {
- grpc_tcp_listener *sp;
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+ size_t addr_len, int *port) {
+ grpc_tcp_listener *sp = NULL;
SOCKET sock;
struct sockaddr_in6 addr6_v4mapped;
struct sockaddr_in6 wildcard;
struct sockaddr *allocated_addr = NULL;
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
- int port;
unsigned port_index = 0;
+ grpc_error *error = GRPC_ERROR_NONE;
+
if (s->tail != NULL) {
port_index = s->tail->port_index + 1;
}
@@ -466,11 +472,11 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
sockname_len = sizeof(sockname_temp);
if (0 == getsockname(sp->socket->socket,
(struct sockaddr *)&sockname_temp, &sockname_len)) {
- port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
- if (port > 0) {
+ *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+ if (*port > 0) {
allocated_addr = gpr_malloc(addr_len);
memcpy(allocated_addr, addr, addr_len);
- grpc_sockaddr_set_port(allocated_addr, port);
+ grpc_sockaddr_set_port(allocated_addr, *port);
addr = allocated_addr;
break;
}
@@ -484,8 +490,8 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
}
/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
- if (grpc_sockaddr_is_wildcard(addr, &port)) {
- grpc_sockaddr_make_wildcard6(port, &wildcard);
+ if (grpc_sockaddr_is_wildcard(addr, port)) {
+ grpc_sockaddr_make_wildcard6(*port, &wildcard);
addr = (struct sockaddr *)&wildcard;
addr_len = sizeof(wildcard);
@@ -494,19 +500,26 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
WSA_FLAG_OVERLAPPED);
if (sock == INVALID_SOCKET) {
- char *utf8_message = gpr_format_message(WSAGetLastError());
- gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message);
- gpr_free(utf8_message);
+ error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
+ goto done;
}
- sp = add_socket_to_server(s, sock, addr, addr_len, port_index);
+ error = add_socket_to_server(s, sock, addr, addr_len, port_index, &sp);
+
+done:
gpr_free(allocated_addr);
- if (sp) {
- return sp->port;
+ if (error != GRPC_ERROR_NONE) {
+ grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING(
+ "Failed to add port to server", &error, 1);
+ GRPC_ERROR_UNREF(error);
+ error = error_out;
+ *port = -1;
} else {
- return -1;
+ GPR_ASSERT(sp != NULL);
+ *port = sp->port;
}
+ return error;
}
void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
@@ -521,7 +534,7 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
s->on_accept_cb = on_accept_cb;
s->on_accept_cb_arg = on_accept_cb_arg;
for (sp = s->head; sp; sp = sp->next) {
- start_accept(exec_ctx, sp);
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept(exec_ctx, sp)));
s->active_ports++;
}
gpr_mu_unlock(&s->mu);
diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
index 551149e1a6..37ab59021e 100644
--- a/src/core/lib/iomgr/tcp_windows.c
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -37,11 +37,12 @@
#include <limits.h>
-#include "src/core/lib/iomgr/sockaddr_win32.h"
+#include "src/core/lib/iomgr/network_status_tracker.h"
+#include "src/core/lib/iomgr/sockaddr_windows.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
@@ -61,27 +62,34 @@
#define GRPC_FIONBIO FIONBIO
#endif
-static int set_non_block(SOCKET sock) {
+static grpc_error *set_non_block(SOCKET sock) {
int status;
uint32_t param = 1;
DWORD ret;
status = WSAIoctl(sock, GRPC_FIONBIO, &param, sizeof(param), NULL, 0, &ret,
NULL, NULL);
- return status == 0;
+ return status == 0
+ ? GRPC_ERROR_NONE
+ : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)");
}
-static int set_dualstack(SOCKET sock) {
+static grpc_error *set_dualstack(SOCKET sock) {
int status;
unsigned long param = 0;
status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&param,
sizeof(param));
- return status == 0;
+ return status == 0
+ ? GRPC_ERROR_NONE
+ : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
}
-int grpc_tcp_prepare_socket(SOCKET sock) {
- if (!set_non_block(sock)) return 0;
- if (!set_dualstack(sock)) return 0;
- return 1;
+grpc_error *grpc_tcp_prepare_socket(SOCKET sock) {
+ grpc_error *err;
+ err = set_non_block(sock);
+ if (err != GRPC_ERROR_NONE) return err;
+ err = set_dualstack(sock);
+ if (err != GRPC_ERROR_NONE) return err;
+ return GRPC_ERROR_NONE;
}
typedef struct grpc_tcp {
@@ -148,39 +156,35 @@ static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
#endif
/* Asynchronous callback from the IOCP, or the background thread. */
-static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
grpc_tcp *tcp = tcpp;
grpc_closure *cb = tcp->read_cb;
grpc_winsocket *socket = tcp->socket;
gpr_slice sub;
grpc_winsocket_callback_info *info = &socket->read_info;
- if (success) {
+ GRPC_ERROR_REF(error);
+
+ if (error == GRPC_ERROR_NONE) {
if (info->wsa_error != 0 && !tcp->shutting_down) {
- if (info->wsa_error != WSAECONNRESET) {
- char *utf8_message = gpr_format_message(info->wsa_error);
- gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
- gpr_free(utf8_message);
- }
- success = 0;
+ char *utf8_message = gpr_format_message(info->wsa_error);
+ error = GRPC_ERROR_CREATE(utf8_message);
+ gpr_free(utf8_message);
gpr_slice_unref(tcp->read_slice);
} else {
if (info->bytes_transfered != 0 && !tcp->shutting_down) {
sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
gpr_slice_buffer_add(tcp->read_slices, sub);
- success = 1;
} else {
gpr_slice_unref(tcp->read_slice);
- success = 0;
+ error = GRPC_ERROR_CREATE("End of TCP stream");
}
}
}
tcp->read_cb = NULL;
TCP_UNREF(tcp, "read");
- if (cb) {
- cb->cb(exec_ctx, cb->cb_arg, success);
- }
+ grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
}
static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -194,7 +198,8 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
WSABUF buffer;
if (tcp->shutting_down) {
- grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, cb,
+ GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
return;
}
@@ -218,7 +223,7 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
/* Did we get data immediately ? Yay. */
if (info->wsa_error != WSAEWOULDBLOCK) {
info->bytes_transfered = bytes_read;
- grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE, NULL);
return;
}
@@ -231,7 +236,8 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
int wsa_error = WSAGetLastError();
if (wsa_error != WSA_IO_PENDING) {
info->wsa_error = wsa_error;
- grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &tcp->on_read,
+ GRPC_WSA_ERROR(info->wsa_error, "WSARecv"), NULL);
return;
}
}
@@ -240,32 +246,29 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
}
/* Asynchronous callback from the IOCP, or the background thread. */
-static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) {
+static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
grpc_tcp *tcp = (grpc_tcp *)tcpp;
grpc_winsocket *handle = tcp->socket;
grpc_winsocket_callback_info *info = &handle->write_info;
grpc_closure *cb;
+ GRPC_ERROR_REF(error);
+
gpr_mu_lock(&tcp->mu);
cb = tcp->write_cb;
tcp->write_cb = NULL;
gpr_mu_unlock(&tcp->mu);
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
if (info->wsa_error != 0) {
- if (info->wsa_error != WSAECONNRESET) {
- char *utf8_message = gpr_format_message(info->wsa_error);
- gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
- gpr_free(utf8_message);
- }
- success = 0;
+ error = GRPC_WSA_ERROR(info->wsa_error, "WSASend");
} else {
GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
}
}
TCP_UNREF(tcp, "write");
- cb->cb(exec_ctx, cb->cb_arg, success);
+ grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
}
/* Initiates a write. */
@@ -283,7 +286,8 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
size_t len;
if (tcp->shutting_down) {
- grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, cb,
+ GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
return;
}
@@ -311,19 +315,10 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
connection that has its send queue filled up. But if we don't, then we can
avoid doing an async write operation at all. */
if (info->wsa_error != WSAEWOULDBLOCK) {
- bool ok = false;
- if (status == 0) {
- ok = true;
- GPR_ASSERT(bytes_sent == tcp->write_slices->length);
- } else {
- if (info->wsa_error != WSAECONNRESET) {
- char *utf8_message = gpr_format_message(info->wsa_error);
- gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
- gpr_free(utf8_message);
- }
- }
- if (allocated) gpr_free(allocated);
- grpc_exec_ctx_enqueue(exec_ctx, cb, ok, NULL);
+ grpc_error *error = status == 0
+ ? GRPC_ERROR_NONE
+ : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
+ grpc_exec_ctx_sched(exec_ctx, cb, error, NULL);
return;
}
@@ -340,7 +335,8 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
int wsa_error = WSAGetLastError();
if (wsa_error != WSA_IO_PENDING) {
TCP_UNREF(tcp, "write");
- grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend"),
+ NULL);
return;
}
}
@@ -383,6 +379,7 @@ static void win_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
}
static void win_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
+ grpc_network_status_unregister_endpoint(ep);
grpc_tcp *tcp = (grpc_tcp *)ep;
TCP_UNREF(tcp, "destroy");
}
@@ -406,6 +403,9 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) {
grpc_closure_init(&tcp->on_read, on_read, tcp);
grpc_closure_init(&tcp->on_write, on_write, tcp);
tcp->peer_string = gpr_strdup(peer_string);
+ /* Tell network status tracking code about the new endpoint */
+ grpc_network_status_register_endpoint(&tcp->base);
+
return &tcp->base;
}
diff --git a/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h
index a2f58eddd5..86d777235e 100644
--- a/src/core/lib/iomgr/tcp_windows.h
+++ b/src/core/lib/iomgr/tcp_windows.h
@@ -52,6 +52,6 @@
*/
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string);
-int grpc_tcp_prepare_socket(SOCKET sock);
+grpc_error *grpc_tcp_prepare_socket(SOCKET sock);
#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */
diff --git a/src/core/lib/iomgr/timer.c b/src/core/lib/iomgr/timer.c
index acb5b26c87..9975fa1671 100644
--- a/src/core/lib/iomgr/timer.c
+++ b/src/core/lib/iomgr/timer.c
@@ -73,7 +73,7 @@ static shard_type *g_shard_queue[NUM_SHARDS];
static bool g_initialized = false;
static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
- gpr_timespec *next, int success);
+ gpr_timespec *next, grpc_error *error);
static gpr_timespec compute_min_deadline(shard_type *shard) {
return grpc_timer_heap_is_empty(&shard->heap)
@@ -105,7 +105,8 @@ void grpc_timer_list_init(gpr_timespec now) {
void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {
int i;
- run_some_expired_timers(exec_ctx, gpr_inf_future(g_clock_type), NULL, 0);
+ run_some_expired_timers(exec_ctx, gpr_inf_future(g_clock_type), NULL,
+ GRPC_ERROR_CREATE("Timer list shutdown"));
for (i = 0; i < NUM_SHARDS; i++) {
shard_type *shard = &g_shards[i];
gpr_mu_destroy(&shard->mu);
@@ -185,13 +186,16 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
if (!g_initialized) {
timer->triggered = 1;
- grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, false, NULL);
+ grpc_exec_ctx_sched(
+ exec_ctx, &timer->closure,
+ GRPC_ERROR_CREATE("Attempt to create timer before initialization"),
+ NULL);
return;
}
if (gpr_time_cmp(deadline, now) <= 0) {
timer->triggered = 1;
- grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL);
return;
}
@@ -235,10 +239,15 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
}
void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
+ if (!g_initialized) {
+ /* must have already been cancelled, also the shard mutex is invalid */
+ return;
+ }
+
shard_type *shard = &g_shards[shard_idx(timer)];
gpr_mu_lock(&shard->mu);
if (!timer->triggered) {
- grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED, NULL);
timer->triggered = 1;
if (timer->heap_index == INVALID_HEAP_INDEX) {
list_remove(timer);
@@ -278,8 +287,8 @@ static int refill_queue(shard_type *shard, gpr_timespec now) {
return !grpc_timer_heap_is_empty(&shard->heap);
}
-/* This pops the next non-cancelled timer with deadline <= now from the queue,
- or returns NULL if there isn't one.
+/* This pops the next non-cancelled timer with deadline <= now from the
+ queue, or returns NULL if there isn't one.
REQUIRES: shard->mu locked */
static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) {
grpc_timer *timer;
@@ -299,12 +308,12 @@ static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) {
/* REQUIRES: shard->mu unlocked */
static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
gpr_timespec now, gpr_timespec *new_min_deadline,
- int success) {
+ grpc_error *error) {
size_t n = 0;
grpc_timer *timer;
gpr_mu_lock(&shard->mu);
while ((timer = pop_one(shard, now))) {
- grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, success, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_REF(error), NULL);
n++;
}
*new_min_deadline = compute_min_deadline(shard);
@@ -313,7 +322,7 @@ static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
}
static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
- gpr_timespec *next, int success) {
+ gpr_timespec *next, grpc_error *error) {
size_t n = 0;
/* TODO(ctiller): verify that there are any timers (atomically) here */
@@ -327,8 +336,8 @@ static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
/* For efficiency, we pop as many available timers as we can from the
shard. This may violate perfect timer deadline ordering, but that
shouldn't be a big deal because we don't make ordering guarantees. */
- n += pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline,
- success);
+ n +=
+ pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, error);
/* An grpc_timer_init() on the shard could intervene here, adding a new
timer that is earlier than new_min_deadline. However,
@@ -359,6 +368,8 @@ static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
*next, gpr_time_add(now, gpr_time_from_millis(1, GPR_TIMESPAN)));
}
+ GRPC_ERROR_UNREF(error);
+
return (int)n;
}
@@ -367,5 +378,7 @@ bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now,
GPR_ASSERT(now.clock_type == g_clock_type);
return run_some_expired_timers(
exec_ctx, now, next,
- gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0);
+ gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0
+ ? GRPC_ERROR_NONE
+ : GRPC_ERROR_CREATE("Shutting down timer system"));
}
diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c
index 98ffccd59b..1ebccf2ee2 100644
--- a/src/core/lib/iomgr/udp_server.c
+++ b/src/core/lib/iomgr/udp_server.c
@@ -210,6 +210,8 @@ static int prepare_socket(int fd, const struct sockaddr *addr,
size_t addr_len) {
struct sockaddr_storage sockname_temp;
socklen_t sockname_len;
+ /* Set send/receive socket buffers to 1 MB */
+ int buffer_size_bytes = 1024 * 1024;
if (fd < 0) {
goto error;
@@ -239,6 +241,18 @@ static int prepare_socket(int fd, const struct sockaddr *addr,
goto error;
}
+ if (!grpc_set_socket_sndbuf(fd, buffer_size_bytes)) {
+ gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes",
+ buffer_size_bytes);
+ goto error;
+ }
+
+ if (!grpc_set_socket_rcvbuf(fd, buffer_size_bytes)) {
+ gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes",
+ buffer_size_bytes);
+ goto error;
+ }
+
return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
error:
diff --git a/src/core/lib/iomgr/unix_sockets_posix.c b/src/core/lib/iomgr/unix_sockets_posix.c
index 5767c852df..0e7670e5a5 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.c
+++ b/src/core/lib/iomgr/unix_sockets_posix.c
@@ -47,17 +47,18 @@ void grpc_create_socketpair_if_unix(int sv[2]) {
GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
}
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
+grpc_error *grpc_resolve_unix_domain_address(const char *name,
+ grpc_resolved_addresses **addrs) {
struct sockaddr_un *un;
- grpc_resolved_addresses *addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
- addrs->naddrs = 1;
- addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address));
- un = (struct sockaddr_un *)addrs->addrs->addr;
+ *addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
+ (*addrs)->naddrs = 1;
+ (*addrs)->addrs = gpr_malloc(sizeof(grpc_resolved_address));
+ un = (struct sockaddr_un *)(*addrs)->addrs->addr;
un->sun_family = AF_UNIX;
strcpy(un->sun_path, name);
- addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
- return addrs;
+ (*addrs)->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
+ return GRPC_ERROR_NONE;
}
int grpc_is_unix_socket(const struct sockaddr *addr) {
diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h
index 6758c498e5..db0516d945 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.h
+++ b/src/core/lib/iomgr/unix_sockets_posix.h
@@ -43,7 +43,8 @@
void grpc_create_socketpair_if_unix(int sv[2]);
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name);
+grpc_error *grpc_resolve_unix_domain_address(
+ const char *name, grpc_resolved_addresses **addresses);
int grpc_is_unix_socket(const struct sockaddr *addr);
diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.c b/src/core/lib/iomgr/unix_sockets_posix_noop.c
index d30952789f..56b47c3daf 100644
--- a/src/core/lib/iomgr/unix_sockets_posix_noop.c
+++ b/src/core/lib/iomgr/unix_sockets_posix_noop.c
@@ -44,8 +44,10 @@ void grpc_create_socketpair_if_unix(int sv[2]) {
GPR_ASSERT(0);
}
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
- return NULL;
+grpc_error *grpc_resolve_unix_domain_address(
+ const char *name, grpc_resolved_addresses **addresses) {
+ *addresses = NULL;
+ return GRPC_ERROR_CREATE("Unix domain sockets are not supported on Windows");
}
int grpc_is_unix_socket(const struct sockaddr *addr) { return false; }
diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c
index 8a772add13..95f6102330 100644
--- a/src/core/lib/iomgr/wakeup_fd_eventfd.c
+++ b/src/core/lib/iomgr/wakeup_fd_eventfd.c
@@ -44,29 +44,39 @@
#include "src/core/lib/iomgr/wakeup_fd_posix.h"
#include "src/core/lib/profiling/timers.h"
-static void eventfd_create(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) {
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
- /* TODO(klempner): Handle failure more gracefully */
- GPR_ASSERT(efd >= 0);
+ if (efd < 0) {
+ return GRPC_OS_ERROR(errno, "eventfd");
+ }
fd_info->read_fd = efd;
fd_info->write_fd = -1;
+ return GRPC_ERROR_NONE;
}
-static void eventfd_consume(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_consume(grpc_wakeup_fd* fd_info) {
eventfd_t value;
int err;
do {
err = eventfd_read(fd_info->read_fd, &value);
} while (err < 0 && errno == EINTR);
+ if (err < 0 && errno != EAGAIN) {
+ return GRPC_OS_ERROR(errno, "eventfd_read");
+ }
+ return GRPC_ERROR_NONE;
}
-static void eventfd_wakeup(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) {
int err;
GPR_TIMER_BEGIN("eventfd_wakeup", 0);
do {
err = eventfd_write(fd_info->read_fd, 1);
} while (err < 0 && errno == EINTR);
+ if (err < 0) {
+ return GRPC_OS_ERROR(errno, "eventfd_write");
+ }
GPR_TIMER_END("eventfd_wakeup", 0);
+ return GRPC_ERROR_NONE;
}
static void eventfd_destroy(grpc_wakeup_fd* fd_info) {
@@ -74,8 +84,10 @@ static void eventfd_destroy(grpc_wakeup_fd* fd_info) {
}
static int eventfd_check_availability(void) {
- /* TODO(klempner): Actually check if eventfd is available */
- return 1;
+ const int efd = eventfd(0, 0);
+ const int is_available = efd >= 0;
+ if (is_available) close(efd);
+ return is_available;
}
const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = {
diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c
index e9b9a0119f..4e5dbdcb73 100644
--- a/src/core/lib/iomgr/wakeup_fd_pipe.c
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.c
@@ -45,7 +45,7 @@
#include "src/core/lib/iomgr/socket_utils_posix.h"
-static void pipe_init(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) {
int pipefd[2];
/* TODO(klempner): Make this nonfatal */
int r = pipe(pipefd);
@@ -53,36 +53,40 @@ static void pipe_init(grpc_wakeup_fd* fd_info) {
gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno));
abort();
}
- GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
- GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
+ grpc_error* err;
+ err = grpc_set_socket_nonblocking(pipefd[0], 1);
+ if (err != GRPC_ERROR_NONE) return err;
+ err = grpc_set_socket_nonblocking(pipefd[1], 1);
+ if (err != GRPC_ERROR_NONE) return err;
fd_info->read_fd = pipefd[0];
fd_info->write_fd = pipefd[1];
+ return GRPC_ERROR_NONE;
}
-static void pipe_consume(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) {
char buf[128];
ssize_t r;
for (;;) {
r = read(fd_info->read_fd, buf, sizeof(buf));
if (r > 0) continue;
- if (r == 0) return;
+ if (r == 0) return GRPC_ERROR_NONE;
switch (errno) {
case EAGAIN:
- return;
+ return GRPC_ERROR_NONE;
case EINTR:
continue;
default:
- gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
- return;
+ return GRPC_OS_ERROR(errno, "read");
}
}
}
-static void pipe_wakeup(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) {
char c = 0;
while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR)
;
+ return GRPC_ERROR_NONE;
}
static void pipe_destroy(grpc_wakeup_fd* fd_info) {
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.c b/src/core/lib/iomgr/wakeup_fd_posix.c
index 525369c356..046208abc8 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.c
+++ b/src/core/lib/iomgr/wakeup_fd_posix.c
@@ -53,16 +53,16 @@ void grpc_wakeup_fd_global_init(void) {
void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; }
-void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) {
- wakeup_fd_vtable->init(fd_info);
+grpc_error *grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) {
+ return wakeup_fd_vtable->init(fd_info);
}
-void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) {
- wakeup_fd_vtable->consume(fd_info);
+grpc_error *grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) {
+ return wakeup_fd_vtable->consume(fd_info);
}
-void grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) {
- wakeup_fd_vtable->wakeup(fd_info);
+grpc_error *grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) {
+ return wakeup_fd_vtable->wakeup(fd_info);
}
void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) {
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h
index 6b069c1837..e269f242d8 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.h
+++ b/src/core/lib/iomgr/wakeup_fd_posix.h
@@ -62,6 +62,8 @@
#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
+#include "src/core/lib/iomgr/error.h"
+
void grpc_wakeup_fd_global_init(void);
void grpc_wakeup_fd_global_destroy(void);
@@ -72,9 +74,9 @@ void grpc_wakeup_fd_global_init_force_fallback(void);
typedef struct grpc_wakeup_fd grpc_wakeup_fd;
typedef struct grpc_wakeup_fd_vtable {
- void (*init)(grpc_wakeup_fd* fd_info);
- void (*consume)(grpc_wakeup_fd* fd_info);
- void (*wakeup)(grpc_wakeup_fd* fd_info);
+ grpc_error* (*init)(grpc_wakeup_fd* fd_info);
+ grpc_error* (*consume)(grpc_wakeup_fd* fd_info);
+ grpc_error* (*wakeup)(grpc_wakeup_fd* fd_info);
void (*destroy)(grpc_wakeup_fd* fd_info);
/* Must be called before calling any other functions */
int (*check_availability)(void);
@@ -89,9 +91,10 @@ extern int grpc_allow_specialized_wakeup_fd;
#define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
-void grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info);
-void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info);
-void grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info);
+grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info)
+ GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info);
/* Defined in some specialized implementation's .c file, or by
diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h
index 3e2b223670..5cc40eea50 100644
--- a/src/core/lib/iomgr/workqueue.h
+++ b/src/core/lib/iomgr/workqueue.h
@@ -43,14 +43,15 @@
#include "src/core/lib/iomgr/workqueue_posix.h"
#endif
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include "src/core/lib/iomgr/workqueue_windows.h"
#endif
/* grpc_workqueue is forward declared in exec_ctx.h */
/** Create a work queue */
-grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx);
+grpc_error *grpc_workqueue_create(grpc_exec_ctx *exec_ctx,
+ grpc_workqueue **workqueue);
void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue);
@@ -77,7 +78,7 @@ void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx,
grpc_pollset *pollset);
/** Add a work item to a workqueue */
-void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure,
- int success);
+void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
+ grpc_closure *closure, grpc_error *error);
#endif /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_H */
diff --git a/src/core/lib/iomgr/workqueue_posix.c b/src/core/lib/iomgr/workqueue_posix.c
index 80e7a0b206..45e0f6063b 100644
--- a/src/core/lib/iomgr/workqueue_posix.c
+++ b/src/core/lib/iomgr/workqueue_posix.c
@@ -45,22 +45,27 @@
#include "src/core/lib/iomgr/ev_posix.h"
-static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success);
+static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
-grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx) {
+grpc_error *grpc_workqueue_create(grpc_exec_ctx *exec_ctx,
+ grpc_workqueue **workqueue) {
char name[32];
- grpc_workqueue *workqueue = gpr_malloc(sizeof(grpc_workqueue));
- gpr_ref_init(&workqueue->refs, 1);
- gpr_mu_init(&workqueue->mu);
- workqueue->closure_list.head = workqueue->closure_list.tail = NULL;
- grpc_wakeup_fd_init(&workqueue->wakeup_fd);
- sprintf(name, "workqueue:%p", (void *)workqueue);
- workqueue->wakeup_read_fd =
- grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&workqueue->wakeup_fd), name);
- grpc_closure_init(&workqueue->read_closure, on_readable, workqueue);
- grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
- &workqueue->read_closure);
- return workqueue;
+ *workqueue = gpr_malloc(sizeof(grpc_workqueue));
+ gpr_ref_init(&(*workqueue)->refs, 1);
+ gpr_mu_init(&(*workqueue)->mu);
+ (*workqueue)->closure_list.head = (*workqueue)->closure_list.tail = NULL;
+ grpc_error *err = grpc_wakeup_fd_init(&(*workqueue)->wakeup_fd);
+ if (err != GRPC_ERROR_NONE) {
+ gpr_free(*workqueue);
+ return err;
+ }
+ sprintf(name, "workqueue:%p", (void *)(*workqueue));
+ (*workqueue)->wakeup_read_fd = grpc_fd_create(
+ GRPC_WAKEUP_FD_GET_READ_FD(&(*workqueue)->wakeup_fd), name);
+ grpc_closure_init(&(*workqueue)->read_closure, on_readable, *workqueue);
+ grpc_fd_notify_on_read(exec_ctx, (*workqueue)->wakeup_read_fd,
+ &(*workqueue)->read_closure);
+ return GRPC_ERROR_NONE;
}
static void workqueue_destroy(grpc_exec_ctx *exec_ctx,
@@ -103,17 +108,14 @@ void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx,
void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
gpr_mu_lock(&workqueue->mu);
- if (grpc_closure_list_empty(workqueue->closure_list)) {
- grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
- }
grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
gpr_mu_unlock(&workqueue->mu);
}
-static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_workqueue *workqueue = arg;
- if (!success) {
+ if (error != GRPC_ERROR_NONE) {
gpr_mu_destroy(&workqueue->mu);
/* HACK: let wakeup_fd code know that we stole the fd */
workqueue->wakeup_fd.read_fd = 0;
@@ -123,20 +125,32 @@ static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
} else {
gpr_mu_lock(&workqueue->mu);
grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
- grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd);
+ error = grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd);
gpr_mu_unlock(&workqueue->mu);
- grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
- &workqueue->read_closure);
+ if (error == GRPC_ERROR_NONE) {
+ grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
+ &workqueue->read_closure);
+ } else {
+ /* recurse to get error handling */
+ on_readable(exec_ctx, arg, error);
+ }
}
}
-void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure,
- int success) {
+void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
+ grpc_closure *closure, grpc_error *error) {
+ grpc_error *push_error = GRPC_ERROR_NONE;
gpr_mu_lock(&workqueue->mu);
if (grpc_closure_list_empty(workqueue->closure_list)) {
- grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
+ push_error = grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
+ }
+ grpc_closure_list_append(&workqueue->closure_list, closure, error);
+ if (push_error != GRPC_ERROR_NONE) {
+ const char *msg = grpc_error_string(push_error);
+ gpr_log(GPR_ERROR, "Failed to push to workqueue: %s", msg);
+ grpc_error_free_string(msg);
+ grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
}
- grpc_closure_list_add(&workqueue->closure_list, closure, success);
gpr_mu_unlock(&workqueue->mu);
}
diff --git a/src/core/lib/iomgr/workqueue_windows.c b/src/core/lib/iomgr/workqueue_windows.c
index c3c0446a57..275f040b1c 100644
--- a/src/core/lib/iomgr/workqueue_windows.c
+++ b/src/core/lib/iomgr/workqueue_windows.c
@@ -33,8 +33,8 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include "src/core/lib/iomgr/workqueue.h"
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/profiling/basic_timers.c b/src/core/lib/profiling/basic_timers.c
index 50082cd7ee..51813d0461 100644
--- a/src/core/lib/profiling/basic_timers.c
+++ b/src/core/lib/profiling/basic_timers.c
@@ -141,11 +141,11 @@ static void write_log(gpr_timer_log *log) {
entry->tm = gpr_time_0(entry->tm.clock_type);
}
fprintf(output_file,
- "{\"t\": %lld.%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
+ "{\"t\": %" PRId64
+ ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": "
"\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n",
- (long long)entry->tm.tv_sec, (int)entry->tm.tv_nsec, entry->thd,
- entry->type, entry->tagstr, entry->file, entry->line,
- entry->important);
+ entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type,
+ entry->tagstr, entry->file, entry->line, entry->important);
}
}
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c
index 18189a8fb8..850e41e646 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.c
+++ b/src/core/lib/security/credentials/composite/composite_credentials.c
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@
#include <string.h>
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/surface/api_trace.h"
#include <grpc/support/alloc.h>
@@ -49,7 +50,7 @@ typedef struct {
grpc_credentials_md_store *md_elems;
grpc_auth_metadata_context auth_md_context;
void *user_data;
- grpc_pollset *pollset;
+ grpc_polling_entity *pollent;
grpc_credentials_metadata_cb cb;
} grpc_composite_call_credentials_metadata_context;
@@ -71,11 +72,12 @@ static void composite_call_md_context_destroy(
static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
grpc_credentials_md *md_elems,
size_t num_md,
- grpc_credentials_status status) {
+ grpc_credentials_status status,
+ const char *error_details) {
grpc_composite_call_credentials_metadata_context *ctx =
(grpc_composite_call_credentials_metadata_context *)user_data;
if (status != GRPC_CREDENTIALS_OK) {
- ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status);
+ ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status, error_details);
return;
}
@@ -93,20 +95,20 @@ static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
grpc_call_credentials *inner_creds =
ctx->composite_creds->inner.creds_array[ctx->creds_index++];
grpc_call_credentials_get_request_metadata(
- exec_ctx, inner_creds, ctx->pollset, ctx->auth_md_context,
+ exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context,
composite_call_metadata_cb, ctx);
return;
}
/* We're done!. */
ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries,
- ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK);
+ ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK, NULL);
composite_call_md_context_destroy(ctx);
}
static void composite_call_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context auth_md_context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context auth_md_context,
grpc_credentials_metadata_cb cb, void *user_data) {
grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
grpc_composite_call_credentials_metadata_context *ctx;
@@ -117,10 +119,10 @@ static void composite_call_get_request_metadata(
ctx->user_data = user_data;
ctx->cb = cb;
ctx->composite_creds = c;
- ctx->pollset = pollset;
+ ctx->pollent = pollent;
ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds);
grpc_call_credentials_get_request_metadata(
- exec_ctx, c->inner.creds_array[ctx->creds_index++], pollset,
+ exec_ctx, c->inner.creds_array[ctx->creds_index++], ctx->pollent,
auth_md_context, composite_call_metadata_cb, ctx);
}
diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c
index 3dde6e587d..029a357261 100644
--- a/src/core/lib/security/credentials/credentials.c
+++ b/src/core/lib/security/credentials/credentials.c
@@ -58,6 +58,7 @@ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
void *user_data) {
grpc_credentials_metadata_request *r =
gpr_malloc(sizeof(grpc_credentials_metadata_request));
+ memset(&r->response, 0, sizeof(r->response));
r->creds = grpc_call_credentials_ref(creds);
r->cb = cb;
r->user_data = user_data;
@@ -67,6 +68,7 @@ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
void grpc_credentials_metadata_request_destroy(
grpc_credentials_metadata_request *r) {
grpc_call_credentials_unref(r->creds);
+ grpc_http_response_destroy(&r->response);
gpr_free(r);
}
@@ -111,15 +113,15 @@ void grpc_call_credentials_release(grpc_call_credentials *creds) {
void grpc_call_credentials_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb, void *user_data) {
if (creds == NULL || creds->vtable->get_request_metadata == NULL) {
if (cb != NULL) {
- cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL);
}
return;
}
- creds->vtable->get_request_metadata(exec_ctx, creds, pollset, context, cb,
+ creds->vtable->get_request_metadata(exec_ctx, creds, pollent, context, cb,
user_data);
}
diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h
index 5f44c7c3e3..8e9d842ead 100644
--- a/src/core/lib/security/credentials/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -41,6 +41,7 @@
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/security/transport/security_connector.h"
struct grpc_http_response;
@@ -155,16 +156,16 @@ void grpc_credentials_md_store_unref(grpc_credentials_md_store *store);
/* --- grpc_call_credentials. --- */
-typedef void (*grpc_credentials_metadata_cb)(grpc_exec_ctx *exec_ctx,
- void *user_data,
- grpc_credentials_md *md_elems,
- size_t num_md,
- grpc_credentials_status status);
+/* error_details must be NULL if status is GRPC_CREDENTIALS_OK. */
+typedef void (*grpc_credentials_metadata_cb)(
+ grpc_exec_ctx *exec_ctx, void *user_data, grpc_credentials_md *md_elems,
+ size_t num_md, grpc_credentials_status status, const char *error_details);
typedef struct {
void (*destruct)(grpc_call_credentials *c);
void (*get_request_metadata)(grpc_exec_ctx *exec_ctx,
- grpc_call_credentials *c, grpc_pollset *pollset,
+ grpc_call_credentials *c,
+ grpc_polling_entity *pollent,
grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb,
void *user_data);
@@ -180,7 +181,7 @@ grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds);
void grpc_call_credentials_unref(grpc_call_credentials *creds);
void grpc_call_credentials_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb, void *user_data);
/* Metadata-only credentials with the specified key and value where
@@ -223,6 +224,7 @@ grpc_server_credentials *grpc_find_server_credentials_in_args(
typedef struct {
grpc_call_credentials *creds;
grpc_credentials_metadata_cb cb;
+ grpc_http_response response;
void *user_data;
} grpc_credentials_metadata_request;
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c
index 54d7cf2581..51cafd986f 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.c
+++ b/src/core/lib/security/credentials/fake/fake_credentials.c
@@ -95,28 +95,29 @@ static void md_only_test_destruct(grpc_call_credentials *creds) {
}
static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
- void *user_data, bool success) {
+ void *user_data, grpc_error *error) {
grpc_credentials_metadata_request *r =
(grpc_credentials_metadata_request *)user_data;
grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds;
r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_metadata_request_destroy(r);
}
static void md_only_test_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb, void *user_data) {
grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
if (c->is_async) {
grpc_credentials_metadata_request *cb_arg =
grpc_credentials_metadata_request_create(creds, cb, user_data);
- grpc_executor_enqueue(
- grpc_closure_create(on_simulated_token_fetch_done, cb_arg), true);
+ grpc_executor_push(
+ grpc_closure_create(on_simulated_token_fetch_done, cb_arg),
+ GRPC_ERROR_NONE);
} else {
- cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK, NULL);
}
}
diff --git a/src/core/lib/security/credentials/google_default/credentials_win32.c b/src/core/lib/security/credentials/google_default/credentials_windows.c
index cd8b48080a..208b8fd9ad 100644
--- a/src/core/lib/security/credentials/google_default/credentials_win32.c
+++ b/src/core/lib/security/credentials/google_default/credentials_windows.c
@@ -33,7 +33,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
@@ -58,4 +58,4 @@ char *grpc_get_well_known_google_credentials_file_path_impl(void) {
return result;
}
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c
index a521d95abc..312a3d4f90 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.c
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.c
@@ -41,10 +41,12 @@
#include "src/core/lib/http/httpcli.h"
#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
#include "src/core/lib/support/env.h"
-#include "src/core/lib/support/load_file.h"
+#include "src/core/lib/support/string.h"
#include "src/core/lib/surface/api_trace.h"
/* -- Constants. -- */
@@ -62,21 +64,23 @@ static gpr_once g_once = GPR_ONCE_INIT;
static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); }
typedef struct {
- grpc_pollset *pollset;
+ grpc_polling_entity pollent;
int is_done;
int success;
+ grpc_http_response response;
} compute_engine_detector;
-static void on_compute_engine_detection_http_response(
- grpc_exec_ctx *exec_ctx, void *user_data,
- const grpc_http_response *response) {
+static void on_compute_engine_detection_http_response(grpc_exec_ctx *exec_ctx,
+ void *user_data,
+ grpc_error *error) {
compute_engine_detector *detector = (compute_engine_detector *)user_data;
- if (response != NULL && response->status == 200 && response->hdr_count > 0) {
+ if (error == GRPC_ERROR_NONE && detector->response.status == 200 &&
+ detector->response.hdr_count > 0) {
/* Internet providers can return a generic response to all requests, so
it is necessary to check that metadata header is present also. */
size_t i;
- for (i = 0; i < response->hdr_count; i++) {
- grpc_http_header *header = &response->hdrs[i];
+ for (i = 0; i < detector->response.hdr_count; i++) {
+ grpc_http_header *header = &detector->response.hdrs[i];
if (strcmp(header->key, "Metadata-Flavor") == 0 &&
strcmp(header->value, "Google") == 0) {
detector->success = 1;
@@ -86,11 +90,13 @@ static void on_compute_engine_detection_http_response(
}
gpr_mu_lock(g_polling_mu);
detector->is_done = 1;
- grpc_pollset_kick(detector->pollset, NULL);
+ GRPC_LOG_IF_ERROR(
+ "Pollset kick",
+ grpc_pollset_kick(grpc_polling_entity_pollset(&detector->pollent), NULL));
gpr_mu_unlock(g_polling_mu);
}
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool s) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, grpc_error *e) {
grpc_pollset_destroy(p);
}
@@ -105,11 +111,13 @@ static int is_stack_running_on_compute_engine(void) {
on compute engine. */
gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN);
- detector.pollset = gpr_malloc(grpc_pollset_size());
- grpc_pollset_init(detector.pollset, &g_polling_mu);
+ grpc_pollset *pollset = gpr_malloc(grpc_pollset_size());
+ grpc_pollset_init(pollset, &g_polling_mu);
+ detector.pollent = grpc_polling_entity_create_from_pollset(pollset);
detector.is_done = 0;
detector.success = 0;
+ memset(&detector.response, 0, sizeof(detector.response));
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = GRPC_COMPUTE_ENGINE_DETECTION_HOST;
request.http.path = "/";
@@ -117,48 +125,71 @@ static int is_stack_running_on_compute_engine(void) {
grpc_httpcli_context_init(&context);
grpc_httpcli_get(
- &exec_ctx, &context, detector.pollset, &request,
+ &exec_ctx, &context, &detector.pollent, &request,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
- on_compute_engine_detection_http_response, &detector);
+ grpc_closure_create(on_compute_engine_detection_http_response, &detector),
+ &detector.response);
- grpc_exec_ctx_finish(&exec_ctx);
+ grpc_exec_ctx_flush(&exec_ctx);
/* Block until we get the response. This is not ideal but this should only be
called once for the lifetime of the process by the default credentials. */
gpr_mu_lock(g_polling_mu);
while (!detector.is_done) {
grpc_pollset_worker *worker = NULL;
- grpc_pollset_work(&exec_ctx, detector.pollset, &worker,
- gpr_now(GPR_CLOCK_MONOTONIC),
- gpr_inf_future(GPR_CLOCK_MONOTONIC));
+ if (!GRPC_LOG_IF_ERROR(
+ "pollset_work",
+ grpc_pollset_work(&exec_ctx,
+ grpc_polling_entity_pollset(&detector.pollent),
+ &worker, gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
+ detector.is_done = 1;
+ detector.success = 0;
+ }
}
gpr_mu_unlock(g_polling_mu);
grpc_httpcli_context_destroy(&context);
- grpc_closure_init(&destroy_closure, destroy_pollset, detector.pollset);
- grpc_pollset_shutdown(&exec_ctx, detector.pollset, &destroy_closure);
+ grpc_closure_init(&destroy_closure, destroy_pollset,
+ grpc_polling_entity_pollset(&detector.pollent));
+ grpc_pollset_shutdown(&exec_ctx,
+ grpc_polling_entity_pollset(&detector.pollent),
+ &destroy_closure);
grpc_exec_ctx_finish(&exec_ctx);
g_polling_mu = NULL;
- gpr_free(detector.pollset);
+ gpr_free(grpc_polling_entity_pollset(&detector.pollent));
+ grpc_http_response_destroy(&detector.response);
return detector.success;
}
/* Takes ownership of creds_path if not NULL. */
-static grpc_call_credentials *create_default_creds_from_path(char *creds_path) {
+static grpc_error *create_default_creds_from_path(
+ char *creds_path, grpc_call_credentials **creds) {
grpc_json *json = NULL;
grpc_auth_json_key key;
grpc_auth_refresh_token token;
grpc_call_credentials *result = NULL;
gpr_slice creds_data = gpr_empty_slice();
- int file_ok = 0;
- if (creds_path == NULL) goto end;
- creds_data = gpr_load_file(creds_path, 0, &file_ok);
- if (!file_ok) goto end;
+ grpc_error *error = GRPC_ERROR_NONE;
+ if (creds_path == NULL) {
+ error = GRPC_ERROR_CREATE("creds_path unset");
+ goto end;
+ }
+ error = grpc_load_file(creds_path, 0, &creds_data);
+ if (error != GRPC_ERROR_NONE) {
+ goto end;
+ }
json = grpc_json_parse_string_with_len(
(char *)GPR_SLICE_START_PTR(creds_data), GPR_SLICE_LENGTH(creds_data));
- if (json == NULL) goto end;
+ if (json == NULL) {
+ char *dump = gpr_dump_slice(creds_data, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+ error = grpc_error_set_str(GRPC_ERROR_CREATE("Failed to parse JSON"),
+ GRPC_ERROR_STR_RAW_BYTES, dump);
+ gpr_free(dump);
+ goto end;
+ }
/* First, try an auth json key. */
key = grpc_auth_json_key_create_from_json(json);
@@ -166,6 +197,11 @@ static grpc_call_credentials *create_default_creds_from_path(char *creds_path) {
result =
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
key, grpc_max_auth_token_lifetime());
+ if (result == NULL) {
+ error = GRPC_ERROR_CREATE(
+ "grpc_service_account_jwt_access_credentials_create_from_auth_json_"
+ "key failed");
+ }
goto end;
}
@@ -174,19 +210,28 @@ static grpc_call_credentials *create_default_creds_from_path(char *creds_path) {
if (grpc_auth_refresh_token_is_valid(&token)) {
result =
grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
+ if (result == NULL) {
+ error = GRPC_ERROR_CREATE(
+ "grpc_refresh_token_credentials_create_from_auth_refresh_token "
+ "failed");
+ }
goto end;
}
end:
+ GPR_ASSERT((result == NULL) + (error == GRPC_ERROR_NONE) == 1);
if (creds_path != NULL) gpr_free(creds_path);
gpr_slice_unref(creds_data);
if (json != NULL) grpc_json_destroy(json);
- return result;
+ *creds = result;
+ return error;
}
grpc_channel_credentials *grpc_google_default_credentials_create(void) {
grpc_channel_credentials *result = NULL;
grpc_call_credentials *call_creds = NULL;
+ grpc_error *error = GRPC_ERROR_CREATE("Failed to create Google credentials");
+ grpc_error *err;
GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
@@ -200,14 +245,16 @@ grpc_channel_credentials *grpc_google_default_credentials_create(void) {
}
/* First, try the environment variable. */
- call_creds = create_default_creds_from_path(
- gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR));
- if (call_creds != NULL) goto end;
+ err = create_default_creds_from_path(
+ gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
+ if (err == GRPC_ERROR_NONE) goto end;
+ error = grpc_error_add_child(error, err);
/* Then the well-known file. */
- call_creds = create_default_creds_from_path(
- grpc_get_well_known_google_credentials_file_path());
- if (call_creds != NULL) goto end;
+ err = create_default_creds_from_path(
+ grpc_get_well_known_google_credentials_file_path(), &call_creds);
+ if (err == GRPC_ERROR_NONE) goto end;
+ error = grpc_error_add_child(error, err);
/* At last try to see if we're on compute engine (do the detection only once
since it requires a network test). */
@@ -216,6 +263,10 @@ grpc_channel_credentials *grpc_google_default_credentials_create(void) {
compute_engine_detection_done = 1;
if (need_compute_engine_creds) {
call_creds = grpc_google_compute_engine_credentials_create(NULL);
+ if (call_creds == NULL) {
+ error = grpc_error_add_child(
+ error, GRPC_ERROR_CREATE("Failed to get credentials from network"));
+ }
}
}
@@ -239,6 +290,11 @@ end:
}
}
gpr_mu_unlock(&g_state_mu);
+ if (result == NULL) {
+ GRPC_LOG_IF_ERROR("grpc_google_default_credentials_create", error);
+ } else {
+ GRPC_ERROR_UNREF(error);
+ }
return result;
}
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c
index 89defa7c60..370a384d0e 100644
--- a/src/core/lib/security/credentials/iam/iam_credentials.c
+++ b/src/core/lib/security/credentials/iam/iam_credentials.c
@@ -49,13 +49,13 @@ static void iam_destruct(grpc_call_credentials *creds) {
static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
}
static grpc_call_credentials_vtable iam_vtable = {iam_destruct,
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c
index 8755a96af4..f87ba0ce8d 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.c
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.c
@@ -64,7 +64,7 @@ static void jwt_destruct(grpc_call_credentials *creds) {
static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb,
void *user_data) {
@@ -113,10 +113,11 @@ static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
if (jwt_md != NULL) {
cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
- GRPC_CREDENTIALS_OK);
+ GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_md_store_unref(jwt_md);
} else {
- cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
+ "Could not generate JWT.");
}
}
@@ -149,11 +150,11 @@ grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
"grpc_service_account_jwt_access_credentials_create("
"json_key=%s, "
"token_lifetime="
- "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 5,
- (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec,
- (int)token_lifetime.clock_type, reserved));
+ 5, (json_key, token_lifetime.tv_sec, token_lifetime.tv_nsec,
+ (int)token_lifetime.clock_type, reserved));
GPR_ASSERT(reserved == NULL);
return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
grpc_auth_json_key_create_from_string(json_key), token_lifetime);
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c
index cd6c7ce392..73eb2e3258 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.c
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.c
@@ -37,6 +37,7 @@
#include <string.h>
#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/security/util/b64.h"
#include "src/core/lib/tsi/ssl_types.h"
@@ -44,6 +45,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
#include <openssl/pem.h>
/* --- Utils. --- */
@@ -319,9 +321,15 @@ grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
/* --- verifier_cb_ctx object. --- */
+typedef enum {
+ HTTP_RESPONSE_OPENID = 0,
+ HTTP_RESPONSE_KEYS,
+ HTTP_RESPONSE_COUNT /* must be last */
+} http_response_index;
+
typedef struct {
grpc_jwt_verifier *verifier;
- grpc_pollset *pollset;
+ grpc_polling_entity pollent;
jose_header *header;
grpc_jwt_claims *claims;
char *audience;
@@ -329,6 +337,7 @@ typedef struct {
gpr_slice signed_data;
void *user_data;
grpc_jwt_verification_done_cb user_cb;
+ grpc_http_response responses[HTTP_RESPONSE_COUNT];
} verifier_cb_ctx;
/* Takes ownership of the header, claims and signature. */
@@ -337,10 +346,11 @@ static verifier_cb_ctx *verifier_cb_ctx_create(
grpc_jwt_claims *claims, const char *audience, gpr_slice signature,
const char *signed_jwt, size_t signed_jwt_len, void *user_data,
grpc_jwt_verification_done_cb cb) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
verifier_cb_ctx *ctx = gpr_malloc(sizeof(verifier_cb_ctx));
memset(ctx, 0, sizeof(verifier_cb_ctx));
ctx->verifier = verifier;
- ctx->pollset = pollset;
+ ctx->pollent = grpc_polling_entity_create_from_pollset(pollset);
ctx->header = header;
ctx->audience = gpr_strdup(audience);
ctx->claims = claims;
@@ -348,6 +358,7 @@ static verifier_cb_ctx *verifier_cb_ctx_create(
ctx->signed_data = gpr_slice_from_copied_buffer(signed_jwt, signed_jwt_len);
ctx->user_data = user_data;
ctx->user_cb = cb;
+ grpc_exec_ctx_finish(&exec_ctx);
return ctx;
}
@@ -357,6 +368,9 @@ void verifier_cb_ctx_destroy(verifier_cb_ctx *ctx) {
gpr_slice_unref(ctx->signature);
gpr_slice_unref(ctx->signed_data);
jose_header_destroy(ctx->header);
+ for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) {
+ grpc_http_response_destroy(&ctx->responses[i]);
+ }
/* TODO: see what to do with claims... */
gpr_free(ctx);
}
@@ -571,9 +585,9 @@ end:
}
static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
- const grpc_httpcli_response *response) {
- grpc_json *json = json_from_http(response);
+ grpc_error *error) {
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
+ grpc_json *json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]);
EVP_PKEY *verification_key = NULL;
grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
grpc_jwt_claims *claims = NULL;
@@ -612,10 +626,11 @@ end:
}
static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
- const grpc_httpcli_response *response) {
+ grpc_error *error) {
const grpc_json *cur;
- grpc_json *json = json_from_http(response);
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
+ const grpc_http_response *response = &ctx->responses[HTTP_RESPONSE_OPENID];
+ grpc_json *json = json_from_http(response);
grpc_httpcli_request req;
const char *jwks_uri;
@@ -641,10 +656,12 @@ static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
} else {
*(req.host + (req.http.path - jwks_uri)) = '\0';
}
+
grpc_httpcli_get(
- exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req,
+ exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
- on_keys_retrieved, ctx);
+ grpc_closure_create(on_keys_retrieved, ctx),
+ &ctx->responses[HTTP_RESPONSE_KEYS]);
grpc_json_destroy(json);
gpr_free(req.host);
return;
@@ -686,12 +703,13 @@ static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain,
static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
verifier_cb_ctx *ctx) {
const char *at_sign;
- grpc_httpcli_response_cb http_cb;
+ grpc_closure *http_cb;
char *path_prefix = NULL;
const char *iss;
grpc_httpcli_request req;
memset(&req, 0, sizeof(grpc_httpcli_request));
req.handshaker = &grpc_httpcli_ssl;
+ http_response_index rsp_idx;
GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL);
iss = ctx->claims->iss;
@@ -730,7 +748,8 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
*(path_prefix++) = '\0';
gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
}
- http_cb = on_keys_retrieved;
+ http_cb = grpc_closure_create(on_keys_retrieved, ctx);
+ rsp_idx = HTTP_RESPONSE_KEYS;
} else {
req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
path_prefix = strchr(req.host, '/');
@@ -741,13 +760,14 @@ static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
GRPC_OPENID_CONFIG_URL_SUFFIX);
}
- http_cb = on_openid_config_retrieved;
+ http_cb = grpc_closure_create(on_openid_config_retrieved, ctx);
+ rsp_idx = HTTP_RESPONSE_OPENID;
}
grpc_httpcli_get(
- exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req,
+ exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
- http_cb, ctx);
+ http_cb, &ctx->responses[rsp_idx]);
gpr_free(req.host);
gpr_free(req.http.path);
return;
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
index 973c6e1d17..c22ea5c468 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
@@ -216,9 +216,9 @@ end:
return status;
}
-static void on_oauth2_token_fetcher_http_response(
- grpc_exec_ctx *exec_ctx, void *user_data,
- const grpc_http_response *response) {
+static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
+ void *user_data,
+ grpc_error *error) {
grpc_credentials_metadata_request *r =
(grpc_credentials_metadata_request *)user_data;
grpc_oauth2_token_fetcher_credentials *c =
@@ -226,17 +226,20 @@ static void on_oauth2_token_fetcher_http_response(
gpr_timespec token_lifetime;
grpc_credentials_status status;
+ GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error));
+
gpr_mu_lock(&c->mu);
status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
- response, &c->access_token_md, &token_lifetime);
+ &r->response, &c->access_token_md, &token_lifetime);
if (status == GRPC_CREDENTIALS_OK) {
c->token_expiration =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
r->cb(exec_ctx, r->user_data, c->access_token_md->entries,
- c->access_token_md->num_entries, status);
+ c->access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL);
} else {
c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
- r->cb(exec_ctx, r->user_data, NULL, 0, status);
+ r->cb(exec_ctx, r->user_data, NULL, 0, status,
+ "Error occured when fetching oauth2 token.");
}
gpr_mu_unlock(&c->mu);
grpc_credentials_metadata_request_destroy(r);
@@ -244,7 +247,7 @@ static void on_oauth2_token_fetcher_http_response(
static void oauth2_token_fetcher_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb, void *user_data) {
grpc_oauth2_token_fetcher_credentials *c =
(grpc_oauth2_token_fetcher_credentials *)creds;
@@ -264,13 +267,13 @@ static void oauth2_token_fetcher_get_request_metadata(
}
if (cached_access_token_md != NULL) {
cb(exec_ctx, user_data, cached_access_token_md->entries,
- cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK);
+ cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL);
grpc_credentials_md_store_unref(cached_access_token_md);
} else {
c->fetch_func(
exec_ctx,
grpc_credentials_metadata_request_create(creds, cb, user_data),
- &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response,
+ &c->httpcli_context, pollent, on_oauth2_token_fetcher_http_response,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold));
}
}
@@ -295,8 +298,8 @@ static grpc_call_credentials_vtable compute_engine_vtable = {
static void compute_engine_fetch_oauth2(
grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
- grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
- grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
+ grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
+ grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
grpc_http_header header = {"Metadata-Flavor", "Google"};
grpc_httpcli_request request;
memset(&request, 0, sizeof(grpc_httpcli_request));
@@ -304,8 +307,9 @@ static void compute_engine_fetch_oauth2(
request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
request.http.hdr_count = 1;
request.http.hdrs = &header;
- grpc_httpcli_get(exec_ctx, httpcli_context, pollset, &request, deadline,
- response_cb, metadata_req);
+ grpc_httpcli_get(exec_ctx, httpcli_context, pollent, &request, deadline,
+ grpc_closure_create(response_cb, metadata_req),
+ &metadata_req->response);
}
grpc_call_credentials *grpc_google_compute_engine_credentials_create(
@@ -336,8 +340,8 @@ static grpc_call_credentials_vtable refresh_token_vtable = {
static void refresh_token_fetch_oauth2(
grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
- grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
- grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
+ grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
+ grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
grpc_google_refresh_token_credentials *c =
(grpc_google_refresh_token_credentials *)metadata_req->creds;
grpc_http_header header = {"Content-Type",
@@ -353,8 +357,10 @@ static void refresh_token_fetch_oauth2(
request.http.hdr_count = 1;
request.http.hdrs = &header;
request.handshaker = &grpc_httpcli_ssl;
- grpc_httpcli_post(exec_ctx, httpcli_context, pollset, &request, body,
- strlen(body), deadline, response_cb, metadata_req);
+ grpc_httpcli_post(exec_ctx, httpcli_context, pollent, &request, body,
+ strlen(body), deadline,
+ grpc_closure_create(response_cb, metadata_req),
+ &metadata_req->response);
gpr_free(body);
}
@@ -396,10 +402,11 @@ static void access_token_destruct(grpc_call_credentials *creds) {
static void access_token_get_request_metadata(
grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
- grpc_pollset *pollset, grpc_auth_metadata_context context,
+ grpc_polling_entity *pollent, grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb, void *user_data) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
- cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK,
+ NULL);
}
static grpc_call_credentials_vtable access_token_vtable = {
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
index 04915b333f..7f6f205c22 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -70,8 +70,8 @@ void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token);
typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx,
grpc_credentials_metadata_request *req,
grpc_httpcli_context *http_context,
- grpc_pollset *pollset,
- grpc_httpcli_response_cb response_cb,
+ grpc_polling_entity *pollent,
+ grpc_iomgr_cb_func cb,
gpr_timespec deadline);
typedef struct {
grpc_call_credentials base;
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index bae357321e..824ff081dc 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -67,7 +67,8 @@ static void plugin_md_request_metadata_ready(void *request,
gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s",
error_details);
}
- r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+ r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR,
+ error_details);
} else {
size_t i;
grpc_credentials_md *md_array = NULL;
@@ -79,7 +80,7 @@ static void plugin_md_request_metadata_ready(void *request,
gpr_slice_from_copied_buffer(md[i].value, md[i].value_length);
}
}
- r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK);
+ r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK, NULL);
if (md_array != NULL) {
for (i = 0; i < num_md; i++) {
gpr_slice_unref(md_array[i].key);
@@ -94,7 +95,7 @@ static void plugin_md_request_metadata_ready(void *request,
static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_credentials *creds,
- grpc_pollset *pollset,
+ grpc_polling_entity *pollent,
grpc_auth_metadata_context context,
grpc_credentials_metadata_cb cb,
void *user_data) {
@@ -107,7 +108,7 @@ static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
c->plugin.get_metadata(c->plugin.state, context,
plugin_md_request_metadata_ready, request);
} else {
- cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+ cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL);
}
}
diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
index e3cbcb4433..14ccf72dc9 100644
--- a/src/core/lib/security/transport/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -54,11 +54,11 @@ typedef struct {
grpc_call_credentials *creds;
grpc_mdstr *host;
grpc_mdstr *method;
- /* pollset bound to this call; if we need to make external
- network requests, they should be done under this pollset
- so that work can progress when this call wants work to
- progress */
- grpc_pollset *pollset;
+ /* pollset{_set} bound to this call; if we need to make external
+ network requests, they should be done under a pollset added to this
+ pollset_set so that work can progress when this call wants work to progress
+ */
+ grpc_polling_entity *pollent;
grpc_transport_stream_op op;
uint8_t security_context_set;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
@@ -91,14 +91,16 @@ static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_status_code status, const char *error_msg) {
call_data *calld = elem->call_data;
gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
- grpc_transport_stream_op_add_cancellation(&calld->op, status);
+ gpr_slice error_slice = gpr_slice_from_copied_string(error_msg);
+ grpc_transport_stream_op_add_close(&calld->op, status, &error_slice);
grpc_call_next_op(exec_ctx, elem, &calld->op);
}
static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
grpc_credentials_md *md_elems,
size_t num_md,
- grpc_credentials_status status) {
+ grpc_credentials_status status,
+ const char *error_details) {
grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data;
grpc_transport_stream_op *op = &calld->op;
@@ -107,7 +109,9 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
reset_auth_metadata_context(&calld->auth_md_context);
if (status != GRPC_CREDENTIALS_OK) {
bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED,
- "Credentials failed to get metadata.");
+ (error_details != NULL && strlen(error_details) > 0)
+ ? error_details
+ : "Credentials failed to get metadata.");
return;
}
GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
@@ -184,9 +188,9 @@ static void send_security_metadata(grpc_exec_ctx *exec_ctx,
build_auth_metadata_context(&chand->security_connector->base,
chand->auth_context, calld);
calld->op = *op; /* Copy op (originates from the caller's stack). */
- GPR_ASSERT(calld->pollset);
+ GPR_ASSERT(calld->pollent != NULL);
grpc_call_credentials_get_request_metadata(
- exec_ctx, calld->creds, calld->pollset, calld->auth_md_context,
+ exec_ctx, calld->creds, calld->pollent, calld->auth_md_context,
on_credentials_metadata, elem);
}
@@ -220,8 +224,7 @@ static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_linked_mdelem *l;
grpc_client_security_context *sec_ctx = NULL;
- if (calld->security_context_set == 0 &&
- op->cancel_with_status == GRPC_STATUS_OK) {
+ if (calld->security_context_set == 0 && op->cancel_error == GRPC_ERROR_NONE) {
calld->security_context_set = 1;
GPR_ASSERT(op->context);
if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
@@ -270,15 +273,16 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
memset(calld, 0, sizeof(*calld));
}
-static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- grpc_pollset *pollset) {
+static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_polling_entity *pollent) {
call_data *calld = elem->call_data;
- calld->pollset = pollset;
+ calld->pollent = pollent;
}
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {
+ const grpc_call_stats *stats, void *ignored) {
call_data *calld = elem->call_data;
grpc_call_credentials_unref(calld->creds);
if (calld->host != NULL) {
@@ -329,8 +333,14 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "client_auth_filter");
}
-const grpc_channel_filter grpc_client_auth_filter = {
- auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
- init_call_elem, set_pollset, destroy_call_elem,
- sizeof(channel_data), init_channel_elem, destroy_channel_elem,
- grpc_call_next_get_peer, "client-auth"};
+const grpc_channel_filter grpc_client_auth_filter = {auth_start_transport_op,
+ grpc_channel_next_op,
+ sizeof(call_data),
+ init_call_elem,
+ set_pollset_or_pollset_set,
+ destroy_call_elem,
+ sizeof(channel_data),
+ init_channel_elem,
+ destroy_channel_elem,
+ grpc_call_next_get_peer,
+ "client-auth"};
diff --git a/src/core/lib/security/transport/handshake.c b/src/core/lib/security/transport/handshake.c
index 6561f4b47d..b374ca7633 100644
--- a/src/core/lib/security/transport/handshake.c
+++ b/src/core/lib/security/transport/handshake.c
@@ -39,8 +39,10 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/tsi_error.h"
#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
@@ -60,13 +62,16 @@ typedef struct {
grpc_closure on_handshake_data_sent_to_peer;
grpc_closure on_handshake_data_received_from_peer;
grpc_auth_context *auth_context;
+ grpc_timer timer;
+ gpr_refcount refs;
} grpc_security_handshake;
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
- void *setup, bool success);
+ void *setup,
+ grpc_error *error);
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup,
- bool success);
+ grpc_error *error);
static void security_connector_remove_handshake(grpc_security_handshake *h) {
GPR_ASSERT(!h->is_client_side);
@@ -95,16 +100,34 @@ static void security_connector_remove_handshake(grpc_security_handshake *h) {
gpr_mu_unlock(&sc->mu);
}
+static void unref_handshake(grpc_security_handshake *h) {
+ if (gpr_unref(&h->refs)) {
+ if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker);
+ if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer);
+ gpr_slice_buffer_destroy(&h->left_overs);
+ gpr_slice_buffer_destroy(&h->outgoing);
+ gpr_slice_buffer_destroy(&h->incoming);
+ GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
+ GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake");
+ gpr_free(h);
+ }
+}
+
static void security_handshake_done(grpc_exec_ctx *exec_ctx,
grpc_security_handshake *h,
- int is_success) {
+ grpc_error *error) {
+ grpc_timer_cancel(exec_ctx, &h->timer);
if (!h->is_client_side) {
security_connector_remove_handshake(h);
}
- if (is_success) {
+ if (error == GRPC_ERROR_NONE) {
h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint,
h->auth_context);
} else {
+ const char *msg = grpc_error_string(error);
+ gpr_log(GPR_ERROR, "Security handshake failed: %s", msg);
+ grpc_error_free_string(msg);
+
if (h->secure_endpoint != NULL) {
grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint);
grpc_endpoint_destroy(exec_ctx, h->secure_endpoint);
@@ -113,14 +136,8 @@ static void security_handshake_done(grpc_exec_ctx *exec_ctx,
}
h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL, NULL);
}
- if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker);
- if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer);
- gpr_slice_buffer_destroy(&h->left_overs);
- gpr_slice_buffer_destroy(&h->outgoing);
- gpr_slice_buffer_destroy(&h->incoming);
- GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
- GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake");
- gpr_free(h);
+ unref_handshake(h);
+ GRPC_ERROR_UNREF(error);
}
static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -130,17 +147,20 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data,
tsi_frame_protector *protector;
tsi_result result;
if (status != GRPC_SECURITY_OK) {
- gpr_log(GPR_ERROR, "Error checking peer.");
- security_handshake_done(exec_ctx, h, 0);
+ security_handshake_done(
+ exec_ctx, h,
+ grpc_error_set_int(GRPC_ERROR_CREATE("Error checking peer."),
+ GRPC_ERROR_INT_SECURITY_STATUS, status));
return;
}
h->auth_context = GRPC_AUTH_CONTEXT_REF(auth_context, "handshake");
result =
tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector);
if (result != TSI_OK) {
- gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.",
- tsi_result_to_string(result));
- security_handshake_done(exec_ctx, h, 0);
+ security_handshake_done(
+ exec_ctx, h,
+ grpc_set_tsi_error_result(
+ GRPC_ERROR_CREATE("Frame protector creation failed"), result));
return;
}
h->secure_endpoint =
@@ -148,7 +168,7 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data,
h->left_overs.slices, h->left_overs.count);
h->left_overs.count = 0;
h->left_overs.length = 0;
- security_handshake_done(exec_ctx, h, 1);
+ security_handshake_done(exec_ctx, h, GRPC_ERROR_NONE);
return;
}
@@ -157,9 +177,9 @@ static void check_peer(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h) {
tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer);
if (result != TSI_OK) {
- gpr_log(GPR_ERROR, "Peer extraction failed with error %s",
- tsi_result_to_string(result));
- security_handshake_done(exec_ctx, h, 0);
+ security_handshake_done(
+ exec_ctx, h, grpc_set_tsi_error_result(
+ GRPC_ERROR_CREATE("Peer extraction failed"), result));
return;
}
grpc_security_connector_check_peer(exec_ctx, h->connector, peer,
@@ -185,9 +205,9 @@ static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx,
} while (result == TSI_INCOMPLETE_DATA);
if (result != TSI_OK) {
- gpr_log(GPR_ERROR, "Handshake failed with error %s",
- tsi_result_to_string(result));
- security_handshake_done(exec_ctx, h, 0);
+ security_handshake_done(exec_ctx, h,
+ grpc_set_tsi_error_result(
+ GRPC_ERROR_CREATE("Handshake failed"), result));
return;
}
@@ -203,7 +223,7 @@ static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx,
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
void *handshake,
- bool success) {
+ grpc_error *error) {
grpc_security_handshake *h = handshake;
size_t consumed_slice_size = 0;
tsi_result result = TSI_OK;
@@ -211,9 +231,10 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
size_t num_left_overs;
int has_left_overs_in_current_slice = 0;
- if (!success) {
- gpr_log(GPR_ERROR, "Read failed.");
- security_handshake_done(exec_ctx, h, 0);
+ if (error != GRPC_ERROR_NONE) {
+ security_handshake_done(
+ exec_ctx, h,
+ GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1));
return;
}
@@ -238,9 +259,9 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
}
if (result != TSI_OK) {
- gpr_log(GPR_ERROR, "Handshake failed with error %s",
- tsi_result_to_string(result));
- security_handshake_done(exec_ctx, h, 0);
+ security_handshake_done(exec_ctx, h,
+ grpc_set_tsi_error_result(
+ GRPC_ERROR_CREATE("Handshake failed"), result));
return;
}
@@ -270,13 +291,15 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
/* If handshake is NULL, the handshake is done. */
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
- void *handshake, bool success) {
+ void *handshake, grpc_error *error) {
grpc_security_handshake *h = handshake;
/* Make sure that write is OK. */
- if (!success) {
- gpr_log(GPR_ERROR, "Write failed.");
- if (handshake != NULL) security_handshake_done(exec_ctx, h, 0);
+ if (error != GRPC_ERROR_NONE) {
+ if (handshake != NULL)
+ security_handshake_done(
+ exec_ctx, h,
+ GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
return;
}
@@ -291,13 +314,19 @@ static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
}
}
-void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx,
- tsi_handshaker *handshaker,
- grpc_security_connector *connector,
- bool is_client_side,
- grpc_endpoint *nonsecure_endpoint,
- grpc_security_handshake_done_cb cb,
- void *user_data) {
+static void on_timeout(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+ grpc_security_handshake *h = arg;
+ if (error == GRPC_ERROR_NONE) {
+ grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint);
+ }
+ unref_handshake(h);
+}
+
+void grpc_do_security_handshake(
+ grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
+ grpc_security_connector *connector, bool is_client_side,
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data) {
grpc_security_connector_handshake_list *handshake_node;
grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake));
memset(h, 0, sizeof(grpc_security_handshake));
@@ -309,6 +338,7 @@ void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx,
h->wrapped_endpoint = nonsecure_endpoint;
h->user_data = user_data;
h->cb = cb;
+ gpr_ref_init(&h->refs, 2); /* timer and handshake proper each get a ref */
grpc_closure_init(&h->on_handshake_data_sent_to_peer,
on_handshake_data_sent_to_peer, h);
grpc_closure_init(&h->on_handshake_data_received_from_peer,
@@ -327,6 +357,8 @@ void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx,
gpr_mu_unlock(&server_connector->mu);
}
send_handshake_bytes_to_peer(exec_ctx, h);
+ grpc_timer_init(exec_ctx, &h->timer, deadline, on_timeout, h,
+ gpr_now(deadline.clock_type));
}
void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/security/transport/handshake.h b/src/core/lib/security/transport/handshake.h
index 6ed850b315..c0906dd6af 100644
--- a/src/core/lib/security/transport/handshake.h
+++ b/src/core/lib/security/transport/handshake.h
@@ -38,13 +38,11 @@
#include "src/core/lib/security/transport/security_connector.h"
/* Calls the callback upon completion. Takes owership of handshaker. */
-void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx,
- tsi_handshaker *handshaker,
- grpc_security_connector *connector,
- bool is_client_side,
- grpc_endpoint *nonsecure_endpoint,
- grpc_security_handshake_done_cb cb,
- void *user_data);
+void grpc_do_security_handshake(
+ grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker,
+ grpc_security_connector *connector, bool is_client_side,
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data);
void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake);
diff --git a/src/core/lib/security/transport/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c
index 4438c8e559..7650d68e89 100644
--- a/src/core/lib/security/transport/secure_endpoint.c
+++ b/src/core/lib/security/transport/secure_endpoint.c
@@ -38,6 +38,7 @@
#include <grpc/support/slice_buffer.h>
#include <grpc/support/sync.h>
#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/transport/tsi_error.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/tsi/transport_security_interface.h"
@@ -126,8 +127,8 @@ static void flush_read_staging_buffer(secure_endpoint *ep, uint8_t **cur,
}
static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep,
- bool success) {
- if (grpc_trace_secure_endpoint) {
+ grpc_error *error) {
+ if (false && grpc_trace_secure_endpoint) {
size_t i;
for (i = 0; i < ep->read_buffer->count; i++) {
char *data = gpr_dump_slice(ep->read_buffer->slices[i],
@@ -137,11 +138,12 @@ static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep,
}
}
ep->read_buffer = NULL;
- grpc_exec_ctx_enqueue(exec_ctx, ep->read_cb, success, NULL);
+ grpc_exec_ctx_sched(exec_ctx, ep->read_cb, error, NULL);
SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read");
}
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
+ grpc_error *error) {
unsigned i;
uint8_t keep_looping = 0;
tsi_result result = TSI_OK;
@@ -149,9 +151,10 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
uint8_t *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer);
uint8_t *end = GPR_SLICE_END_PTR(ep->read_staging_buffer);
- if (!success) {
+ if (error != GRPC_ERROR_NONE) {
gpr_slice_buffer_reset_and_unref(ep->read_buffer);
- call_read_cb(exec_ctx, ep, 0);
+ call_read_cb(exec_ctx, ep, GRPC_ERROR_CREATE_REFERENCING(
+ "Secure read failed", &error, 1));
return;
}
@@ -208,11 +211,12 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
if (result != TSI_OK) {
gpr_slice_buffer_reset_and_unref(ep->read_buffer);
- call_read_cb(exec_ctx, ep, 0);
+ call_read_cb(exec_ctx, ep, grpc_set_tsi_error_result(
+ GRPC_ERROR_CREATE("Unwrap failed"), result));
return;
}
- call_read_cb(exec_ctx, ep, 1);
+ call_read_cb(exec_ctx, ep, GRPC_ERROR_NONE);
}
static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
@@ -226,7 +230,7 @@ static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
if (ep->leftover_bytes.count) {
gpr_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer);
GPR_ASSERT(ep->leftover_bytes.count == 0);
- on_read(exec_ctx, ep, 1);
+ on_read(exec_ctx, ep, GRPC_ERROR_NONE);
return;
}
@@ -252,7 +256,7 @@ static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
gpr_slice_buffer_reset_and_unref(&ep->output_buffer);
- if (grpc_trace_secure_endpoint) {
+ if (false && grpc_trace_secure_endpoint) {
for (i = 0; i < slices->count; i++) {
char *data =
gpr_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -315,7 +319,10 @@ static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
if (result != TSI_OK) {
/* TODO(yangg) do different things according to the error type? */
gpr_slice_buffer_reset_and_unref(&ep->output_buffer);
- grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+ grpc_exec_ctx_sched(
+ exec_ctx, cb,
+ grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Wrap failed"), result),
+ NULL);
return;
}
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index 72173e7c9d..f0ee6770e5 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -43,12 +43,12 @@
#include <grpc/support/string_util.h>
#include "src/core/ext/transport/chttp2/alpn/alpn.h"
+#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/context/security_context.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/transport/handshake.h"
#include "src/core/lib/security/transport/secure_endpoint.h"
#include "src/core/lib/support/env.h"
-#include "src/core/lib/support/load_file.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/tsi/fake_transport_security.h"
#include "src/core/lib/tsi/ssl_transport_security.h"
@@ -127,23 +127,25 @@ void grpc_server_security_connector_shutdown(
void grpc_channel_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc,
- grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb,
- void *user_data) {
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data) {
if (sc == NULL || nonsecure_endpoint == NULL) {
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
- sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data);
+ sc->do_handshake(exec_ctx, sc, nonsecure_endpoint, deadline, cb, user_data);
}
}
void grpc_server_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
- grpc_security_handshake_done_cb cb, void *user_data) {
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb,
+ void *user_data) {
if (sc == NULL || nonsecure_endpoint == NULL) {
cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
} else {
- sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, cb, user_data);
+ sc->do_handshake(exec_ctx, sc, acceptor, nonsecure_endpoint, deadline, cb,
+ user_data);
}
}
@@ -310,20 +312,23 @@ static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx,
static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), &sc->base,
- true, nonsecure_endpoint, cb, user_data);
+ true, nonsecure_endpoint, deadline, cb, user_data);
}
static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor,
grpc_endpoint *nonsecure_endpoint,
+ gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), &sc->base,
- false, nonsecure_endpoint, cb, user_data);
+ false, nonsecure_endpoint, deadline, cb,
+ user_data);
}
static grpc_security_connector_vtable fake_channel_vtable = {
@@ -413,6 +418,7 @@ static grpc_security_status ssl_create_handshaker(
static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
grpc_endpoint *nonsecure_endpoint,
+ gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_ssl_channel_security_connector *c =
@@ -427,7 +433,7 @@ static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx,
cb(exec_ctx, user_data, status, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true,
- nonsecure_endpoint, cb, user_data);
+ nonsecure_endpoint, deadline, cb, user_data);
}
}
@@ -435,6 +441,7 @@ static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx,
grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor,
grpc_endpoint *nonsecure_endpoint,
+ gpr_timespec deadline,
grpc_security_handshake_done_cb cb,
void *user_data) {
grpc_ssl_server_security_connector *c =
@@ -446,7 +453,7 @@ static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx,
cb(exec_ctx, user_data, status, NULL, NULL);
} else {
grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false,
- nonsecure_endpoint, cb, user_data);
+ nonsecure_endpoint, deadline, cb, user_data);
}
}
@@ -635,7 +642,8 @@ static gpr_slice compute_default_pem_root_certs_once(void) {
char *default_root_certs_path =
gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
if (default_root_certs_path != NULL) {
- result = gpr_load_file(default_root_certs_path, 0, NULL);
+ GRPC_LOG_IF_ERROR("load_file",
+ grpc_load_file(default_root_certs_path, 0, &result));
gpr_free(default_root_certs_path);
}
@@ -653,7 +661,8 @@ static gpr_slice compute_default_pem_root_certs_once(void) {
/* Fall back to installed certs if needed. */
if (GPR_SLICE_IS_EMPTY(result) &&
ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) {
- result = gpr_load_file(installed_roots_path, 0, NULL);
+ GRPC_LOG_IF_ERROR("load_file",
+ grpc_load_file(installed_roots_path, 0, &result));
}
return result;
}
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index 84e586deaa..c2ddf5ee1e 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -143,7 +143,7 @@ struct grpc_channel_security_connector {
grpc_security_call_host_check_cb cb, void *user_data);
void (*do_handshake)(grpc_exec_ctx *exec_ctx,
grpc_channel_security_connector *sc,
- grpc_endpoint *nonsecure_endpoint,
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
grpc_security_handshake_done_cb cb, void *user_data);
};
@@ -156,8 +156,8 @@ void grpc_channel_security_connector_check_call_host(
/* Handshake. */
void grpc_channel_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
- grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb,
- void *user_data);
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
+ grpc_security_handshake_done_cb cb, void *user_data);
/* --- server_security_connector object. ---
@@ -174,14 +174,14 @@ struct grpc_server_security_connector {
void (*do_handshake)(grpc_exec_ctx *exec_ctx,
grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor,
- grpc_endpoint *nonsecure_endpoint,
+ grpc_endpoint *nonsecure_endpoint, gpr_timespec deadline,
grpc_security_handshake_done_cb cb, void *user_data);
};
void grpc_server_security_connector_do_handshake(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
- grpc_security_handshake_done_cb cb, void *user_data);
+ gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data);
void grpc_server_security_connector_shutdown(
grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector);
diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
index 006a30f0c6..12e789bde9 100644
--- a/src/core/lib/security/transport/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -128,7 +128,7 @@ static void on_md_processing_done(
grpc_metadata_batch_filter(calld->recv_initial_metadata, remove_consumed_md,
elem);
grpc_metadata_array_destroy(&calld->md);
- calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1);
+ grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE, NULL);
} else {
gpr_slice message;
grpc_transport_stream_op close_op;
@@ -146,18 +146,21 @@ static void on_md_processing_done(
calld->transport_op.send_trailing_metadata = NULL;
grpc_transport_stream_op_add_close(&close_op, status, &message);
grpc_call_next_op(&exec_ctx, elem, &close_op);
- calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 0);
+ grpc_exec_ctx_sched(&exec_ctx, calld->on_done_recv,
+ grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
+ GRPC_ERROR_INT_GRPC_STATUS, status),
+ NULL);
}
grpc_exec_ctx_finish(&exec_ctx);
}
static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
- bool success) {
+ grpc_error *error) {
grpc_call_element *elem = user_data;
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
if (chand->creds->processor.process != NULL) {
calld->md = metadata_batch_to_md_array(calld->recv_initial_metadata);
chand->creds->processor.process(
@@ -166,7 +169,8 @@ static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
return;
}
}
- calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+ grpc_exec_ctx_sched(exec_ctx, calld->on_done_recv, GRPC_ERROR_REF(error),
+ NULL);
}
static void set_recv_ops_md_callbacks(grpc_call_element *elem,
@@ -220,12 +224,9 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_server_security_context_destroy;
}
-static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- grpc_pollset *pollset) {}
-
/* Destructor for call_data */
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {}
+ const grpc_call_stats *stats, void *ignored) {}
/* Constructor for channel_data */
static void init_channel_elem(grpc_exec_ctx *exec_ctx,
@@ -258,7 +259,14 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
}
const grpc_channel_filter grpc_server_auth_filter = {
- auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
- init_call_elem, set_pollset, destroy_call_elem,
- sizeof(channel_data), init_channel_elem, destroy_channel_elem,
- grpc_call_next_get_peer, "server-auth"};
+ auth_start_transport_op,
+ grpc_channel_next_op,
+ sizeof(call_data),
+ init_call_elem,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
+ destroy_call_elem,
+ sizeof(channel_data),
+ init_channel_elem,
+ destroy_channel_elem,
+ grpc_call_next_get_peer,
+ "server-auth"};
diff --git a/src/core/lib/security/transport/tsi_error.c b/src/core/lib/security/transport/tsi_error.c
new file mode 100644
index 0000000000..afc1733567
--- /dev/null
+++ b/src/core/lib/security/transport/tsi_error.c
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/transport/tsi_error.h"
+
+grpc_error *grpc_set_tsi_error_result(grpc_error *error, tsi_result result) {
+ return grpc_error_set_int(grpc_error_set_str(error, GRPC_ERROR_STR_TSI_ERROR,
+ tsi_result_to_string(result)),
+ GRPC_ERROR_INT_TSI_CODE, result);
+}
diff --git a/src/core/lib/security/transport/tsi_error.h b/src/core/lib/security/transport/tsi_error.h
new file mode 100644
index 0000000000..636fbb89cf
--- /dev/null
+++ b/src/core/lib/security/transport/tsi_error.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H
+
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/tsi/transport_security_interface.h"
+
+grpc_error *grpc_set_tsi_error_result(grpc_error *error, tsi_result result);
+
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H */
diff --git a/src/core/lib/support/avl.c b/src/core/lib/support/avl.c
index 8d3ce23e6c..acf8fd5a55 100644
--- a/src/core/lib/support/avl.c
+++ b/src/core/lib/support/avl.c
@@ -124,6 +124,15 @@ void *gpr_avl_get(gpr_avl avl, void *key) {
return node ? node->value : NULL;
}
+int gpr_avl_maybe_get(gpr_avl avl, void *key, void **value) {
+ gpr_avl_node *node = get(avl.vtable, avl.root, key);
+ if (node != NULL) {
+ *value = node->value;
+ return 1;
+ }
+ return 0;
+}
+
static gpr_avl_node *rotate_left(const gpr_avl_vtable *vtable, void *key,
void *value, gpr_avl_node *left,
gpr_avl_node *right) {
@@ -286,3 +295,5 @@ gpr_avl gpr_avl_ref(gpr_avl avl) {
}
void gpr_avl_unref(gpr_avl avl) { unref_node(avl.vtable, avl.root); }
+
+int gpr_avl_is_empty(gpr_avl avl) { return avl.root == NULL; }
diff --git a/src/core/lib/support/cpu_windows.c b/src/core/lib/support/cpu_windows.c
index ce32eb0a9d..34d006bfc8 100644
--- a/src/core/lib/support/cpu_windows.c
+++ b/src/core/lib/support/cpu_windows.c
@@ -33,7 +33,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include <grpc/support/log.h>
unsigned gpr_cpu_num_cores(void) {
@@ -44,4 +44,4 @@ unsigned gpr_cpu_num_cores(void) {
unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); }
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/support/env_win32.c b/src/core/lib/support/env_windows.c
index e670e1e8d0..9116959442 100644
--- a/src/core/lib/support/env_win32.c
+++ b/src/core/lib/support/env_windows.c
@@ -33,13 +33,13 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32_ENV
+#ifdef GPR_WINDOWS_ENV
#include <windows.h>
#include "src/core/lib/support/env.h"
#include "src/core/lib/support/string.h"
-#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/string_windows.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@@ -76,4 +76,4 @@ void gpr_setenv(const char *name, const char *value) {
GPR_ASSERT(res);
}
-#endif /* GPR_WIN32_ENV */
+#endif /* GPR_WINDOWS_ENV */
diff --git a/src/core/lib/support/log.c b/src/core/lib/support/log.c
index 882abf673c..bae0957df7 100644
--- a/src/core/lib/support/log.c
+++ b/src/core/lib/support/log.c
@@ -96,4 +96,6 @@ void gpr_log_verbosity_init() {
}
}
-void gpr_set_log_function(gpr_log_func f) { g_log_func = f; }
+void gpr_set_log_function(gpr_log_func f) {
+ g_log_func = f ? f : gpr_default_log;
+}
diff --git a/src/core/lib/support/log_linux.c b/src/core/lib/support/log_linux.c
index ca04c022e3..508fae4eec 100644
--- a/src/core/lib/support/log_linux.c
+++ b/src/core/lib/support/log_linux.c
@@ -95,9 +95,9 @@ void gpr_default_log(gpr_log_func_args *args) {
strcpy(time_buffer, "error:strftime");
}
- gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]",
+ gpr_asprintf(&prefix, "%s%s.%09" PRId32 " %7ld %s:%d]",
gpr_log_severity_string(args->severity), time_buffer,
- (int)(now.tv_nsec), gettid(), display_file, args->line);
+ now.tv_nsec, gettid(), display_file, args->line);
fprintf(stderr, "%-60s %s\n", prefix, args->message);
gpr_free(prefix);
diff --git a/src/core/lib/support/log_win32.c b/src/core/lib/support/log_windows.c
index 29735bd18c..ea898c359d 100644
--- a/src/core/lib/support/log_win32.c
+++ b/src/core/lib/support/log_windows.c
@@ -33,19 +33,19 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32_LOG
+#ifdef GPR_WINDOWS_LOG
#include <stdarg.h>
#include <stdio.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
-#include <grpc/support/log_win32.h>
+#include <grpc/support/log_windows.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include "src/core/lib/support/string.h"
-#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/string_windows.h"
void gpr_log(const char *file, int line, gpr_log_severity severity,
const char *format, ...) {
@@ -109,4 +109,4 @@ void gpr_default_log(gpr_log_func_args *args) {
fflush(stderr);
}
-#endif /* GPR_WIN32_LOG */
+#endif /* GPR_WINDOWS_LOG */
diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c
index a2ab6c5f1f..30c1e67647 100644
--- a/src/core/lib/support/string.c
+++ b/src/core/lib/support/string.c
@@ -194,6 +194,16 @@ int int64_ttoa(int64_t value, char *string) {
return i;
}
+char *gpr_leftpad(const char *str, char flag, size_t length) {
+ const size_t str_length = strlen(str);
+ const size_t out_length = str_length > length ? str_length : length;
+ char *out = gpr_malloc(out_length + 1);
+ memset(out, flag, out_length - str_length);
+ memcpy(out + out_length - str_length, str, str_length);
+ out[out_length] = 0;
+ return out;
+}
+
char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) {
return gpr_strjoin_sep(strs, nstrs, "", final_length);
}
diff --git a/src/core/lib/support/string.h b/src/core/lib/support/string.h
index ea58610914..2b6bb3eec6 100644
--- a/src/core/lib/support/string.h
+++ b/src/core/lib/support/string.h
@@ -83,6 +83,10 @@ int int64_ttoa(int64_t value, char *output);
/* Reverse a run of bytes */
void gpr_reverse_bytes(char *str, int len);
+/* Pad a string with flag characters. The given length specifies the minimum
+ field width. The input string is never truncated. */
+char *gpr_leftpad(const char *str, char flag, size_t length);
+
/* Join a set of strings, returning the resulting string.
Total combined length (excluding null terminator) is returned in total_length
if it is non-null. */
diff --git a/src/core/lib/support/string_util_win32.c b/src/core/lib/support/string_util_windows.c
index 0d7bcdb5aa..049c9a8c04 100644
--- a/src/core/lib/support/string_util_win32.c
+++ b/src/core/lib/support/string_util_windows.c
@@ -35,7 +35,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
/* Some platforms (namely msys) need wchar to be included BEFORE
anything else, especially strsafe.h. */
@@ -91,4 +91,4 @@ char *gpr_format_message(int messageid) {
return message;
}
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/support/string_win32.c b/src/core/lib/support/string_windows.c
index 6b92f79253..ecc2a3a4e5 100644
--- a/src/core/lib/support/string_win32.c
+++ b/src/core/lib/support/string_windows.c
@@ -35,7 +35,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32_STRING
+#ifdef GPR_WINDOWS_STRING
#include <stdarg.h>
#include <stdio.h>
@@ -80,4 +80,4 @@ int gpr_asprintf(char **strp, const char *format, ...) {
return -1;
}
-#endif /* GPR_WIN32_STRING */
+#endif /* GPR_WINDOWS_STRING */
diff --git a/src/core/lib/support/string_win32.h b/src/core/lib/support/string_windows.h
index ff4a694ca9..899563b72d 100644
--- a/src/core/lib/support/string_win32.h
+++ b/src/core/lib/support/string_windows.h
@@ -31,17 +31,17 @@
*
*/
-#ifndef GRPC_CORE_LIB_SUPPORT_STRING_WIN32_H
-#define GRPC_CORE_LIB_SUPPORT_STRING_WIN32_H
+#ifndef GRPC_CORE_LIB_SUPPORT_STRING_WINDOWS_H
+#define GRPC_CORE_LIB_SUPPORT_STRING_WINDOWS_H
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
/* 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);
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
-#endif /* GRPC_CORE_LIB_SUPPORT_STRING_WIN32_H */
+#endif /* GRPC_CORE_LIB_SUPPORT_STRING_WINDOWS_H */
diff --git a/src/core/lib/support/subprocess_windows.c b/src/core/lib/support/subprocess_windows.c
index 264306f1bd..dee8c44ac1 100644
--- a/src/core/lib/support/subprocess_windows.c
+++ b/src/core/lib/support/subprocess_windows.c
@@ -43,7 +43,7 @@
#include <grpc/support/log.h>
#include <grpc/support/subprocess.h>
#include "src/core/lib/support/string.h"
-#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/string_windows.h"
struct gpr_subprocess {
PROCESS_INFORMATION pi;
diff --git a/src/core/lib/support/sync_win32.c b/src/core/lib/support/sync_windows.c
index 470a9f9704..8f0e8ff69f 100644
--- a/src/core/lib/support/sync_win32.c
+++ b/src/core/lib/support/sync_windows.c
@@ -35,7 +35,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
@@ -130,4 +130,4 @@ void gpr_once_init(gpr_once *once, void (*init_function)(void)) {
InitOnceExecuteOnce(once, run_once_func, &arg, &dummy);
}
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/support/thd_win32.c b/src/core/lib/support/thd_windows.c
index 6deb3140eb..74d2250df4 100644
--- a/src/core/lib/support/thd_win32.c
+++ b/src/core/lib/support/thd_windows.c
@@ -35,7 +35,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32
+#ifdef GPR_WINDOWS
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@@ -114,4 +114,4 @@ void gpr_thd_join(gpr_thd_id t) {
destroy_thread(info);
}
-#endif /* GPR_WIN32 */
+#endif /* GPR_WINDOWS */
diff --git a/src/core/lib/support/time_win32.c b/src/core/lib/support/time_windows.c
index 9e924ab3f4..6459732879 100644
--- a/src/core/lib/support/time_win32.c
+++ b/src/core/lib/support/time_windows.c
@@ -35,7 +35,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32_TIME
+#ifdef GPR_WINDOWS_TIME
#include <grpc/support/log.h>
#include <grpc/support/time.h>
@@ -107,4 +107,4 @@ void gpr_sleep_until(gpr_timespec until) {
}
}
-#endif /* GPR_WIN32_TIME */
+#endif /* GPR_WINDOWS_TIME */
diff --git a/src/core/lib/support/tmpfile_msys.c b/src/core/lib/support/tmpfile_msys.c
index 2fdc89a64f..4f566c4c28 100644
--- a/src/core/lib/support/tmpfile_msys.c
+++ b/src/core/lib/support/tmpfile_msys.c
@@ -44,7 +44,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
-#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/string_windows.h"
#include "src/core/lib/support/tmpfile.h"
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) {
diff --git a/src/core/lib/support/tmpfile_win32.c b/src/core/lib/support/tmpfile_windows.c
index 9ac73128c3..542f53e589 100644
--- a/src/core/lib/support/tmpfile_win32.c
+++ b/src/core/lib/support/tmpfile_windows.c
@@ -33,7 +33,7 @@
#include <grpc/support/port_platform.h>
-#ifdef GPR_WIN32_TMPFILE
+#ifdef GPR_WINDOWS_TMPFILE
#include <io.h>
#include <stdio.h>
@@ -44,7 +44,7 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
-#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/string_windows.h"
#include "src/core/lib/support/tmpfile.h"
FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) {
@@ -81,4 +81,4 @@ end:
return result;
}
-#endif /* GPR_WIN32_TMPFILE */
+#endif /* GPR_WINDOWS_TMPFILE */
diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
index 2cf2f00b31..aa9d60ee6a 100644
--- a/src/core/lib/surface/alarm.c
+++ b/src/core/lib/surface/alarm.c
@@ -48,9 +48,9 @@ struct grpc_alarm {
static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg,
grpc_cq_completion *c) {}
-static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_alarm *alarm = arg;
- grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, success,
+ grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error,
do_nothing_end_completion, NULL, &alarm->completion);
}
diff --git a/src/core/lib/surface/byte_buffer_reader.c b/src/core/lib/surface/byte_buffer_reader.c
index c97079f638..310bacb2c9 100644
--- a/src/core/lib/surface/byte_buffer_reader.c
+++ b/src/core/lib/surface/byte_buffer_reader.c
@@ -54,8 +54,8 @@ static int is_compressed(grpc_byte_buffer *buffer) {
return 1 /* GPR_TRUE */;
}
-void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
- grpc_byte_buffer *buffer) {
+int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
+ grpc_byte_buffer *buffer) {
gpr_slice_buffer decompressed_slices_buffer;
reader->buffer_in = buffer;
switch (reader->buffer_in->type) {
@@ -67,9 +67,10 @@ void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
&decompressed_slices_buffer) == 0) {
gpr_log(GPR_ERROR,
"Unexpected error decompressing data for algorithm with enum "
- "value '%d'. Reading data as if it were uncompressed.",
+ "value '%d'.",
reader->buffer_in->data.raw.compression);
- reader->buffer_out = reader->buffer_in;
+ memset(reader, 0, sizeof(*reader));
+ return 0;
} else { /* all fine */
reader->buffer_out =
grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices,
@@ -82,6 +83,7 @@ void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
reader->current.index = 0;
break;
}
+ return 1;
}
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) {
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index c8728fa278..e5668be47f 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -40,6 +40,7 @@
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h>
@@ -52,7 +53,9 @@
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/transport.h"
/** The maximum number of concurrent batches possible.
Based upon the maximum number of individually queueable ops in the batch
@@ -65,12 +68,6 @@
- status/close recv (depending on client/server) */
#define MAX_CONCURRENT_BATCHES 6
-typedef struct {
- grpc_ioreq_completion_func on_complete;
- void *user_data;
- int success;
-} completed_request;
-
#define MAX_SEND_EXTRA_METADATA_COUNT 3
/* Status data for a request can come from several sources; this
@@ -97,31 +94,13 @@ typedef struct {
grpc_mdstr *details;
} received_status;
-/* How far through the GRPC stream have we read? */
-typedef enum {
- /* We are still waiting for initial metadata to complete */
- READ_STATE_INITIAL = 0,
- /* We have gotten initial metadata, and are reading either
- messages or trailing metadata */
- READ_STATE_GOT_INITIAL_METADATA,
- /* The stream is closed for reading */
- READ_STATE_READ_CLOSED,
- /* The stream is closed for reading & writing */
- READ_STATE_STREAM_CLOSED
-} read_state;
-
-typedef enum {
- WRITE_STATE_INITIAL = 0,
- WRITE_STATE_STARTED,
- WRITE_STATE_WRITE_CLOSED
-} write_state;
-
typedef struct batch_control {
grpc_call *call;
grpc_cq_completion cq_completion;
grpc_closure finish_batch;
void *notify_tag;
gpr_refcount steps_to_complete;
+ grpc_error *error;
uint8_t send_initial_metadata;
uint8_t send_message;
@@ -130,11 +109,11 @@ typedef struct batch_control {
uint8_t recv_message;
uint8_t recv_final_op;
uint8_t is_notify_tag_closure;
- uint8_t success;
} batch_control;
struct grpc_call {
grpc_completion_queue *cq;
+ grpc_polling_entity pollent;
grpc_channel *channel;
grpc_call *parent;
grpc_call *first_child;
@@ -176,10 +155,10 @@ struct grpc_call {
received_status status[STATUS_SOURCE_COUNT];
/* Call stats: only valid after trailing metadata received */
- grpc_transport_stream_stats stats;
+ grpc_call_stats stats;
- /* Compression algorithm for the call */
- grpc_compression_algorithm compression_algorithm;
+ /* Compression algorithm for *incoming* data */
+ grpc_compression_algorithm incoming_compression_algorithm;
/* Supported encodings (compression algorithms), a bitset */
uint32_t encodings_accepted_by_peer;
@@ -238,18 +217,19 @@ static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
grpc_status_code status,
const char *description);
+static grpc_call_error close_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
+ grpc_status_code status,
+ const char *description);
static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack,
- bool success);
+ grpc_error *error);
static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
- bool success);
-
-grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
- uint32_t propagation_mask,
- grpc_completion_queue *cq,
- const void *server_transport_data,
- grpc_mdelem **add_initial_metadata,
- size_t add_initial_metadata_count,
- gpr_timespec send_deadline) {
+ grpc_error *error);
+
+grpc_call *grpc_call_create(
+ grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
+ grpc_completion_queue *cq, grpc_pollset_set *pollset_set_alternative,
+ const void *server_transport_data, grpc_mdelem **add_initial_metadata,
+ size_t add_initial_metadata_count, gpr_timespec send_deadline) {
size_t i, j;
grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel);
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@@ -286,9 +266,20 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
call->context, server_transport_data,
CALL_STACK_FROM_CALL(call));
if (cq != NULL) {
+ GPR_ASSERT(
+ pollset_set_alternative == NULL &&
+ "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL.");
GRPC_CQ_INTERNAL_REF(cq, "bind");
- grpc_call_stack_set_pollset(&exec_ctx, CALL_STACK_FROM_CALL(call),
- grpc_cq_pollset(cq));
+ call->pollent =
+ grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq));
+ }
+ if (pollset_set_alternative != NULL) {
+ call->pollent =
+ grpc_polling_entity_create_from_pollset_set(pollset_set_alternative);
+ }
+ if (!grpc_polling_entity_is_empty(&call->pollent)) {
+ grpc_call_stack_set_pollset_or_pollset_set(
+ &exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
}
if (parent_call != NULL) {
GRPC_CALL_INTERNAL_REF(parent_call, "child");
@@ -343,10 +334,16 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call,
grpc_completion_queue *cq) {
GPR_ASSERT(cq);
+
+ if (grpc_polling_entity_pollset_set(&call->pollent) != NULL) {
+ gpr_log(GPR_ERROR, "A pollset_set is already registered for this call.");
+ abort();
+ }
call->cq = cq;
GRPC_CQ_INTERNAL_REF(cq, "bind");
- grpc_call_stack_set_pollset(exec_ctx, CALL_STACK_FROM_CALL(call),
- grpc_cq_pollset(cq));
+ call->pollent = grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq));
+ grpc_call_stack_set_pollset_or_pollset_set(
+ exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent);
}
#ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -363,7 +360,8 @@ void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) {
GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON);
}
-static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
+static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
+ grpc_error *error) {
size_t i;
int ii;
grpc_call *c = call;
@@ -393,7 +391,7 @@ static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
GRPC_CQ_INTERNAL_UNREF(c->cq, "bind");
}
grpc_channel *channel = c->channel;
- grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), c);
+ grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->stats, c);
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call");
GPR_TIMER_END("destroy_call", 0);
}
@@ -404,25 +402,74 @@ static void set_status_code(grpc_call *call, status_source source,
call->status[source].is_set = 1;
call->status[source].code = (grpc_status_code)status;
+}
+
+static void set_status_details(grpc_call *call, status_source source,
+ grpc_mdstr *status) {
+ if (call->status[source].details != NULL) {
+ GRPC_MDSTR_UNREF(status);
+ } else {
+ call->status[source].details = status;
+ }
+}
+
+static void get_final_status(grpc_call *call,
+ void (*set_value)(grpc_status_code code,
+ void *user_data),
+ void *set_value_user_data) {
+ int i;
+ for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
+ if (call->status[i].is_set) {
+ set_value(call->status[i].code, set_value_user_data);
+ return;
+ }
+ }
+ if (call->is_client) {
+ set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
+ } else {
+ set_value(GRPC_STATUS_OK, set_value_user_data);
+ }
+}
- /* TODO(ctiller): what to do about the flush that was previously here */
+static void set_status_from_error(grpc_call *call, status_source source,
+ grpc_error *error) {
+ intptr_t status;
+ if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &status)) {
+ set_status_code(call, source, (uint32_t)status);
+ } else {
+ set_status_code(call, source, GRPC_STATUS_INTERNAL);
+ }
+ const char *msg = grpc_error_get_str(error, GRPC_ERROR_STR_GRPC_MESSAGE);
+ bool free_msg = false;
+ if (msg == NULL) {
+ free_msg = true;
+ msg = grpc_error_string(error);
+ }
+ set_status_details(call, source, grpc_mdstr_from_string(msg));
+ if (free_msg) grpc_error_free_string(msg);
}
-static void set_compression_algorithm(grpc_call *call,
- grpc_compression_algorithm algo) {
+static void set_incoming_compression_algorithm(
+ grpc_call *call, grpc_compression_algorithm algo) {
GPR_ASSERT(algo < GRPC_COMPRESS_ALGORITHMS_COUNT);
- call->compression_algorithm = algo;
+ call->incoming_compression_algorithm = algo;
}
grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
grpc_call *call) {
grpc_compression_algorithm algorithm;
gpr_mu_lock(&call->mu);
- algorithm = call->compression_algorithm;
+ algorithm = call->incoming_compression_algorithm;
gpr_mu_unlock(&call->mu);
return algorithm;
}
+static grpc_compression_algorithm compression_algorithm_for_level_locked(
+ grpc_call *call, grpc_compression_level level) {
+ return grpc_compression_algorithm_for_level(level,
+ call->encodings_accepted_by_peer);
+}
+
uint32_t grpc_call_test_only_get_message_flags(grpc_call *call) {
uint32_t flags;
gpr_mu_lock(&call->mu);
@@ -488,32 +535,6 @@ uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) {
return encodings_accepted_by_peer;
}
-static void set_status_details(grpc_call *call, status_source source,
- grpc_mdstr *status) {
- if (call->status[source].details != NULL) {
- GRPC_MDSTR_UNREF(call->status[source].details);
- }
- call->status[source].details = status;
-}
-
-static void get_final_status(grpc_call *call,
- void (*set_value)(grpc_status_code code,
- void *user_data),
- void *set_value_user_data) {
- int i;
- for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
- if (call->status[i].is_set) {
- set_value(call->status[i].code, set_value_user_data);
- return;
- }
- }
- if (call->is_client) {
- set_value(GRPC_STATUS_UNKNOWN, set_value_user_data);
- } else {
- set_value(GRPC_STATUS_OK, set_value_user_data);
- }
-}
-
static void get_final_details(grpc_call *call, char **out_details,
size_t *out_details_capacity) {
int i;
@@ -548,15 +569,28 @@ static grpc_linked_mdelem *linked_from_md(grpc_metadata *md) {
return (grpc_linked_mdelem *)&md->internal_data;
}
+static grpc_metadata *get_md_elem(grpc_metadata *metadata,
+ grpc_metadata *additional_metadata, int i,
+ int count) {
+ grpc_metadata *res =
+ i < count ? &metadata[i] : &additional_metadata[i - count];
+ GPR_ASSERT(res);
+ return res;
+}
+
static int prepare_application_metadata(grpc_call *call, int count,
grpc_metadata *metadata,
int is_trailing,
- int prepend_extra_metadata) {
+ int prepend_extra_metadata,
+ grpc_metadata *additional_metadata,
+ int additional_metadata_count) {
+ int total_count = count + additional_metadata_count;
int i;
grpc_metadata_batch *batch =
&call->metadata_batch[0 /* is_receiving */][is_trailing];
- for (i = 0; i < count; i++) {
- grpc_metadata *md = &metadata[i];
+ for (i = 0; i < total_count; i++) {
+ const grpc_metadata *md =
+ get_md_elem(metadata, additional_metadata, i, count);
grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data));
l->md = grpc_mdelem_from_string_and_buffer(
@@ -575,9 +609,10 @@ static int prepare_application_metadata(grpc_call *call, int count,
break;
}
}
- if (i != count) {
+ if (i != total_count) {
for (int j = 0; j <= i; j++) {
- grpc_metadata *md = &metadata[j];
+ const grpc_metadata *md =
+ get_md_elem(metadata, additional_metadata, j, count);
grpc_linked_mdelem *l = (grpc_linked_mdelem *)&md->internal_data;
GRPC_MDELEM_UNREF(l->md);
}
@@ -598,24 +633,36 @@ static int prepare_application_metadata(grpc_call *call, int count,
}
}
}
- for (i = 1; i < count; i++) {
- linked_from_md(&metadata[i])->prev = linked_from_md(&metadata[i - 1]);
+ for (i = 1; i < total_count; i++) {
+ grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count);
+ grpc_metadata *prev_md =
+ get_md_elem(metadata, additional_metadata, i - 1, count);
+ linked_from_md(md)->prev = linked_from_md(prev_md);
}
- for (i = 0; i < count - 1; i++) {
- linked_from_md(&metadata[i])->next = linked_from_md(&metadata[i + 1]);
+ for (i = 0; i < total_count - 1; i++) {
+ grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count);
+ grpc_metadata *next_md =
+ get_md_elem(metadata, additional_metadata, i + 1, count);
+ linked_from_md(md)->next = linked_from_md(next_md);
}
- switch (prepend_extra_metadata * 2 + (count != 0)) {
+
+ switch (prepend_extra_metadata * 2 + (total_count != 0)) {
case 0:
/* no prepend, no metadata => nothing to do */
batch->list.head = batch->list.tail = NULL;
break;
- case 1:
+ case 1: {
/* metadata, but no prepend */
- batch->list.head = linked_from_md(&metadata[0]);
- batch->list.tail = linked_from_md(&metadata[count - 1]);
+ grpc_metadata *first_md =
+ get_md_elem(metadata, additional_metadata, 0, count);
+ grpc_metadata *last_md =
+ get_md_elem(metadata, additional_metadata, total_count - 1, count);
+ batch->list.head = linked_from_md(first_md);
+ batch->list.tail = linked_from_md(last_md);
batch->list.head->prev = NULL;
batch->list.tail->next = NULL;
break;
+ }
case 2:
/* prepend, but no md */
batch->list.head = &call->send_extra_metadata[0];
@@ -624,17 +671,22 @@ static int prepare_application_metadata(grpc_call *call, int count,
batch->list.head->prev = NULL;
batch->list.tail->next = NULL;
break;
- case 3:
+ case 3: {
/* prepend AND md */
+ grpc_metadata *first_md =
+ get_md_elem(metadata, additional_metadata, 0, count);
+ grpc_metadata *last_md =
+ get_md_elem(metadata, additional_metadata, total_count - 1, count);
batch->list.head = &call->send_extra_metadata[0];
call->send_extra_metadata[call->send_extra_metadata_count - 1].next =
- linked_from_md(&metadata[0]);
- linked_from_md(&metadata[0])->prev =
+ linked_from_md(first_md);
+ linked_from_md(first_md)->prev =
&call->send_extra_metadata[call->send_extra_metadata_count - 1];
- batch->list.tail = linked_from_md(&metadata[count - 1]);
+ batch->list.tail = linked_from_md(last_md);
batch->list.head->prev = NULL;
batch->list.tail->next = NULL;
break;
+ }
default:
GPR_UNREACHABLE_CODE(return 0);
}
@@ -703,48 +755,98 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call *c,
return r;
}
-typedef struct cancel_closure {
+typedef struct termination_closure {
grpc_closure closure;
grpc_call *call;
- grpc_status_code status;
-} cancel_closure;
+ grpc_error *error;
+ grpc_closure *op_closure;
+ enum { TC_CANCEL, TC_CLOSE } type;
+} termination_closure;
+
+static void done_termination(grpc_exec_ctx *exec_ctx, void *tcp,
+ grpc_error *error) {
+ termination_closure *tc = tcp;
+ switch (tc->type) {
+ case TC_CANCEL:
+ GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "cancel");
+ break;
+ case TC_CLOSE:
+ GRPC_CALL_INTERNAL_UNREF(exec_ctx, tc->call, "close");
+ break;
+ }
+ GRPC_ERROR_UNREF(tc->error);
+ grpc_exec_ctx_sched(exec_ctx, tc->op_closure, GRPC_ERROR_NONE, NULL);
+ gpr_free(tc);
+}
-static void done_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) {
- cancel_closure *cc = ccp;
- GRPC_CALL_INTERNAL_UNREF(exec_ctx, cc->call, "cancel");
- gpr_free(cc);
+static void send_cancel(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
+ grpc_transport_stream_op op;
+ termination_closure *tc = tcp;
+ memset(&op, 0, sizeof(op));
+ op.cancel_error = tc->error;
+ /* reuse closure to catch completion */
+ grpc_closure_init(&tc->closure, done_termination, tc);
+ op.on_complete = &tc->closure;
+ execute_op(exec_ctx, tc->call, &op);
}
-static void send_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) {
+static void send_close(grpc_exec_ctx *exec_ctx, void *tcp, grpc_error *error) {
grpc_transport_stream_op op;
- cancel_closure *cc = ccp;
+ termination_closure *tc = tcp;
memset(&op, 0, sizeof(op));
- op.cancel_with_status = cc->status;
+ op.close_error = tc->error;
/* reuse closure to catch completion */
- grpc_closure_init(&cc->closure, done_cancel, cc);
- op.on_complete = &cc->closure;
- execute_op(exec_ctx, cc->call, &op);
+ grpc_closure_init(&tc->closure, done_termination, tc);
+ tc->op_closure = op.on_complete;
+ op.on_complete = &tc->closure;
+ execute_op(exec_ctx, tc->call, &op);
+}
+
+static grpc_call_error terminate_with_status(grpc_exec_ctx *exec_ctx,
+ termination_closure *tc) {
+ set_status_from_error(tc->call, STATUS_FROM_API_OVERRIDE, tc->error);
+
+ if (tc->type == TC_CANCEL) {
+ grpc_closure_init(&tc->closure, send_cancel, tc);
+ GRPC_CALL_INTERNAL_REF(tc->call, "cancel");
+ } else if (tc->type == TC_CLOSE) {
+ grpc_closure_init(&tc->closure, send_close, tc);
+ GRPC_CALL_INTERNAL_REF(tc->call, "close");
+ }
+ grpc_exec_ctx_sched(exec_ctx, &tc->closure, GRPC_ERROR_NONE, NULL);
+ return GRPC_CALL_OK;
}
static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
grpc_status_code status,
const char *description) {
- grpc_mdstr *details =
- description ? grpc_mdstr_from_string(description) : NULL;
- cancel_closure *cc = gpr_malloc(sizeof(*cc));
-
GPR_ASSERT(status != GRPC_STATUS_OK);
+ termination_closure *tc = gpr_malloc(sizeof(*tc));
+ memset(tc, 0, sizeof(termination_closure));
+ tc->type = TC_CANCEL;
+ tc->call = c;
+ tc->error = grpc_error_set_int(
+ grpc_error_set_str(GRPC_ERROR_CREATE(description),
+ GRPC_ERROR_STR_GRPC_MESSAGE, description),
+ GRPC_ERROR_INT_GRPC_STATUS, status);
+
+ return terminate_with_status(exec_ctx, tc);
+}
- set_status_code(c, STATUS_FROM_API_OVERRIDE, (uint32_t)status);
- set_status_details(c, STATUS_FROM_API_OVERRIDE, details);
-
- grpc_closure_init(&cc->closure, send_cancel, cc);
- cc->call = c;
- cc->status = status;
- GRPC_CALL_INTERNAL_REF(c, "cancel");
- grpc_exec_ctx_enqueue(exec_ctx, &cc->closure, true, NULL);
-
- return GRPC_CALL_OK;
+static grpc_call_error close_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
+ grpc_status_code status,
+ const char *description) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
+ termination_closure *tc = gpr_malloc(sizeof(*tc));
+ memset(tc, 0, sizeof(termination_closure));
+ tc->type = TC_CLOSE;
+ tc->call = c;
+ tc->error = grpc_error_set_int(
+ grpc_error_set_str(GRPC_ERROR_CREATE(description),
+ GRPC_ERROR_STR_GRPC_MESSAGE, description),
+ GRPC_ERROR_INT_GRPC_STATUS, status);
+
+ return terminate_with_status(exec_ctx, tc);
}
static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
@@ -778,11 +880,11 @@ grpc_call *grpc_call_from_top_element(grpc_call_element *elem) {
return CALL_FROM_TOP_ELEM(elem);
}
-static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_call *call = arg;
gpr_mu_lock(&call->mu);
call->have_alarm = 0;
- if (success) {
+ if (error != GRPC_ERROR_CANCELLED) {
cancel_with_status(exec_ctx, call, GRPC_STATUS_DEADLINE_EXCEEDED,
"Deadline Exceeded");
}
@@ -885,9 +987,9 @@ static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) {
if (elem == NULL) {
return NULL;
} else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) {
- GPR_TIMER_BEGIN("compression_algorithm", 0);
- set_compression_algorithm(call, decode_compression(elem));
- GPR_TIMER_END("compression_algorithm", 0);
+ GPR_TIMER_BEGIN("incoming_compression_algorithm", 0);
+ set_incoming_compression_algorithm(call, decode_compression(elem));
+ GPR_TIMER_END("incoming_compression_algorithm", 0);
return NULL;
} else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) {
GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0);
@@ -968,7 +1070,8 @@ static void post_batch_completion(grpc_exec_ctx *exec_ctx,
batch_control *bctl) {
grpc_call *call = bctl->call;
if (bctl->is_notify_tag_closure) {
- grpc_exec_ctx_enqueue(exec_ctx, bctl->notify_tag, bctl->success, NULL);
+ /* unrefs bctl->error */
+ grpc_exec_ctx_sched(exec_ctx, bctl->notify_tag, bctl->error, NULL);
gpr_mu_lock(&call->mu);
bctl->call->used_batches =
(uint8_t)(bctl->call->used_batches &
@@ -976,7 +1079,8 @@ static void post_batch_completion(grpc_exec_ctx *exec_ctx,
gpr_mu_unlock(&call->mu);
GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion");
} else {
- grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, bctl->success,
+ /* unrefs bctl->error */
+ grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, bctl->error,
finish_batch_completion, bctl, &bctl->cq_completion);
}
}
@@ -1008,15 +1112,18 @@ static void continue_receiving_slices(grpc_exec_ctx *exec_ctx,
}
static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
- bool success) {
+ grpc_error *error) {
batch_control *bctl = bctlp;
grpc_call *call = bctl->call;
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer,
call->receiving_slice);
continue_receiving_slices(exec_ctx, bctl);
} else {
+ if (grpc_trace_operation_failures) {
+ GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error));
+ }
grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
call->receiving_stream = NULL;
grpc_byte_buffer_destroy(*call->receiving_buffer);
@@ -1050,9 +1157,9 @@ static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl,
} else {
call->test_only_last_message_flags = call->receiving_stream->flags;
if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) &&
- (call->compression_algorithm > GRPC_COMPRESS_NONE)) {
+ (call->incoming_compression_algorithm > GRPC_COMPRESS_NONE)) {
*call->receiving_buffer = grpc_raw_compressed_byte_buffer_create(
- NULL, 0, call->compression_algorithm);
+ NULL, 0, call->incoming_compression_algorithm);
} else {
*call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0);
}
@@ -1065,53 +1172,89 @@ static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl,
}
static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
- bool success) {
+ grpc_error *error) {
batch_control *bctl = bctlp;
grpc_call *call = bctl->call;
gpr_mu_lock(&bctl->call->mu);
- if (bctl->call->has_initial_md_been_received || !success ||
+ if (bctl->call->has_initial_md_been_received || error != GRPC_ERROR_NONE ||
call->receiving_stream == NULL) {
gpr_mu_unlock(&bctl->call->mu);
- process_data_after_md(exec_ctx, bctlp, success);
+ process_data_after_md(exec_ctx, bctlp, error);
} else {
call->saved_receiving_stream_ready_bctlp = bctlp;
gpr_mu_unlock(&bctl->call->mu);
}
}
+static void validate_filtered_metadata(grpc_exec_ctx *exec_ctx,
+ batch_control *bctl) {
+ grpc_call *call = bctl->call;
+ /* validate call->incoming_compression_algorithm */
+ if (call->incoming_compression_algorithm != GRPC_COMPRESS_NONE) {
+ const grpc_compression_algorithm algo =
+ call->incoming_compression_algorithm;
+ char *error_msg = NULL;
+ const grpc_compression_options compression_options =
+ grpc_channel_compression_options(call->channel);
+ /* check if algorithm is known */
+ if (algo >= GRPC_COMPRESS_ALGORITHMS_COUNT) {
+ gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.",
+ algo);
+ gpr_log(GPR_ERROR, "%s", error_msg);
+ close_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+ } else if (grpc_compression_options_is_algorithm_enabled(
+ &compression_options, algo) == 0) {
+ /* check if algorithm is supported by current channel config */
+ char *algo_name = NULL;
+ grpc_compression_algorithm_name(algo, &algo_name);
+ gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.",
+ algo_name);
+ gpr_log(GPR_ERROR, "%s", error_msg);
+ close_with_status(exec_ctx, call, GRPC_STATUS_UNIMPLEMENTED, error_msg);
+ } else {
+ call->incoming_compression_algorithm = algo;
+ }
+ gpr_free(error_msg);
+ }
+
+ /* make sure the received grpc-encoding is amongst the ones listed in
+ * grpc-accept-encoding */
+ GPR_ASSERT(call->encodings_accepted_by_peer != 0);
+ if (!GPR_BITGET(call->encodings_accepted_by_peer,
+ call->incoming_compression_algorithm)) {
+ extern int grpc_compression_trace;
+ if (grpc_compression_trace) {
+ char *algo_name = NULL;
+ grpc_compression_algorithm_name(call->incoming_compression_algorithm,
+ &algo_name);
+ gpr_log(GPR_ERROR,
+ "Compression algorithm (grpc-encoding = '%s') not present in "
+ "the bitset of accepted encodings (grpc-accept-encodings: "
+ "'0x%x')",
+ algo_name, call->encodings_accepted_by_peer);
+ }
+ }
+}
+
static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
- void *bctlp, bool success) {
+ void *bctlp, grpc_error *error) {
batch_control *bctl = bctlp;
grpc_call *call = bctl->call;
gpr_mu_lock(&call->mu);
- if (!success) {
- bctl->success = false;
+ if (error != GRPC_ERROR_NONE) {
+ bctl->error = GRPC_ERROR_REF(error);
} else {
grpc_metadata_batch *md =
&call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
grpc_metadata_batch_filter(md, recv_initial_filter, call);
- /* make sure the received grpc-encoding is amongst the ones listed in
- * grpc-accept-encoding */
-
- GPR_ASSERT(call->encodings_accepted_by_peer != 0);
- if (!GPR_BITGET(call->encodings_accepted_by_peer,
- call->compression_algorithm)) {
- extern int grpc_compression_trace;
- if (grpc_compression_trace) {
- char *algo_name;
- grpc_compression_algorithm_name(call->compression_algorithm,
- &algo_name);
- gpr_log(GPR_ERROR,
- "Compression algorithm (grpc-encoding = '%s') not present in "
- "the bitset of accepted encodings (grpc-accept-encodings: "
- "'0x%x')",
- algo_name, call->encodings_accepted_by_peer);
- }
- }
+ GPR_TIMER_BEGIN("validate_filtered_metadata", 0);
+ validate_filtered_metadata(exec_ctx, bctl);
+ GPR_TIMER_END("validate_filtered_metadata", 0);
+
if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) !=
0 &&
!call->is_client) {
@@ -1126,7 +1269,7 @@ static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
grpc_closure *saved_rsr_closure = grpc_closure_create(
receiving_stream_ready, call->saved_receiving_stream_ready_bctlp);
call->saved_receiving_stream_ready_bctlp = NULL;
- grpc_exec_ctx_enqueue(exec_ctx, saved_rsr_closure, success, NULL);
+ grpc_exec_ctx_sched(exec_ctx, saved_rsr_closure, error, NULL);
}
gpr_mu_unlock(&call->mu);
@@ -1136,15 +1279,18 @@ static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
}
}
-static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, bool success) {
+static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp,
+ grpc_error *error) {
batch_control *bctl = bctlp;
grpc_call *call = bctl->call;
grpc_call *child_call;
grpc_call *next_child_call;
+ GRPC_ERROR_REF(error);
+
gpr_mu_lock(&call->mu);
if (bctl->send_initial_metadata) {
- if (!success) {
+ if (error != GRPC_ERROR_NONE) {
set_status_code(call, STATUS_FROM_CORE, GRPC_STATUS_UNAVAILABLE);
}
grpc_metadata_batch_destroy(
@@ -1190,13 +1336,17 @@ static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, bool success) {
call->final_op.server.cancelled);
}
- success = 1;
+ GRPC_ERROR_UNREF(error);
+ error = GRPC_ERROR_NONE;
}
- bctl->success = success != 0;
+ GRPC_ERROR_UNREF(bctl->error);
+ bctl->error = GRPC_ERROR_REF(error);
gpr_mu_unlock(&call->mu);
if (gpr_unref(&bctl->steps_to_complete)) {
post_batch_completion(exec_ctx, bctl);
}
+
+ GRPC_ERROR_UNREF(error);
}
static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
@@ -1226,7 +1376,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
if (nops == 0) {
GRPC_CALL_INTERNAL_REF(call, "completion");
- bctl->success = 1;
+ bctl->error = GRPC_ERROR_NONE;
if (!is_notify_tag_closure) {
grpc_cq_begin_op(call->cq, notify_tag);
}
@@ -1254,7 +1404,40 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error;
}
- if (op->data.send_initial_metadata.count > INT_MAX) {
+ /* process compression level */
+ grpc_metadata compression_md;
+ memset(&compression_md, 0, sizeof(grpc_metadata));
+ size_t additional_metadata_count = 0;
+ grpc_compression_level effective_compression_level;
+ bool level_set = false;
+ if (op->data.send_initial_metadata.maybe_compression_level.is_set) {
+ effective_compression_level =
+ op->data.send_initial_metadata.maybe_compression_level.level;
+ level_set = true;
+ } else {
+ const grpc_compression_options copts =
+ grpc_channel_compression_options(call->channel);
+ level_set = copts.default_level.is_set;
+ if (level_set) {
+ effective_compression_level = copts.default_level.level;
+ }
+ }
+ if (level_set && !call->is_client) {
+ const grpc_compression_algorithm calgo =
+ compression_algorithm_for_level_locked(
+ call, effective_compression_level);
+ char *calgo_name = NULL;
+ grpc_compression_algorithm_name(calgo, &calgo_name);
+ // the following will be picked up by the compress filter and used as
+ // the call's compression algorithm.
+ compression_md.key = GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY;
+ compression_md.value = calgo_name;
+ compression_md.value_length = strlen(calgo_name);
+ additional_metadata_count++;
+ }
+
+ if (op->data.send_initial_metadata.count + additional_metadata_count >
+ INT_MAX) {
error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error;
}
@@ -1262,7 +1445,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
call->sent_initial_metadata = 1;
if (!prepare_application_metadata(
call, (int)op->data.send_initial_metadata.count,
- op->data.send_initial_metadata.metadata, 0, call->is_client)) {
+ op->data.send_initial_metadata.metadata, 0, call->is_client,
+ &compression_md, (int)additional_metadata_count)) {
error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error;
}
@@ -1350,7 +1534,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
if (!prepare_application_metadata(
call,
(int)op->data.send_status_from_server.trailing_metadata_count,
- op->data.send_status_from_server.trailing_metadata, 1, 1)) {
+ op->data.send_status_from_server.trailing_metadata, 1, 1, NULL,
+ 0)) {
error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error;
}
@@ -1422,7 +1607,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
bctl->recv_final_op = 1;
stream_op.recv_trailing_metadata =
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
- stream_op.collect_stats = &call->stats;
+ stream_op.collect_stats = &call->stats.transport_stream_stats;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
/* Flag validation: currently allow no flags */
@@ -1444,7 +1629,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
bctl->recv_final_op = 1;
stream_op.recv_trailing_metadata =
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
- stream_op.collect_stats = &call->stats;
+ stream_op.collect_stats = &call->stats.transport_stream_stats;
break;
}
}
@@ -1539,9 +1724,10 @@ uint8_t grpc_call_is_client(grpc_call *call) { return call->is_client; }
grpc_compression_algorithm grpc_call_compression_for_level(
grpc_call *call, grpc_compression_level level) {
gpr_mu_lock(&call->mu);
- const uint32_t accepted_encodings = call->encodings_accepted_by_peer;
+ grpc_compression_algorithm algo =
+ compression_algorithm_for_level_locked(call, level);
gpr_mu_unlock(&call->mu);
- return grpc_compression_algorithm_for_level(level, accepted_encodings);
+ return algo;
}
const char *grpc_call_error_to_string(grpc_call_error error) {
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index 2725e060b8..3a78fe3aa3 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -37,7 +37,6 @@
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/surface/api_trace.h"
-#include "src/core/lib/surface/surface_trace.h"
#include <grpc/grpc.h>
#include <grpc/impl/codegen/compression_types.h>
@@ -53,6 +52,8 @@ typedef void (*grpc_ioreq_completion_func)(grpc_exec_ctx *exec_ctx,
grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
uint32_t propagation_mask,
grpc_completion_queue *cq,
+ /* if not NULL, it'll be used in lieu of \a cq */
+ grpc_pollset_set *pollset_set_alternative,
const void *server_transport_data,
grpc_mdelem **add_initial_metadata,
size_t add_initial_metadata_count,
diff --git a/src/core/lib/surface/call_log_batch.c b/src/core/lib/surface/call_log_batch.c
index a6d1d5149f..31c074f15d 100644
--- a/src/core/lib/surface/call_log_batch.c
+++ b/src/core/lib/surface/call_log_batch.c
@@ -112,7 +112,7 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity,
size_t i;
for (i = 0; i < nops; i++) {
tmp = grpc_op_string(&ops[i]);
- gpr_log(file, line, severity, "ops[%d]: %s", i, tmp);
+ gpr_log(file, line, severity, "ops[%" PRIuPTR "]: %s", i, tmp);
gpr_free(tmp);
}
}
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index b6b760b5d8..2cf6d8890a 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -36,16 +36,17 @@
#include <stdlib.h>
#include <string.h>
+#include <grpc/compression.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
+#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/iomgr.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/channel_init.h"
-#include "src/core/lib/surface/init.h"
#include "src/core/lib/transport/static_metadata.h"
/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
@@ -64,10 +65,12 @@ typedef struct registered_call {
struct grpc_channel {
int is_client;
uint32_t max_message_length;
+ grpc_compression_options compression_options;
grpc_mdelem *default_authority;
gpr_mu registered_call_mu;
registered_call *registered_calls;
+
char *target;
};
@@ -80,7 +83,8 @@ struct grpc_channel {
/* the protobuf library will (by default) start warning at 100megs */
#define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024)
-static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, bool success);
+static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error);
grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
const grpc_channel_args *input_args,
@@ -111,6 +115,7 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
channel->registered_calls = NULL;
channel->max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
+ grpc_compression_options_init(&channel->compression_options);
if (args) {
for (size_t i = 0; i < args->num_args; i++) {
if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_MESSAGE_LENGTH)) {
@@ -151,6 +156,27 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
":authority", args->args[i].value.string);
}
}
+ } else if (0 == strcmp(args->args[i].key,
+ GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) {
+ channel->compression_options.default_level.is_set = true;
+ GPR_ASSERT(args->args[i].value.integer >= 0 &&
+ args->args[i].value.integer < GRPC_COMPRESS_LEVEL_COUNT);
+ channel->compression_options.default_level.level =
+ (grpc_compression_level)args->args[i].value.integer;
+ } else if (0 == strcmp(args->args[i].key,
+ GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) {
+ channel->compression_options.default_algorithm.is_set = true;
+ GPR_ASSERT(args->args[i].value.integer >= 0 &&
+ args->args[i].value.integer <
+ GRPC_COMPRESS_ALGORITHMS_COUNT);
+ channel->compression_options.default_algorithm.algorithm =
+ (grpc_compression_algorithm)args->args[i].value.integer;
+ } else if (0 ==
+ strcmp(args->args[i].key,
+ GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) {
+ channel->compression_options.enabled_algorithms_bitset =
+ (uint32_t)args->args[i].value.integer |
+ 0x1; /* always support no compression */
}
}
grpc_channel_args_destroy(args);
@@ -166,12 +192,14 @@ char *grpc_channel_get_target(grpc_channel *channel) {
static grpc_call *grpc_channel_create_call_internal(
grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
- grpc_completion_queue *cq, grpc_mdelem *path_mdelem,
- grpc_mdelem *authority_mdelem, gpr_timespec deadline) {
+ grpc_completion_queue *cq, grpc_pollset_set *pollset_set_alternative,
+ grpc_mdelem *path_mdelem, grpc_mdelem *authority_mdelem,
+ gpr_timespec deadline) {
grpc_mdelem *send_metadata[2];
size_t num_metadata = 0;
GPR_ASSERT(channel->is_client);
+ GPR_ASSERT(!(cq != NULL && pollset_set_alternative != NULL));
send_metadata[num_metadata++] = path_mdelem;
if (authority_mdelem != NULL) {
@@ -180,8 +208,9 @@ static grpc_call *grpc_channel_create_call_internal(
send_metadata[num_metadata++] = GRPC_MDELEM_REF(channel->default_authority);
}
- return grpc_call_create(channel, parent_call, propagation_mask, cq, NULL,
- send_metadata, num_metadata, deadline);
+ return grpc_call_create(channel, parent_call, propagation_mask, cq,
+ pollset_set_alternative, NULL, send_metadata,
+ num_metadata, deadline);
}
grpc_call *grpc_channel_create_call(grpc_channel *channel,
@@ -194,14 +223,30 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
"grpc_channel_create_call("
"channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, "
"host=%s, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 10, (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
- (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ 10,
+ (channel, parent_call, (unsigned)propagation_mask, cq, method, host,
+ deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, reserved));
GPR_ASSERT(!reserved);
return grpc_channel_create_call_internal(
- channel, parent_call, propagation_mask, cq,
+ channel, parent_call, propagation_mask, cq, NULL,
+ grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
+ grpc_mdstr_from_string(method)),
+ host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
+ grpc_mdstr_from_string(host))
+ : NULL,
+ deadline);
+}
+
+grpc_call *grpc_channel_create_pollset_set_call(
+ grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
+ grpc_pollset_set *pollset_set, const char *method, const char *host,
+ gpr_timespec deadline, void *reserved) {
+ GPR_ASSERT(!reserved);
+ return grpc_channel_create_call_internal(
+ channel, parent_call, propagation_mask, NULL, pollset_set,
grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH,
grpc_mdstr_from_string(method)),
host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY,
@@ -238,14 +283,15 @@ grpc_call *grpc_channel_create_registered_call(
"grpc_channel_create_registered_call("
"channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, "
"registered_call_handle=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
9, (channel, parent_call, (unsigned)propagation_mask, completion_queue,
- registered_call_handle, (long long)deadline.tv_sec,
- (int)deadline.tv_nsec, (int)deadline.clock_type, reserved));
+ registered_call_handle, deadline.tv_sec, deadline.tv_nsec,
+ (int)deadline.clock_type, reserved));
GPR_ASSERT(!reserved);
return grpc_channel_create_call_internal(
- channel, parent_call, propagation_mask, completion_queue,
+ channel, parent_call, propagation_mask, completion_queue, NULL,
GRPC_MDELEM_REF(rc->path),
rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline);
}
@@ -267,7 +313,7 @@ void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx,
}
static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
- bool iomgr_success) {
+ grpc_error *error) {
grpc_channel *channel = arg;
grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel));
while (channel->registered_calls) {
@@ -293,7 +339,7 @@ void grpc_channel_destroy(grpc_channel *channel) {
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel));
memset(&op, 0, sizeof(op));
- op.disconnect = 1;
+ op.disconnect_with_error = GRPC_ERROR_CREATE("Channel Destroyed");
elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
elem->filter->start_transport_op(&exec_ctx, elem, &op);
@@ -306,6 +352,11 @@ grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) {
return CHANNEL_STACK_FROM_CHANNEL(channel);
}
+grpc_compression_options grpc_channel_compression_options(
+ const grpc_channel *channel) {
+ return channel->compression_options;
+}
+
grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
char tmp[GPR_LTOA_MIN_BUFSIZE];
switch (i) {
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 22dae930e4..7eff7b8883 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -42,6 +42,11 @@ grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
grpc_channel_stack_type channel_stack_type,
grpc_transport *optional_transport);
+grpc_call *grpc_channel_create_pollset_set_call(
+ grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask,
+ grpc_pollset_set *pollset_set, const char *method, const char *host,
+ gpr_timespec deadline, void *reserved);
+
/** Get a (borrowed) pointer to this channels underlying channel stack */
grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
@@ -71,4 +76,8 @@ void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx,
grpc_channel_internal_unref(exec_ctx, channel)
#endif
+/** Return the channel's compression options. */
+grpc_compression_options grpc_channel_compression_options(
+ const grpc_channel *channel);
+
#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_H */
diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c
index 5a50698695..9818f9d2f2 100644
--- a/src/core/lib/surface/channel_ping.c
+++ b/src/core/lib/surface/channel_ping.c
@@ -53,10 +53,10 @@ static void ping_destroy(grpc_exec_ctx *exec_ctx, void *arg,
gpr_free(arg);
}
-static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
ping_result *pr = arg;
- grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, success, ping_destroy, pr,
- &pr->completion_storage);
+ grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy,
+ pr, &pr->completion_storage);
}
void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq,
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index 5eb7cf1bf4..5978884db8 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -48,7 +48,8 @@
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call.h"
#include "src/core/lib/surface/event_string.h"
-#include "src/core/lib/surface/surface_trace.h"
+
+int grpc_trace_operation_failures;
typedef struct {
grpc_pollset_worker **worker;
@@ -91,8 +92,19 @@ struct grpc_completion_queue {
static gpr_mu g_freelist_mu;
static grpc_completion_queue *g_freelist;
+int grpc_cq_pluck_trace;
+int grpc_cq_event_timeout_trace;
+
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \
+ if (grpc_api_trace && \
+ (grpc_cq_pluck_trace || (event)->type != GRPC_QUEUE_TIMEOUT)) { \
+ char *_ev = grpc_event_string(event); \
+ gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
+ gpr_free(_ev); \
+ }
+
static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cc,
- bool success);
+ grpc_error *error);
void grpc_cq_global_init(void) { gpr_mu_init(&g_freelist_mu); }
@@ -176,7 +188,7 @@ void grpc_cq_internal_ref(grpc_completion_queue *cc) {
}
static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg,
- bool success) {
+ grpc_error *error) {
grpc_completion_queue *cc = arg;
GRPC_CQ_INTERNAL_UNREF(cc, "pollset_destroy");
}
@@ -219,7 +231,7 @@ void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag) {
event, then enter shutdown mode */
/* Queue a GRPC_OP_COMPLETED operation */
void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
- void *tag, int success,
+ void *tag, grpc_error *error,
void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg,
grpc_cq_completion *storage),
void *done_arg, grpc_cq_completion *storage) {
@@ -231,16 +243,24 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
#endif
GPR_TIMER_BEGIN("grpc_cq_end_op", 0);
- GRPC_API_TRACE(
- "grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, success=%d, done=%p, "
- "done_arg=%p, storage=%p)",
- 7, (exec_ctx, cc, tag, success, done, done_arg, storage));
+ if (grpc_api_trace ||
+ (grpc_trace_operation_failures && error != GRPC_ERROR_NONE)) {
+ const char *errmsg = grpc_error_string(error);
+ GRPC_API_TRACE(
+ "grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, error=%s, done=%p, "
+ "done_arg=%p, storage=%p)",
+ 7, (exec_ctx, cc, tag, errmsg, done, done_arg, storage));
+ if (grpc_trace_operation_failures && error != GRPC_ERROR_NONE) {
+ gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
+ }
+ grpc_error_free_string(errmsg);
+ }
storage->tag = tag;
storage->done = done;
storage->done_arg = done_arg;
- storage->next =
- ((uintptr_t)&cc->completed_head) | ((uintptr_t)(success != 0));
+ storage->next = ((uintptr_t)&cc->completed_head) |
+ ((uintptr_t)(error == GRPC_ERROR_NONE));
gpr_mu_lock(cc->mu);
#ifndef NDEBUG
@@ -267,8 +287,15 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
break;
}
}
- grpc_pollset_kick(POLLSET_FROM_CQ(cc), pluck_worker);
+ grpc_error *kick_error =
+ grpc_pollset_kick(POLLSET_FROM_CQ(cc), pluck_worker);
gpr_mu_unlock(cc->mu);
+ if (kick_error != GRPC_ERROR_NONE) {
+ const char *msg = grpc_error_string(kick_error);
+ gpr_log(GPR_ERROR, "Kick failed: %s", msg);
+ grpc_error_free_string(msg);
+ GRPC_ERROR_UNREF(kick_error);
+ }
} else {
cc->completed_tail->next =
((uintptr_t)storage) | (1u & (uintptr_t)cc->completed_tail->next);
@@ -282,6 +309,8 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
}
GPR_TIMER_END("grpc_cq_end_op", 0);
+
+ GRPC_ERROR_UNREF(error);
}
grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
@@ -297,10 +326,11 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
GRPC_API_TRACE(
"grpc_completion_queue_next("
"cc=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
"reserved=%p)",
- 5, (cc, (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ 5, (cc, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type,
+ reserved));
GPR_ASSERT(!reserved);
deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
@@ -347,8 +377,18 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
gpr_mu_lock(cc->mu);
continue;
} else {
- grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
- iteration_deadline);
+ grpc_error *err = grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc),
+ &worker, now, iteration_deadline);
+ if (err != GRPC_ERROR_NONE) {
+ gpr_mu_unlock(cc->mu);
+ const char *msg = grpc_error_string(err);
+ gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
+ grpc_error_free_string(msg);
+ GRPC_ERROR_UNREF(err);
+ memset(&ret, 0, sizeof(ret));
+ ret.type = GRPC_QUEUE_TIMEOUT;
+ break;
+ }
}
}
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
@@ -396,13 +436,16 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0);
- GRPC_API_TRACE(
- "grpc_completion_queue_pluck("
- "cc=%p, tag=%p, "
- "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
- "reserved=%p)",
- 6, (cc, tag, (long long)deadline.tv_sec, (int)deadline.tv_nsec,
- (int)deadline.clock_type, reserved));
+ if (grpc_cq_pluck_trace) {
+ GRPC_API_TRACE(
+ "grpc_completion_queue_pluck("
+ "cc=%p, tag=%p, "
+ "deadline=gpr_timespec { tv_sec: %" PRId64
+ ", tv_nsec: %d, clock_type: %d }, "
+ "reserved=%p)",
+ 6, (cc, tag, deadline.tv_sec, deadline.tv_nsec,
+ (int)deadline.clock_type, reserved));
+ }
GPR_ASSERT(!reserved);
deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
@@ -464,8 +507,19 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
grpc_exec_ctx_flush(&exec_ctx);
gpr_mu_lock(cc->mu);
} else {
- grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
- iteration_deadline);
+ grpc_error *err = grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc),
+ &worker, now, iteration_deadline);
+ if (err != GRPC_ERROR_NONE) {
+ del_plucker(cc, tag, &worker);
+ gpr_mu_unlock(cc->mu);
+ const char *msg = grpc_error_string(err);
+ gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
+ grpc_error_free_string(msg);
+ GRPC_ERROR_UNREF(err);
+ memset(&ret, 0, sizeof(ret));
+ ret.type = GRPC_QUEUE_TIMEOUT;
+ break;
+ }
}
del_plucker(cc, tag, &worker);
}
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index 3d0dd13c53..3049284f68 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -39,6 +39,12 @@
#include <grpc/grpc.h>
#include "src/core/lib/iomgr/pollset.h"
+/* These trace flags default to 1. The corresponding lines are only traced
+ if grpc_api_trace is also truthy */
+extern int grpc_cq_pluck_trace;
+extern int grpc_cq_event_timeout_trace;
+extern int grpc_trace_operation_failures;
+
typedef struct grpc_cq_completion {
/** user supplied tag */
void *tag;
@@ -75,7 +81,7 @@ void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag);
/* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to
grpc_cq_begin_op */
void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
- void *tag, int success,
+ void *tag, grpc_error *error,
void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg,
grpc_cq_completion *storage),
void *done_arg, grpc_cq_completion *storage);
diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
index 1c8b709015..5397913a21 100644
--- a/src/core/lib/surface/init.c
+++ b/src/core/lib/surface/init.c
@@ -57,7 +57,6 @@
#include "src/core/lib/surface/init.h"
#include "src/core/lib/surface/lame_client.h"
#include "src/core/lib/surface/server.h"
-#include "src/core/lib/surface/surface_trace.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/transport_impl.h"
@@ -165,6 +164,13 @@ void grpc_init(void) {
&grpc_trace_channel_stack_builder);
grpc_register_tracer("http1", &grpc_http1_trace);
grpc_register_tracer("compression", &grpc_compression_trace);
+ grpc_register_tracer("queue_pluck", &grpc_cq_pluck_trace);
+ // Default pluck trace to 1
+ grpc_cq_pluck_trace = 1;
+ grpc_register_tracer("queue_timeout", &grpc_cq_event_timeout_trace);
+ // Default timeout trace to 1
+ grpc_cq_event_timeout_trace = 1;
+ grpc_register_tracer("op_failure", &grpc_trace_operation_failures);
grpc_security_pre_init();
grpc_iomgr_init();
grpc_executor_init();
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index f50ec54cea..5ea4cba5d1 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -80,7 +80,8 @@ static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
} else if (op->recv_trailing_metadata != NULL) {
fill_metadata(elem, op->recv_trailing_metadata);
}
- grpc_transport_stream_op_finish_with_failure(exec_ctx, op);
+ grpc_transport_stream_op_finish_with_failure(
+ exec_ctx, op, GRPC_ERROR_CREATE("lame client channel"));
}
static char *lame_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
@@ -91,23 +92,26 @@ static void lame_start_transport_op(grpc_exec_ctx *exec_ctx,
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(
- exec_ctx, op->on_connectivity_state_change->cb_arg, 1);
+ GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_SHUTDOWN);
+ *op->connectivity_state = GRPC_CHANNEL_SHUTDOWN;
+ grpc_exec_ctx_sched(exec_ctx, op->on_connectivity_state_change,
+ GRPC_ERROR_NONE, NULL);
}
if (op->on_consumed != NULL) {
- op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 1);
+ grpc_exec_ctx_sched(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
}
if (op->send_ping != NULL) {
- op->send_ping->cb(exec_ctx, op->send_ping->cb_arg, 0);
+ grpc_exec_ctx_sched(exec_ctx, op->send_ping,
+ GRPC_ERROR_CREATE("lame client channel"), NULL);
}
+ GRPC_ERROR_UNREF(op->disconnect_with_error);
}
static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_call_element_args *args) {}
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ const grpc_call_stats *stats,
void *and_free_memory) {
gpr_free(and_free_memory);
}
@@ -127,7 +131,7 @@ const grpc_channel_filter grpc_lame_filter = {
lame_start_transport_op,
sizeof(call_data),
init_call_elem,
- grpc_call_stack_ignore_set_pollset,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 505b501968..def6e5068b 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -230,9 +230,10 @@ struct grpc_server {
#define SERVER_FROM_CALL_ELEM(elem) \
(((channel_data *)(elem)->channel_data)->server)
-static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld, bool success);
+static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld,
+ grpc_error *error);
static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
- size_t cq_idx, requested_call *rc);
+ size_t cq_idx, requested_call *rc, grpc_error *error);
/* Before calling maybe_finish_shutdown, we must hold mu_global and not
hold mu_call */
static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_server *server);
@@ -263,14 +264,14 @@ struct shutdown_cleanup_args {
};
static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg,
- bool iomgr_status_ignored) {
+ grpc_error *error) {
struct shutdown_cleanup_args *a = arg;
gpr_slice_unref(a->slice);
gpr_free(a);
}
static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
- int send_goaway, int send_disconnect) {
+ int send_goaway, grpc_error *send_disconnect) {
grpc_transport_op op;
struct shutdown_cleanup_args *sc;
grpc_channel_element *elem;
@@ -281,7 +282,7 @@ static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
sc->slice = gpr_slice_from_copied_string("Server shutdown");
op.goaway_message = &sc->slice;
op.goaway_status = GRPC_STATUS_OK;
- op.disconnect = send_disconnect;
+ op.disconnect_with_error = send_disconnect;
grpc_closure_init(&sc->closure, shutdown_cleanup, sc);
op.on_consumed = &sc->closure;
@@ -292,14 +293,16 @@ static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx,
channel_broadcaster *cb,
int send_goaway,
- int force_disconnect) {
+ grpc_error *force_disconnect) {
size_t i;
for (i = 0; i < cb->num_channels; i++) {
- send_shutdown(exec_ctx, cb->channels[i], send_goaway, force_disconnect);
+ send_shutdown(exec_ctx, cb->channels[i], send_goaway,
+ GRPC_ERROR_REF(force_disconnect));
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast");
}
gpr_free(cb->channels);
+ GRPC_ERROR_UNREF(force_disconnect);
}
/*
@@ -325,7 +328,8 @@ static void request_matcher_destroy(request_matcher *rm) {
gpr_free(rm->requests_per_cq);
}
-static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, bool success) {
+static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem,
+ grpc_error *error) {
grpc_call_destroy(grpc_call_from_top_element(elem));
}
@@ -340,20 +344,24 @@ static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx,
grpc_closure_init(
&calld->kill_zombie_closure, kill_zombie,
grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
- grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
+ NULL);
}
}
static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx,
grpc_server *server,
- request_matcher *rm) {
+ request_matcher *rm,
+ grpc_error *error) {
int request_id;
for (size_t i = 0; i < server->cq_count; i++) {
while ((request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[i])) !=
-1) {
- fail_call(exec_ctx, server, i, &server->requested_calls[request_id]);
+ fail_call(exec_ctx, server, i, &server->requested_calls[request_id],
+ GRPC_ERROR_REF(error));
}
}
+ GRPC_ERROR_UNREF(error);
}
/*
@@ -410,7 +418,7 @@ static void orphan_channel(channel_data *chand) {
}
static void finish_destroy_channel(grpc_exec_ctx *exec_ctx, void *cd,
- bool success) {
+ grpc_error *error) {
channel_data *chand = cd;
grpc_server *server = chand->server;
GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server");
@@ -499,25 +507,26 @@ static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
channel_data *chand = elem->channel_data;
server_ref(chand->server);
- grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, true, done_request_event, rc,
- &rc->completion);
+ grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, GRPC_ERROR_NONE,
+ done_request_event, rc, &rc->completion);
}
-static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
grpc_call_element *call_elem = arg;
call_data *calld = call_elem->call_data;
channel_data *chand = call_elem->channel_data;
request_matcher *rm = calld->request_matcher;
grpc_server *server = rm->server;
- if (!success || gpr_atm_acq_load(&server->shutdown_flag)) {
+ if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) {
gpr_mu_lock(&calld->mu_state);
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_closure_init(
&calld->kill_zombie_closure, kill_zombie,
grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
- grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, error, NULL);
return;
}
@@ -562,7 +571,8 @@ static void finish_start_new_rpc(
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
- grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
+ NULL);
return;
}
@@ -570,7 +580,7 @@ static void finish_start_new_rpc(
switch (payload_handling) {
case GRPC_SRM_PAYLOAD_NONE:
- publish_new_rpc(exec_ctx, elem, true);
+ publish_new_rpc(exec_ctx, elem, GRPC_ERROR_NONE);
break;
case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
grpc_op op;
@@ -658,18 +668,21 @@ static int num_channels(grpc_server *server) {
}
static void kill_pending_work_locked(grpc_exec_ctx *exec_ctx,
- grpc_server *server) {
+ grpc_server *server, grpc_error *error) {
if (server->started) {
request_matcher_kill_requests(exec_ctx, server,
- &server->unregistered_request_matcher);
+ &server->unregistered_request_matcher,
+ GRPC_ERROR_REF(error));
request_matcher_zombify_all_pending_calls(
exec_ctx, &server->unregistered_request_matcher);
for (registered_method *rm = server->registered_methods; rm;
rm = rm->next) {
- request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher);
+ request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher,
+ GRPC_ERROR_REF(error));
request_matcher_zombify_all_pending_calls(exec_ctx, &rm->request_matcher);
}
}
+ GRPC_ERROR_UNREF(error);
}
static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
@@ -679,7 +692,8 @@ static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
return;
}
- kill_pending_work_locked(exec_ctx, server);
+ kill_pending_work_locked(exec_ctx, server,
+ GRPC_ERROR_CREATE("Server Shutdown"));
if (server->root_channel_data.next != &server->root_channel_data ||
server->listeners_destroyed < num_listeners(server)) {
@@ -700,7 +714,8 @@ static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
for (i = 0; i < server->num_shutdown_tags; i++) {
server_ref(server);
grpc_cq_end_op(exec_ctx, server->shutdown_tags[i].cq,
- server->shutdown_tags[i].tag, 1, done_shutdown_event, server,
+ server->shutdown_tags[i].tag, GRPC_ERROR_NONE,
+ done_shutdown_event, server,
&server->shutdown_tags[i].completion);
}
}
@@ -723,11 +738,12 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
}
static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
- bool success) {
+ grpc_error *error) {
grpc_call_element *elem = ptr;
call_data *calld = elem->call_data;
gpr_timespec op_deadline;
+ GRPC_ERROR_REF(error);
grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, elem);
op_deadline = calld->recv_initial_metadata->deadline;
if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) {
@@ -736,11 +752,13 @@ static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
if (calld->host && calld->path) {
/* do nothing */
} else {
- success = 0;
+ GRPC_ERROR_UNREF(error);
+ error =
+ GRPC_ERROR_CREATE_REFERENCING("Missing :authority or :path", &error, 1);
}
- calld->on_done_recv_initial_metadata->cb(
- exec_ctx, calld->on_done_recv_initial_metadata->cb_arg, success);
+ grpc_exec_ctx_sched(exec_ctx, calld->on_done_recv_initial_metadata, error,
+ NULL);
}
static void server_mutate_op(grpc_call_element *elem,
@@ -765,10 +783,10 @@ static void server_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
}
static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
- bool success) {
+ grpc_error *error) {
grpc_call_element *elem = ptr;
call_data *calld = elem->call_data;
- if (success) {
+ if (error == GRPC_ERROR_NONE) {
start_new_rpc(exec_ctx, elem);
} else {
gpr_mu_lock(&calld->mu_state);
@@ -776,7 +794,8 @@ static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
- grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure,
+ GRPC_ERROR_NONE, NULL);
} else if (calld->state == PENDING) {
calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
@@ -793,9 +812,9 @@ static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd,
const void *transport_server_data) {
channel_data *chand = cd;
/* create a call */
- grpc_call *call =
- grpc_call_create(chand->channel, NULL, 0, NULL, transport_server_data,
- NULL, 0, gpr_inf_future(GPR_CLOCK_MONOTONIC));
+ grpc_call *call = grpc_call_create(chand->channel, NULL, 0, NULL, NULL,
+ transport_server_data, NULL, 0,
+ gpr_inf_future(GPR_CLOCK_MONOTONIC));
grpc_call_element *elem =
grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
call_data *calld = elem->call_data;
@@ -809,10 +828,10 @@ static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd,
}
static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd,
- bool iomgr_status_ignored) {
+ grpc_error *error) {
channel_data *chand = cd;
grpc_server *server = chand->server;
- if (chand->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) {
+ if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) {
grpc_transport_op op;
memset(&op, 0, sizeof(op));
op.on_connectivity_state_change = &chand->channel_connectivity_changed,
@@ -845,7 +864,7 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
}
static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- void *ignored) {
+ const grpc_call_stats *stats, void *ignored) {
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
@@ -910,7 +929,7 @@ const grpc_channel_filter grpc_server_top_filter = {
grpc_channel_next_op,
sizeof(call_data),
init_call_elem,
- grpc_call_stack_ignore_set_pollset,
+ grpc_call_stack_ignore_set_pollset_or_pollset_set,
destroy_call_elem,
sizeof(channel_data),
init_channel_elem,
@@ -1148,7 +1167,9 @@ void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s,
op.set_accept_stream_user_data = chand;
op.on_connectivity_state_change = &chand->channel_connectivity_changed;
op.connectivity_state = &chand->connectivity_state;
- op.disconnect = gpr_atm_acq_load(&s->shutdown_flag) != 0;
+ if (gpr_atm_acq_load(&s->shutdown_flag) != 0) {
+ op.disconnect_with_error = GRPC_ERROR_CREATE("Server shutdown");
+ }
grpc_transport_perform_op(exec_ctx, transport, &op);
}
@@ -1159,7 +1180,7 @@ void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg,
}
static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s,
- bool success) {
+ grpc_error *error) {
grpc_server *server = s;
gpr_mu_lock(&server->mu_global);
server->listeners_destroyed++;
@@ -1181,8 +1202,8 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
gpr_mu_lock(&server->mu_global);
grpc_cq_begin_op(cq, tag);
if (server->shutdown_published) {
- grpc_cq_end_op(&exec_ctx, cq, tag, 1, done_published_shutdown, NULL,
- gpr_malloc(sizeof(grpc_cq_completion)));
+ grpc_cq_end_op(&exec_ctx, cq, tag, GRPC_ERROR_NONE, done_published_shutdown,
+ NULL, gpr_malloc(sizeof(grpc_cq_completion)));
gpr_mu_unlock(&server->mu_global);
goto done;
}
@@ -1205,7 +1226,8 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
/* collect all unregistered then registered calls */
gpr_mu_lock(&server->mu_call);
- kill_pending_work_locked(&exec_ctx, server);
+ kill_pending_work_locked(&exec_ctx, server,
+ GRPC_ERROR_CREATE("Server Shutdown"));
gpr_mu_unlock(&server->mu_call);
maybe_finish_shutdown(&exec_ctx, server);
@@ -1233,7 +1255,8 @@ void grpc_server_cancel_all_calls(grpc_server *server) {
channel_broadcaster_init(server, &broadcaster);
gpr_mu_unlock(&server->mu_global);
- channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0, 1);
+ channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0,
+ GRPC_ERROR_CREATE("Cancelling all calls"));
grpc_exec_ctx_finish(&exec_ctx);
}
@@ -1280,13 +1303,15 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx,
request_matcher *rm = NULL;
int request_id;
if (gpr_atm_acq_load(&server->shutdown_flag)) {
- fail_call(exec_ctx, server, cq_idx, rc);
+ fail_call(exec_ctx, server, cq_idx, rc,
+ GRPC_ERROR_CREATE("Server Shutdown"));
return GRPC_CALL_OK;
}
request_id = gpr_stack_lockfree_pop(server->request_freelist);
if (request_id == -1) {
/* out of request ids: just fail this one */
- fail_call(exec_ctx, server, cq_idx, rc);
+ fail_call(exec_ctx, server, cq_idx, rc,
+ GRPC_ERROR_CREATE("Server Shutdown"));
return GRPC_CALL_OK;
}
switch (rc->type) {
@@ -1314,8 +1339,8 @@ static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx,
grpc_closure_init(
&calld->kill_zombie_closure, kill_zombie,
grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
- grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true,
- NULL);
+ grpc_exec_ctx_sched(exec_ctx, &calld->kill_zombie_closure,
+ GRPC_ERROR_NONE, NULL);
} else {
GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED;
@@ -1421,13 +1446,14 @@ done:
}
static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
- size_t cq_idx, requested_call *rc) {
+ size_t cq_idx, requested_call *rc, grpc_error *error) {
*rc->call = NULL;
rc->initial_metadata->count = 0;
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
server_ref(server);
- grpc_cq_end_op(exec_ctx, server->cqs[cq_idx], rc->tag, 0, done_request_event,
- rc, &rc->completion);
+ grpc_cq_end_op(exec_ctx, server->cqs[cq_idx], rc->tag, error,
+ done_request_event, rc, &rc->completion);
}
const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) {
diff --git a/src/core/lib/surface/version.c b/src/core/lib/surface/version.c
index aca76d2bb7..53f3c43854 100644
--- a/src/core/lib/surface/version.c
+++ b/src/core/lib/surface/version.c
@@ -36,4 +36,4 @@
#include <grpc/grpc.h>
-const char *grpc_version_string(void) { return "0.15.0-dev"; }
+const char *grpc_version_string(void) { return "0.16.0-dev"; }
diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c
index e24ee638fd..054f112127 100644
--- a/src/core/lib/transport/connectivity_state.c
+++ b/src/core/lib/transport/connectivity_state.c
@@ -51,8 +51,8 @@ const char *grpc_connectivity_state_name(grpc_connectivity_state state) {
return "READY";
case GRPC_CHANNEL_TRANSIENT_FAILURE:
return "TRANSIENT_FAILURE";
- case GRPC_CHANNEL_FATAL_FAILURE:
- return "FATAL_FAILURE";
+ case GRPC_CHANNEL_SHUTDOWN:
+ return "SHUTDOWN";
}
GPR_UNREACHABLE_CODE(return "UNKNOWN");
}
@@ -61,35 +61,40 @@ void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker,
grpc_connectivity_state init_state,
const char *name) {
tracker->current_state = init_state;
+ tracker->current_error = GRPC_ERROR_NONE;
tracker->watchers = NULL;
tracker->name = gpr_strdup(name);
}
void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx,
grpc_connectivity_state_tracker *tracker) {
- int success;
+ grpc_error *error;
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;
- success = 1;
+ if (GRPC_CHANNEL_SHUTDOWN != *w->current) {
+ *w->current = GRPC_CHANNEL_SHUTDOWN;
+ error = GRPC_ERROR_NONE;
} else {
- success = 0;
+ error = GRPC_ERROR_CREATE("Shutdown connectivity owner");
}
- grpc_exec_ctx_enqueue(exec_ctx, w->notify, success, NULL);
+ grpc_exec_ctx_sched(exec_ctx, w->notify, error, NULL);
gpr_free(w);
}
+ GRPC_ERROR_UNREF(tracker->current_error);
gpr_free(tracker->name);
}
grpc_connectivity_state grpc_connectivity_state_check(
- grpc_connectivity_state_tracker *tracker) {
+ grpc_connectivity_state_tracker *tracker, grpc_error **error) {
if (grpc_connectivity_state_trace) {
gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
grpc_connectivity_state_name(tracker->current_state));
}
+ if (error != NULL) {
+ *error = GRPC_ERROR_REF(tracker->current_error);
+ }
return tracker->current_state;
}
@@ -109,7 +114,7 @@ int grpc_connectivity_state_notify_on_state_change(
if (current == NULL) {
grpc_connectivity_state_watcher *w = tracker->watchers;
if (w != NULL && w->notify == notify) {
- grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
tracker->watchers = w->next;
gpr_free(w);
return 0;
@@ -117,7 +122,7 @@ int grpc_connectivity_state_notify_on_state_change(
while (w != NULL) {
grpc_connectivity_state_watcher *rm_candidate = w->next;
if (rm_candidate != NULL && rm_candidate->notify == notify) {
- grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL);
+ grpc_exec_ctx_sched(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
w->next = w->next->next;
gpr_free(rm_candidate);
return 0;
@@ -128,7 +133,8 @@ int grpc_connectivity_state_notify_on_state_change(
} else {
if (tracker->current_state != *current) {
*current = tracker->current_state;
- grpc_exec_ctx_enqueue(exec_ctx, notify, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, notify,
+ GRPC_ERROR_REF(tracker->current_error), NULL);
} else {
grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w));
w->current = current;
@@ -143,22 +149,38 @@ int grpc_connectivity_state_notify_on_state_change(
void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx,
grpc_connectivity_state_tracker *tracker,
grpc_connectivity_state state,
- const char *reason) {
+ grpc_error *error, const char *reason) {
grpc_connectivity_state_watcher *w;
if (grpc_connectivity_state_trace) {
- gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s]", tracker, tracker->name,
- grpc_connectivity_state_name(tracker->current_state),
- grpc_connectivity_state_name(state), reason);
+ const char *error_string = grpc_error_string(error);
+ gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
+ tracker->name, grpc_connectivity_state_name(tracker->current_state),
+ grpc_connectivity_state_name(state), reason, error, error_string);
+ grpc_error_free_string(error_string);
+ }
+ switch (state) {
+ case GRPC_CHANNEL_CONNECTING:
+ case GRPC_CHANNEL_IDLE:
+ case GRPC_CHANNEL_READY:
+ GPR_ASSERT(error == GRPC_ERROR_NONE);
+ break;
+ case GRPC_CHANNEL_SHUTDOWN:
+ case GRPC_CHANNEL_TRANSIENT_FAILURE:
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
+ break;
}
+ GRPC_ERROR_UNREF(tracker->current_error);
+ tracker->current_error = error;
if (tracker->current_state == state) {
return;
}
- GPR_ASSERT(tracker->current_state != GRPC_CHANNEL_FATAL_FAILURE);
+ GPR_ASSERT(tracker->current_state != GRPC_CHANNEL_SHUTDOWN);
tracker->current_state = state;
while ((w = tracker->watchers) != NULL) {
*w->current = tracker->current_state;
tracker->watchers = w->next;
- grpc_exec_ctx_enqueue(exec_ctx, w->notify, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, w->notify,
+ GRPC_ERROR_REF(tracker->current_error), NULL);
gpr_free(w);
}
}
diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
index 2eb7e09124..7a2fa52c10 100644
--- a/src/core/lib/transport/connectivity_state.h
+++ b/src/core/lib/transport/connectivity_state.h
@@ -49,6 +49,8 @@ typedef struct grpc_connectivity_state_watcher {
typedef struct {
/** current connectivity state */
grpc_connectivity_state current_state;
+ /** error associated with state */
+ grpc_error *current_error;
/** all our watchers */
grpc_connectivity_state_watcher *watchers;
/** a name to help debugging */
@@ -70,10 +72,11 @@ void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx,
void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx,
grpc_connectivity_state_tracker *tracker,
grpc_connectivity_state state,
+ grpc_error *associated_error,
const char *reason);
grpc_connectivity_state grpc_connectivity_state_check(
- grpc_connectivity_state_tracker *tracker);
+ grpc_connectivity_state_tracker *tracker, grpc_error **current_error);
/** Return 1 if the channel should start connecting, 0 otherwise.
If current==NULL cancel notify if it is already queued (success==0 in that
diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c
index 82c8e239f6..0677f29766 100644
--- a/src/core/lib/transport/metadata.c
+++ b/src/core/lib/transport/metadata.c
@@ -129,7 +129,10 @@ typedef struct mdtab_shard {
internal_metadata **elems;
size_t count;
size_t capacity;
- size_t free;
+ /** Estimate of the number of unreferenced mdelems in the hash table.
+ This will eventually converge to the exact number, but it's instantaneous
+ accuracy is not guaranteed */
+ gpr_atm free_estimate;
} mdtab_shard;
#define LOG2_STRTAB_SHARD_COUNT 5
@@ -217,7 +220,7 @@ void grpc_mdctx_global_init(void) {
mdtab_shard *shard = &g_mdtab_shard[i];
gpr_mu_init(&shard->mu);
shard->count = 0;
- shard->free = 0;
+ gpr_atm_no_barrier_store(&shard->free_estimate, 0);
shard->capacity = INITIAL_MDTAB_CAPACITY;
shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
@@ -232,7 +235,7 @@ void grpc_mdctx_global_shutdown(void) {
gc_mdtab(shard);
/* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
if (shard->count != 0) {
- gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked",
+ gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked",
shard->count);
if (grpc_iomgr_abort_on_leaks()) {
abort();
@@ -245,7 +248,7 @@ void grpc_mdctx_global_shutdown(void) {
gpr_mu_destroy(&shard->mu);
/* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
if (shard->count != 0) {
- gpr_log(GPR_DEBUG, "WARNING: %d metadata strings were leaked",
+ gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked",
shard->count);
for (size_t j = 0; j < shard->capacity; j++) {
for (internal_string *s = shard->strs[j]; s; s = s->bucket_next) {
@@ -281,10 +284,8 @@ static void ref_md_locked(mdtab_shard *shard,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
- if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
- shard->free--;
- } else {
- GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
+ if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) {
+ gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1);
}
}
@@ -447,6 +448,7 @@ static void gc_mdtab(mdtab_shard *shard) {
size_t i;
internal_metadata **prev_next;
internal_metadata *md, *next;
+ gpr_atm num_freed = 0;
GPR_TIMER_BEGIN("gc_mdtab", 0);
for (i = 0; i < shard->capacity; i++) {
@@ -463,13 +465,14 @@ static void gc_mdtab(mdtab_shard *shard) {
}
gpr_free(md);
*prev_next = next;
- shard->free--;
+ num_freed++;
shard->count--;
} else {
prev_next = &md->bucket_next;
}
}
}
+ gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed);
GPR_TIMER_END("gc_mdtab", 0);
}
@@ -504,7 +507,8 @@ static void grow_mdtab(mdtab_shard *shard) {
}
static void rehash_mdtab(mdtab_shard *shard) {
- if (shard->free > shard->capacity / 4) {
+ if (gpr_atm_no_barrier_load(&shard->free_estimate) >
+ (gpr_atm)(shard->capacity / 4)) {
gc_mdtab(shard);
} else {
grow_mdtab(shard);
@@ -553,7 +557,7 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
/* not found: create a new pair */
md = gpr_malloc(sizeof(internal_metadata));
- gpr_atm_rel_store(&md->refcnt, 2);
+ gpr_atm_rel_store(&md->refcnt, 1);
md->key = key;
md->value = value;
md->user_data = 0;
@@ -645,7 +649,7 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
this function - meaning that no adjustment to mdtab_free is necessary,
simplifying the logic here to be just an atomic increment */
/* use C assert to have this removed in opt builds */
- assert(gpr_atm_no_barrier_load(&md->refcnt) >= 2);
+ assert(gpr_atm_no_barrier_load(&md->refcnt) >= 1);
gpr_atm_no_barrier_fetch_add(&md->refcnt, 1);
return gmd;
}
@@ -662,18 +666,13 @@ void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
- if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
- uint32_t hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
+ uint32_t hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
+ if (1 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
+ /* once the refcount hits zero, some other thread can come along and
+ free md at any time: it's unsafe from this point on to access it */
mdtab_shard *shard =
&g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
- GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
- gpr_mu_lock(&shard->mu);
- if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
- shard->free++;
- gpr_atm_no_barrier_store(&md->refcnt, 0);
- }
- gpr_mu_unlock(&shard->mu);
- GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
+ gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1);
}
}
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
index 73b0041fd4..c5f16e530d 100644
--- a/src/core/lib/transport/static_metadata.c
+++ b/src/core/lib/transport/static_metadata.c
@@ -48,7 +48,7 @@ uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 4, 8, 6, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
{11, 35, 10, 35, 12, 35, 12, 49, 13, 35, 14, 35, 15, 35, 16, 35, 17, 35,
@@ -56,10 +56,10 @@ const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] =
30, 18, 30, 35, 31, 35, 32, 35, 36, 35, 37, 35, 38, 35, 39, 35, 42, 33,
42, 34, 42, 48, 42, 53, 42, 54, 42, 55, 42, 56, 43, 33, 43, 48, 43, 53,
46, 0, 46, 1, 46, 2, 50, 35, 57, 35, 58, 35, 59, 35, 60, 35, 61, 35,
- 62, 35, 63, 35, 64, 35, 65, 35, 66, 40, 66, 68, 66, 71, 67, 79, 67, 80,
- 69, 35, 70, 35, 72, 35, 73, 35, 74, 35, 75, 35, 76, 41, 76, 51, 76, 52,
- 77, 35, 78, 35, 81, 3, 81, 4, 81, 5, 81, 6, 81, 7, 81, 8, 81, 9,
- 82, 35, 83, 84, 85, 35, 86, 35, 87, 35, 88, 35, 89, 35};
+ 62, 35, 63, 35, 64, 35, 65, 35, 66, 35, 67, 40, 67, 69, 67, 72, 68, 80,
+ 68, 81, 70, 35, 71, 35, 73, 35, 74, 35, 75, 35, 76, 35, 77, 41, 77, 51,
+ 77, 52, 78, 35, 79, 35, 82, 3, 82, 4, 82, 5, 82, 6, 82, 7, 82, 8,
+ 82, 9, 83, 35, 84, 85, 86, 35, 87, 35, 88, 35, 89, 35, 90, 35};
const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
"0",
@@ -126,6 +126,7 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
"if-unmodified-since",
"last-modified",
"link",
+ "load-reporting",
"location",
"max-forwards",
":method",
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index f9d8bcdc8f..5ff0d2f3bc 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -44,7 +44,7 @@
#include "src/core/lib/transport/metadata.h"
-#define GRPC_STATIC_MDSTR_COUNT 90
+#define GRPC_STATIC_MDSTR_COUNT 91
extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
/* "0" */
#define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
@@ -175,60 +175,62 @@ extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[62])
/* "link" */
#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[63])
+/* "load-reporting" */
+#define GRPC_MDSTR_LOAD_REPORTING (&grpc_static_mdstr_table[64])
/* "location" */
-#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[64])
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[65])
/* "max-forwards" */
-#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[65])
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[66])
/* ":method" */
-#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[66])
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[67])
/* ":path" */
-#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[67])
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[68])
/* "POST" */
-#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[68])
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[69])
/* "proxy-authenticate" */
-#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[69])
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[70])
/* "proxy-authorization" */
-#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[70])
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[71])
/* "PUT" */
-#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[71])
+#define GRPC_MDSTR_PUT (&grpc_static_mdstr_table[72])
/* "range" */
-#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[72])
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[73])
/* "referer" */
-#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[73])
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[74])
/* "refresh" */
-#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[74])
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[75])
/* "retry-after" */
-#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[75])
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[76])
/* ":scheme" */
-#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[76])
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[77])
/* "server" */
-#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[77])
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[78])
/* "set-cookie" */
-#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[78])
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[79])
/* "/" */
-#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[79])
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[80])
/* "/index.html" */
-#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[80])
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[81])
/* ":status" */
-#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[81])
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[82])
/* "strict-transport-security" */
-#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[82])
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[83])
/* "te" */
-#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[83])
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[84])
/* "trailers" */
-#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[84])
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[85])
/* "transfer-encoding" */
-#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[85])
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[86])
/* "user-agent" */
-#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[86])
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[87])
/* "vary" */
-#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[87])
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[88])
/* "via" */
-#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[88])
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[89])
/* "www-authenticate" */
-#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[89])
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[90])
-#define GRPC_STATIC_MDELEM_COUNT 79
+#define GRPC_STATIC_MDELEM_COUNT 80
extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
/* "accept-charset": "" */
@@ -333,71 +335,73 @@ extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
/* "link": "" */
#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
+/* "load-reporting": "" */
+#define GRPC_MDELEM_LOAD_REPORTING_EMPTY (&grpc_static_mdelem_table[47])
/* "location": "" */
-#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[47])
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[48])
/* "max-forwards": "" */
-#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[48])
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[49])
/* ":method": "GET" */
-#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[49])
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[50])
/* ":method": "POST" */
-#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[50])
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[51])
/* ":method": "PUT" */
-#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[51])
+#define GRPC_MDELEM_METHOD_PUT (&grpc_static_mdelem_table[52])
/* ":path": "/" */
-#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[52])
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[53])
/* ":path": "/index.html" */
-#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[53])
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[54])
/* "proxy-authenticate": "" */
-#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[54])
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[55])
/* "proxy-authorization": "" */
-#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[55])
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[56])
/* "range": "" */
-#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[56])
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[57])
/* "referer": "" */
-#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[57])
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[58])
/* "refresh": "" */
-#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[58])
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[59])
/* "retry-after": "" */
-#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[59])
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[60])
/* ":scheme": "grpc" */
-#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[60])
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[61])
/* ":scheme": "http" */
-#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[61])
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[62])
/* ":scheme": "https" */
-#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[62])
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[63])
/* "server": "" */
-#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[63])
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[64])
/* "set-cookie": "" */
-#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[64])
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[65])
/* ":status": "200" */
-#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[65])
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[66])
/* ":status": "204" */
-#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[66])
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[67])
/* ":status": "206" */
-#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[67])
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[68])
/* ":status": "304" */
-#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[68])
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[69])
/* ":status": "400" */
-#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[69])
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[70])
/* ":status": "404" */
-#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[70])
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[71])
/* ":status": "500" */
-#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[71])
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[72])
/* "strict-transport-security": "" */
#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
- (&grpc_static_mdelem_table[72])
+ (&grpc_static_mdelem_table[73])
/* "te": "trailers" */
-#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[73])
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[74])
/* "transfer-encoding": "" */
-#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[74])
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[75])
/* "user-agent": "" */
-#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[75])
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[76])
/* "vary": "" */
-#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[76])
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[77])
/* "via": "" */
-#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[77])
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[78])
/* "www-authenticate": "" */
-#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[78])
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[79])
extern const uint8_t
grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index e6d524abe6..857c3909d2 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -36,6 +36,7 @@
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
+#include "src/core/lib/support/string.h"
#include "src/core/lib/transport/transport_impl.h"
#ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -60,7 +61,7 @@ void grpc_stream_unref(grpc_exec_ctx *exec_ctx,
grpc_stream_refcount *refcount) {
#endif
if (gpr_unref(&refcount->refs)) {
- grpc_exec_ctx_enqueue(exec_ctx, &refcount->destroy, true, NULL);
+ grpc_exec_ctx_sched(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE, NULL);
}
}
@@ -125,10 +126,19 @@ void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx,
transport->vtable->perform_op(exec_ctx, transport, op);
}
-void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_transport *transport, grpc_stream *stream,
- grpc_pollset *pollset) {
- transport->vtable->set_pollset(exec_ctx, transport, stream, pollset);
+void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport,
+ grpc_stream *stream,
+ grpc_polling_entity *pollent) {
+ grpc_pollset *pollset;
+ grpc_pollset_set *pollset_set;
+ if ((pollset = grpc_polling_entity_pollset(pollent)) != NULL) {
+ transport->vtable->set_pollset(exec_ctx, transport, stream, pollset);
+ } else if ((pollset_set = grpc_polling_entity_pollset_set(pollent)) != NULL) {
+ transport->vtable->set_pollset_set(exec_ctx, transport, stream,
+ pollset_set);
+ } else {
+ abort();
+ }
}
void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
@@ -143,62 +153,97 @@ char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx,
return transport->vtable->get_peer(exec_ctx, transport);
}
-void grpc_transport_stream_op_finish_with_failure(
- grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op) {
- grpc_exec_ctx_enqueue(exec_ctx, op->recv_message_ready, false, NULL);
- grpc_exec_ctx_enqueue(exec_ctx, op->recv_initial_metadata_ready, false, NULL);
- grpc_exec_ctx_enqueue(exec_ctx, op->on_complete, false, NULL);
-}
-
-void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
- grpc_status_code status) {
- GPR_ASSERT(status != GRPC_STATUS_OK);
- if (op->cancel_with_status == GRPC_STATUS_OK) {
- op->cancel_with_status = status;
- }
- if (op->close_with_status != GRPC_STATUS_OK) {
- op->close_with_status = GRPC_STATUS_OK;
- if (op->optional_close_message != NULL) {
- gpr_slice_unref(*op->optional_close_message);
- op->optional_close_message = NULL;
- }
- }
+void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
+ grpc_transport_stream_op *op,
+ grpc_error *error) {
+ grpc_exec_ctx_sched(exec_ctx, op->recv_message_ready, GRPC_ERROR_REF(error),
+ NULL);
+ grpc_exec_ctx_sched(exec_ctx, op->recv_initial_metadata_ready,
+ GRPC_ERROR_REF(error), NULL);
+ grpc_exec_ctx_sched(exec_ctx, op->on_complete, error, NULL);
}
typedef struct {
- gpr_slice message;
+ grpc_error *error;
grpc_closure *then_call;
grpc_closure closure;
} close_message_data;
-static void free_message(grpc_exec_ctx *exec_ctx, void *p, bool iomgr_success) {
+static void free_message(grpc_exec_ctx *exec_ctx, void *p, grpc_error *error) {
close_message_data *cmd = p;
- gpr_slice_unref(cmd->message);
+ GRPC_ERROR_UNREF(cmd->error);
if (cmd->then_call != NULL) {
- cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, iomgr_success);
+ cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, error);
}
gpr_free(cmd);
}
+static void add_error(grpc_transport_stream_op *op, grpc_error **which,
+ grpc_error *error) {
+ close_message_data *cmd;
+ cmd = gpr_malloc(sizeof(*cmd));
+ cmd->error = error;
+ cmd->then_call = op->on_complete;
+ grpc_closure_init(&cmd->closure, free_message, cmd);
+ op->on_complete = &cmd->closure;
+ *which = error;
+}
+
+void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
+ grpc_status_code status) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
+ if (op->cancel_error == GRPC_ERROR_NONE) {
+ op->cancel_error = grpc_error_set_int(GRPC_ERROR_CANCELLED,
+ GRPC_ERROR_INT_GRPC_STATUS, status);
+ op->close_error = GRPC_ERROR_NONE;
+ }
+}
+
+void grpc_transport_stream_op_add_cancellation_with_message(
+ grpc_transport_stream_op *op, grpc_status_code status,
+ gpr_slice *optional_message) {
+ GPR_ASSERT(status != GRPC_STATUS_OK);
+ if (op->cancel_error != GRPC_ERROR_NONE) {
+ if (optional_message) {
+ gpr_slice_unref(*optional_message);
+ }
+ return;
+ }
+ grpc_error *error;
+ if (optional_message != NULL) {
+ char *msg = gpr_dump_slice(*optional_message, GPR_DUMP_ASCII);
+ error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
+ GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+ gpr_free(msg);
+ gpr_slice_unref(*optional_message);
+ } else {
+ error = GRPC_ERROR_CREATE("Call cancelled");
+ }
+ error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
+ add_error(op, &op->close_error, error);
+}
+
void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
grpc_status_code status,
gpr_slice *optional_message) {
- close_message_data *cmd;
GPR_ASSERT(status != GRPC_STATUS_OK);
- if (op->cancel_with_status != GRPC_STATUS_OK ||
- op->close_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE ||
+ op->close_error != GRPC_ERROR_NONE) {
if (optional_message) {
gpr_slice_unref(*optional_message);
}
return;
}
- if (optional_message) {
- cmd = gpr_malloc(sizeof(*cmd));
- cmd->message = *optional_message;
- cmd->then_call = op->on_complete;
- grpc_closure_init(&cmd->closure, free_message, cmd);
- op->on_complete = &cmd->closure;
- op->optional_close_message = &cmd->message;
+ grpc_error *error;
+ if (optional_message != NULL) {
+ char *msg = gpr_dump_slice(*optional_message, GPR_DUMP_ASCII);
+ error = grpc_error_set_str(GRPC_ERROR_CREATE(msg),
+ GRPC_ERROR_STR_GRPC_MESSAGE, msg);
+ gpr_free(msg);
+ gpr_slice_unref(*optional_message);
+ } else {
+ error = GRPC_ERROR_CREATE("Call force closed");
}
- op->close_with_status = status;
+ error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, status);
+ add_error(op, &op->close_error, error);
}
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 482a9d1791..08c0a237c9 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -37,6 +37,7 @@
#include <stddef.h>
#include "src/core/lib/channel/context.h"
+#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/pollset.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/transport/byte_stream.h"
@@ -134,13 +135,12 @@ typedef struct grpc_transport_stream_op {
/** Collect any stats into provided buffer, zero internal stat counters */
grpc_transport_stream_stats *collect_stats;
- /** If != GRPC_STATUS_OK, cancel this stream */
- grpc_status_code cancel_with_status;
+ /** If != GRPC_ERROR_NONE, cancel this stream */
+ grpc_error *cancel_error;
- /** If != GRPC_STATUS_OK, send grpc-status, grpc-message, and close this
+ /** If != GRPC_ERROR, send grpc-status, grpc-message, and close this
stream for both reading and writing */
- grpc_status_code close_with_status;
- gpr_slice *optional_close_message;
+ grpc_error *close_error;
/* Indexes correspond to grpc_context_index enum values */
grpc_call_context_element *context;
@@ -154,7 +154,7 @@ typedef struct grpc_transport_op {
grpc_closure *on_connectivity_state_change;
grpc_connectivity_state *connectivity_state;
/** should the transport be disconnected */
- int disconnect;
+ grpc_error *disconnect_with_error;
/** should we send a goaway?
after a goaway is sent, once there are no more active calls on
the transport, the transport should disconnect */
@@ -197,9 +197,8 @@ int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx,
grpc_stream_refcount *refcount,
const void *server_data);
-void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx,
- grpc_transport *transport, grpc_stream *stream,
- grpc_pollset *pollset);
+void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport,
+ grpc_stream *stream, grpc_polling_entity *pollent);
/* Destroy transport data for a stream.
@@ -216,11 +215,16 @@ void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
grpc_stream *stream, void *and_free_memory);
void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
- grpc_transport_stream_op *op);
+ grpc_transport_stream_op *op,
+ grpc_error *error);
void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
grpc_status_code status);
+void grpc_transport_stream_op_add_cancellation_with_message(
+ grpc_transport_stream_op *op, grpc_status_code status,
+ gpr_slice *optional_message);
+
void grpc_transport_stream_op_add_close(grpc_transport_stream_op *op,
grpc_status_code status,
gpr_slice *optional_message);
diff --git a/src/core/lib/transport/transport_impl.h b/src/core/lib/transport/transport_impl.h
index 956155eec8..fc7140671b 100644
--- a/src/core/lib/transport/transport_impl.h
+++ b/src/core/lib/transport/transport_impl.h
@@ -53,6 +53,10 @@ typedef struct grpc_transport_vtable {
void (*set_pollset)(grpc_exec_ctx *exec_ctx, grpc_transport *self,
grpc_stream *stream, grpc_pollset *pollset);
+ /* implementation of grpc_transport_set_pollset */
+ void (*set_pollset_set)(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, grpc_pollset_set *pollset_set);
+
/* implementation of grpc_transport_perform_stream_op */
void (*perform_stream_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self,
grpc_stream *stream, grpc_transport_stream_op *op);
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
index df04c61127..138591db2a 100644
--- a/src/core/lib/transport/transport_op_string.c
+++ b/src/core/lib/transport/transport_op_string.c
@@ -63,8 +63,8 @@ static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) {
}
if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) {
char *tmp;
- gpr_asprintf(&tmp, " deadline=%lld.%09d", (long long)md.deadline.tv_sec,
- (int)md.deadline.tv_nsec);
+ gpr_asprintf(&tmp, " deadline=%" PRId64 ".%09d", md.deadline.tv_sec,
+ md.deadline.tv_nsec);
gpr_strvec_add(b, tmp);
}
}
@@ -119,10 +119,21 @@ char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) {
gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA"));
}
- if (op->cancel_with_status != GRPC_STATUS_OK) {
+ if (op->cancel_error != GRPC_ERROR_NONE) {
if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
first = 0;
- gpr_asprintf(&tmp, "CANCEL:%d", op->cancel_with_status);
+ const char *msg = grpc_error_string(op->cancel_error);
+ gpr_asprintf(&tmp, "CANCEL:%s", msg);
+ grpc_error_free_string(msg);
+ gpr_strvec_add(&b, tmp);
+ }
+
+ if (op->close_error != GRPC_ERROR_NONE) {
+ if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
+ first = 0;
+ const char *msg = grpc_error_string(op->close_error);
+ gpr_asprintf(&tmp, "CLOSE:%s", msg);
+ grpc_error_free_string(msg);
gpr_strvec_add(&b, tmp);
}