blob: 010fedd7b735507bc8850b40ed806e30562fc21d [file] [log] [blame]
Mark D. Roth14c072c2016-08-26 08:31:34 -07001//
2// Copyright 2016, Google Inc.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30//
31
32#include "src/core/lib/channel/deadline_filter.h"
33
34#include <stdbool.h>
35#include <string.h>
36
37#include <grpc/support/log.h>
Mark D. Roth1bbe6cb2016-08-31 08:33:37 -070038#include <grpc/support/sync.h>
Mark D. Roth14c072c2016-08-26 08:31:34 -070039#include <grpc/support/time.h>
40
41#include "src/core/lib/iomgr/timer.h"
42
Mark D. Roth72f6da82016-09-02 13:42:38 -070043//
44// grpc_deadline_state
45//
46
47// Timer callback.
Mark D. Roth932b10c2016-09-09 08:44:30 -070048static void timer_callback(grpc_exec_ctx* exec_ctx, void* arg,
49 grpc_error* error) {
Mark D. Roth72f6da82016-09-02 13:42:38 -070050 grpc_call_element* elem = arg;
51 grpc_deadline_state* deadline_state = elem->call_data;
Mark D. Roth72f6da82016-09-02 13:42:38 -070052 gpr_mu_lock(&deadline_state->timer_mu);
53 deadline_state->timer_pending = false;
54 gpr_mu_unlock(&deadline_state->timer_mu);
55 if (error != GRPC_ERROR_CANCELLED) {
Mark D. Roth75d74782016-09-09 07:46:01 -070056 gpr_slice msg = gpr_slice_from_static_string("Deadline Exceeded");
Mark D. Roth932b10c2016-09-09 08:44:30 -070057 grpc_call_element_send_cancel_with_message(
58 exec_ctx, elem, GRPC_STATUS_DEADLINE_EXCEEDED, &msg);
Mark D. Roth75d74782016-09-09 07:46:01 -070059 gpr_slice_unref(msg);
Mark D. Roth72f6da82016-09-02 13:42:38 -070060 }
61 GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer");
62}
63
64// Starts the deadline timer.
Mark D. Roth932b10c2016-09-09 08:44:30 -070065static void start_timer_if_needed(grpc_exec_ctx* exec_ctx,
Mark D. Roth72f6da82016-09-02 13:42:38 -070066 grpc_call_element* elem,
67 gpr_timespec deadline) {
68 grpc_deadline_state* deadline_state = elem->call_data;
Mark D. Roth72f6da82016-09-02 13:42:38 -070069 deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
70 if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) {
71 // Take a reference to the call stack, to be owned by the timer.
72 GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
73 gpr_mu_lock(&deadline_state->timer_mu);
Mark D. Roth72f6da82016-09-02 13:42:38 -070074 deadline_state->timer_pending = true;
75 grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, timer_callback,
76 elem, gpr_now(GPR_CLOCK_MONOTONIC));
77 gpr_mu_unlock(&deadline_state->timer_mu);
78 }
79}
80
81// Cancels the deadline timer.
82static void cancel_timer_if_needed(grpc_exec_ctx* exec_ctx,
83 grpc_deadline_state* deadline_state) {
Mark D. Roth72f6da82016-09-02 13:42:38 -070084 gpr_mu_lock(&deadline_state->timer_mu);
85 if (deadline_state->timer_pending) {
Mark D. Roth72f6da82016-09-02 13:42:38 -070086 grpc_timer_cancel(exec_ctx, &deadline_state->timer);
87 deadline_state->timer_pending = false;
88 }
89 gpr_mu_unlock(&deadline_state->timer_mu);
90}
91
92// Callback run when the call is complete.
Mark D. Roth932b10c2016-09-09 08:44:30 -070093static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
Mark D. Roth72f6da82016-09-02 13:42:38 -070094 grpc_deadline_state* deadline_state = arg;
Mark D. Roth72f6da82016-09-02 13:42:38 -070095 cancel_timer_if_needed(exec_ctx, deadline_state);
96 // Invoke the next callback.
97 deadline_state->next_on_complete->cb(
98 exec_ctx, deadline_state->next_on_complete->cb_arg, error);
99}
100
101// Inject our own on_complete callback into op.
102static void inject_on_complete_cb(grpc_deadline_state* deadline_state,
103 grpc_transport_stream_op* op) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700104 deadline_state->next_on_complete = op->on_complete;
105 grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state);
106 op->on_complete = &deadline_state->on_complete;
107}
108
109void grpc_deadline_state_init(grpc_deadline_state* deadline_state,
110 grpc_call_stack* call_stack) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700111 memset(deadline_state, 0, sizeof(*deadline_state));
112 deadline_state->call_stack = call_stack;
113 gpr_mu_init(&deadline_state->timer_mu);
114}
115
116void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx,
117 grpc_deadline_state* deadline_state) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700118 cancel_timer_if_needed(exec_ctx, deadline_state);
119 gpr_mu_destroy(&deadline_state->timer_mu);
120}
121
122void grpc_deadline_state_client_start_transport_stream_op(
123 grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
124 grpc_transport_stream_op* op) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700125 grpc_deadline_state* deadline_state = elem->call_data;
126 if (op->cancel_error != GRPC_ERROR_NONE ||
127 op->close_error != GRPC_ERROR_NONE) {
128 cancel_timer_if_needed(exec_ctx, deadline_state);
129 } else {
130 // If we're sending initial metadata, get the deadline from the metadata
131 // and start the timer if needed.
132 if (op->send_initial_metadata != NULL) {
133 start_timer_if_needed(exec_ctx, elem,
134 op->send_initial_metadata->deadline);
135 }
136 // Make sure we know when the call is complete, so that we can cancel
137 // the timer.
138 if (op->recv_trailing_metadata != NULL) {
139 inject_on_complete_cb(deadline_state, op);
140 }
141 }
142}
143
144//
145// filter code
146//
147
Mark D. Roth72f6da82016-09-02 13:42:38 -0700148// Constructor for channel_data. Used for both client and server filters.
149static void init_channel_elem(grpc_exec_ctx* exec_ctx,
150 grpc_channel_element* elem,
151 grpc_channel_element_args* args) {
152 GPR_ASSERT(!args->is_last);
153}
154
155// Destructor for channel_data. Used for both client and server filters.
156static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
Mark D. Roth932b10c2016-09-09 08:44:30 -0700157 grpc_channel_element* elem) {}
Mark D. Roth72f6da82016-09-02 13:42:38 -0700158
Mark D. Roth14c072c2016-08-26 08:31:34 -0700159// Call data used for both client and server filter.
160typedef struct base_call_data {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700161 grpc_deadline_state deadline_state;
Mark D. Roth14c072c2016-08-26 08:31:34 -0700162} base_call_data;
163
164// Additional call data used only for the server filter.
165typedef struct server_call_data {
166 base_call_data base; // Must be first.
167 // The closure for receiving initial metadata.
168 grpc_closure recv_initial_metadata_ready;
169 // Received initial metadata batch.
170 grpc_metadata_batch* recv_initial_metadata;
171 // The original recv_initial_metadata_ready closure, which we chain to
172 // after our own closure is invoked.
173 grpc_closure* next_recv_initial_metadata_ready;
174} server_call_data;
175
Mark D. Roth14c072c2016-08-26 08:31:34 -0700176// Constructor for call_data. Used for both client and server filters.
Mark D. Roth932b10c2016-09-09 08:44:30 -0700177static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx,
Mark D. Roth14c072c2016-08-26 08:31:34 -0700178 grpc_call_element* elem,
179 grpc_call_element_args* args) {
180 base_call_data* calld = elem->call_data;
181 // Note: size of call data is different between client and server.
182 memset(calld, 0, elem->filter->sizeof_call_data);
Mark D. Roth72f6da82016-09-02 13:42:38 -0700183 grpc_deadline_state_init(&calld->deadline_state, args->call_stack);
Mark D. Roth14c072c2016-08-26 08:31:34 -0700184 return GRPC_ERROR_NONE;
185}
186
187// Destructor for call_data. Used for both client and server filters.
188static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
189 const grpc_call_final_info* final_info,
190 void* and_free_memory) {
Mark D. Roth1bbe6cb2016-08-31 08:33:37 -0700191 base_call_data* calld = elem->call_data;
Mark D. Roth72f6da82016-09-02 13:42:38 -0700192 grpc_deadline_state_destroy(exec_ctx, &calld->deadline_state);
Mark D. Rothd2b45332016-08-26 11:18:00 -0700193}
194
Mark D. Roth14c072c2016-08-26 08:31:34 -0700195// Method for starting a call op for client filter.
196static void client_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
197 grpc_call_element* elem,
198 grpc_transport_stream_op* op) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700199 grpc_deadline_state_client_start_transport_stream_op(exec_ctx, elem, op);
Mark D. Roth14c072c2016-08-26 08:31:34 -0700200 // Chain to next filter.
201 grpc_call_next_op(exec_ctx, elem, op);
202}
203
204// Callback for receiving initial metadata on the server.
Mark D. Roth932b10c2016-09-09 08:44:30 -0700205static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* arg,
206 grpc_error* error) {
Mark D. Roth14c072c2016-08-26 08:31:34 -0700207 grpc_call_element* elem = arg;
208 server_call_data* calld = elem->call_data;
209 // Get deadline from metadata and start the timer if needed.
Mark D. Roth932b10c2016-09-09 08:44:30 -0700210 start_timer_if_needed(exec_ctx, elem, calld->recv_initial_metadata->deadline);
Mark D. Roth14c072c2016-08-26 08:31:34 -0700211 // Invoke the next callback.
212 calld->next_recv_initial_metadata_ready->cb(
213 exec_ctx, calld->next_recv_initial_metadata_ready->cb_arg, error);
214}
215
216// Method for starting a call op for server filter.
217static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
218 grpc_call_element* elem,
219 grpc_transport_stream_op* op) {
220 server_call_data* calld = elem->call_data;
Mark D. Roth1bbe6cb2016-08-31 08:33:37 -0700221 if (op->cancel_error != GRPC_ERROR_NONE ||
222 op->close_error != GRPC_ERROR_NONE) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700223 cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state);
Mark D. Roth1bbe6cb2016-08-31 08:33:37 -0700224 } else {
225 // If we're receiving initial metadata, we need to get the deadline
226 // from the recv_initial_metadata_ready callback. So we inject our
227 // own callback into that hook.
228 if (op->recv_initial_metadata_ready != NULL) {
229 calld->next_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
230 calld->recv_initial_metadata = op->recv_initial_metadata;
231 grpc_closure_init(&calld->recv_initial_metadata_ready,
232 recv_initial_metadata_ready, elem);
233 op->recv_initial_metadata_ready = &calld->recv_initial_metadata_ready;
234 }
235 // Make sure we know when the call is complete, so that we can cancel
236 // the timer.
237 // Note that we trigger this on recv_trailing_metadata, even though
238 // the client never sends trailing metadata, because this is the
239 // hook that tells us when the call is complete on the server side.
240 if (op->recv_trailing_metadata != NULL) {
Mark D. Roth72f6da82016-09-02 13:42:38 -0700241 inject_on_complete_cb(&calld->base.deadline_state, op);
Mark D. Roth1bbe6cb2016-08-31 08:33:37 -0700242 }
Mark D. Rothd2b45332016-08-26 11:18:00 -0700243 }
Mark D. Roth14c072c2016-08-26 08:31:34 -0700244 // Chain to next filter.
245 grpc_call_next_op(exec_ctx, elem, op);
246}
247
248const grpc_channel_filter grpc_client_deadline_filter = {
249 client_start_transport_stream_op,
250 grpc_channel_next_op,
251 sizeof(base_call_data),
252 init_call_elem,
253 grpc_call_stack_ignore_set_pollset_or_pollset_set,
254 destroy_call_elem,
Mark D. Rothb3405f0a2016-09-09 08:46:28 -0700255 0, // sizeof(channel_data)
Mark D. Roth14c072c2016-08-26 08:31:34 -0700256 init_channel_elem,
257 destroy_channel_elem,
258 grpc_call_next_get_peer,
259 "deadline",
260};
261
262const grpc_channel_filter grpc_server_deadline_filter = {
263 server_start_transport_stream_op,
264 grpc_channel_next_op,
265 sizeof(server_call_data),
266 init_call_elem,
267 grpc_call_stack_ignore_set_pollset_or_pollset_set,
268 destroy_call_elem,
Mark D. Rothb3405f0a2016-09-09 08:46:28 -0700269 0, // sizeof(channel_data)
Mark D. Roth14c072c2016-08-26 08:31:34 -0700270 init_channel_elem,
271 destroy_channel_elem,
272 grpc_call_next_get_peer,
273 "deadline",
274};