blob: 852fbaf0036cd244306f363b94048d45421aebe4 [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>
38#include <grpc/support/time.h>
39
40#include "src/core/lib/iomgr/timer.h"
41
42// Used for both client and server filters.
43typedef struct channel_data {
44} channel_data;
45
46// Call data used for both client and server filter.
47typedef struct base_call_data {
Mark D. Rothd2b45332016-08-26 11:18:00 -070048 // We take a reference to the call stack for the timer callback.
Mark D. Roth14c072c2016-08-26 08:31:34 -070049 grpc_call_stack* call_stack;
Mark D. Rothd2b45332016-08-26 11:18:00 -070050 // True if the timer callback is currently pending.
Mark D. Roth14c072c2016-08-26 08:31:34 -070051 bool timer_pending;
Mark D. Rothd2b45332016-08-26 11:18:00 -070052 // The deadline timer.
Mark D. Roth14c072c2016-08-26 08:31:34 -070053 grpc_timer timer;
Mark D. Rothd2b45332016-08-26 11:18:00 -070054 // Closure to invoke when the call is complete.
55 // We use this to cancel the timer.
56 grpc_closure on_complete;
57 // The original on_complete closure, which we chain to after our own
58 // closure is invoked.
59 grpc_closure* next_on_complete;
Mark D. Roth14c072c2016-08-26 08:31:34 -070060} base_call_data;
61
62// Additional call data used only for the server filter.
63typedef struct server_call_data {
64 base_call_data base; // Must be first.
65 // The closure for receiving initial metadata.
66 grpc_closure recv_initial_metadata_ready;
67 // Received initial metadata batch.
68 grpc_metadata_batch* recv_initial_metadata;
69 // The original recv_initial_metadata_ready closure, which we chain to
70 // after our own closure is invoked.
71 grpc_closure* next_recv_initial_metadata_ready;
72} server_call_data;
73
74// Constructor for channel_data. Used for both client and server filters.
75static void init_channel_elem(grpc_exec_ctx* exec_ctx,
76 grpc_channel_element* elem,
77 grpc_channel_element_args* args) {
78 GPR_ASSERT(!args->is_last);
79}
80
81// Destructor for channel_data. Used for both client and server filters.
82static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
83 grpc_channel_element* elem) {
84}
85
86// Constructor for call_data. Used for both client and server filters.
87static grpc_error *init_call_elem(grpc_exec_ctx* exec_ctx,
88 grpc_call_element* elem,
89 grpc_call_element_args* args) {
90 base_call_data* calld = elem->call_data;
91 // Note: size of call data is different between client and server.
92 memset(calld, 0, elem->filter->sizeof_call_data);
93 calld->call_stack = args->call_stack;
94 return GRPC_ERROR_NONE;
95}
96
97// Destructor for call_data. Used for both client and server filters.
98static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
99 const grpc_call_final_info* final_info,
100 void* and_free_memory) {
Mark D. Roth14c072c2016-08-26 08:31:34 -0700101}
102
103// Timer callback.
104static void timer_callback(grpc_exec_ctx *exec_ctx, void *arg,
105 grpc_error *error) {
106 grpc_call_element* elem = arg;
107 base_call_data* calld = elem->call_data;
108 calld->timer_pending = false;
109 if (error != GRPC_ERROR_CANCELLED) {
Mark D. Roth14c072c2016-08-26 08:31:34 -0700110 gpr_slice message = gpr_slice_from_static_string("Deadline Exceeded");
111 grpc_call_element_send_cancel_with_message(
112 exec_ctx, elem, GRPC_STATUS_DEADLINE_EXCEEDED, &message);
113 }
Mark D. Roth14c072c2016-08-26 08:31:34 -0700114 GRPC_CALL_STACK_UNREF(exec_ctx, calld->call_stack, "deadline");
115}
116
117// Starts the deadline timer.
118static void start_timer_if_needed(grpc_exec_ctx *exec_ctx,
119 grpc_call_element* elem,
120 gpr_timespec deadline) {
121 base_call_data* calld = elem->call_data;
122 deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
123 if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) {
124 // Take a reference to the call stack, to be owned by the timer.
Mark D. Roth14c072c2016-08-26 08:31:34 -0700125 GRPC_CALL_STACK_REF(calld->call_stack, "deadline");
126 grpc_timer_init(exec_ctx, &calld->timer, deadline, timer_callback, elem,
127 gpr_now(GPR_CLOCK_MONOTONIC));
128 calld->timer_pending = true;
129 }
130}
131
Mark D. Rothd2b45332016-08-26 11:18:00 -0700132// Callback run when the call is complete.
133static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
134 base_call_data* calld = arg;
135 if (calld->timer_pending) {
136 grpc_timer_cancel(exec_ctx, &calld->timer);
137 calld->timer_pending = false;
138 }
139 // Invoke the next callback.
140 calld->next_on_complete->cb(exec_ctx, calld->next_on_complete->cb_arg, error);
141}
142
Mark D. Roth14c072c2016-08-26 08:31:34 -0700143// Method for starting a call op for client filter.
144static void client_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
145 grpc_call_element* elem,
146 grpc_transport_stream_op* op) {
Mark D. Rothd2b45332016-08-26 11:18:00 -0700147 base_call_data* calld = elem->call_data;
Mark D. Roth14c072c2016-08-26 08:31:34 -0700148 // If we're sending initial metadata, get the deadline from the metadata
149 // and start the timer if needed.
150 if (op->send_initial_metadata != NULL) {
151 start_timer_if_needed(exec_ctx, elem,
152 op->send_initial_metadata->deadline);
153 }
Mark D. Rothd2b45332016-08-26 11:18:00 -0700154 // Make sure we know when the call is complete, so that we can cancel
155 // the timer.
156 if (op->recv_trailing_metadata != NULL) {
157 calld->next_on_complete = op->on_complete;
158 grpc_closure_init(&calld->on_complete, on_complete, calld);
159 op->on_complete = &calld->on_complete;
160 }
Mark D. Roth14c072c2016-08-26 08:31:34 -0700161 // Chain to next filter.
162 grpc_call_next_op(exec_ctx, elem, op);
163}
164
165// Callback for receiving initial metadata on the server.
166static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg,
167 grpc_error *error) {
168 grpc_call_element* elem = arg;
169 server_call_data* calld = elem->call_data;
170 // Get deadline from metadata and start the timer if needed.
171 start_timer_if_needed(exec_ctx, elem,
172 calld->recv_initial_metadata->deadline);
173 // Invoke the next callback.
174 calld->next_recv_initial_metadata_ready->cb(
175 exec_ctx, calld->next_recv_initial_metadata_ready->cb_arg, error);
176}
177
178// Method for starting a call op for server filter.
179static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
180 grpc_call_element* elem,
181 grpc_transport_stream_op* op) {
182 server_call_data* calld = elem->call_data;
183 // If we're receiving initial metadata, we need to get the deadline
184 // from the recv_initial_metadata_ready callback. So we inject our
185 // own callback into that hook.
186 if (op->recv_initial_metadata_ready != NULL) {
187 calld->next_recv_initial_metadata_ready = op->recv_initial_metadata_ready;
188 calld->recv_initial_metadata = op->recv_initial_metadata;
189 grpc_closure_init(&calld->recv_initial_metadata_ready,
190 recv_initial_metadata_ready, elem);
191 op->recv_initial_metadata_ready = &calld->recv_initial_metadata_ready;
192 }
Mark D. Rothd2b45332016-08-26 11:18:00 -0700193 // Make sure we know when the call is complete, so that we can cancel
194 // the timer.
195 if (op->send_trailing_metadata != NULL) {
196 calld->base.next_on_complete = op->on_complete;
197 grpc_closure_init(&calld->base.on_complete, on_complete, calld);
198 op->on_complete = &calld->base.on_complete;
199 }
Mark D. Roth14c072c2016-08-26 08:31:34 -0700200 // Chain to next filter.
201 grpc_call_next_op(exec_ctx, elem, op);
202}
203
204const grpc_channel_filter grpc_client_deadline_filter = {
205 client_start_transport_stream_op,
206 grpc_channel_next_op,
207 sizeof(base_call_data),
208 init_call_elem,
209 grpc_call_stack_ignore_set_pollset_or_pollset_set,
210 destroy_call_elem,
211 sizeof(channel_data),
212 init_channel_elem,
213 destroy_channel_elem,
214 grpc_call_next_get_peer,
215 "deadline",
216};
217
218const grpc_channel_filter grpc_server_deadline_filter = {
219 server_start_transport_stream_op,
220 grpc_channel_next_op,
221 sizeof(server_call_data),
222 init_call_elem,
223 grpc_call_stack_ignore_set_pollset_or_pollset_set,
224 destroy_call_elem,
225 sizeof(channel_data),
226 init_channel_elem,
227 destroy_channel_elem,
228 grpc_call_next_get_peer,
229 "deadline",
230};