blob: 60c1ab3edd6046ae2f752c912ced84149017293a [file] [log] [blame]
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <grpc/support/port_platform.h>
35
36#ifdef GPR_WINSOCK_SOCKET
37
38#include "src/core/iomgr/sockaddr_win32.h"
39
40#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
42#include <grpc/support/log_win32.h>
43#include <grpc/support/slice_buffer.h>
Masood Malekghassemi701af602015-06-03 15:01:17 -070044#include <grpc/support/string_util.h>
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010045#include <grpc/support/useful.h>
46
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010047#include "src/core/iomgr/alarm.h"
Nicolas Noble45e67a32015-02-09 16:20:49 -080048#include "src/core/iomgr/iocp_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010049#include "src/core/iomgr/sockaddr.h"
50#include "src/core/iomgr/sockaddr_utils.h"
Nicolas Noble45e67a32015-02-09 16:20:49 -080051#include "src/core/iomgr/socket_windows.h"
52#include "src/core/iomgr/tcp_client.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010053
54static int set_non_block(SOCKET sock) {
55 int status;
56 unsigned long param = 1;
57 DWORD ret;
Craig Tillerd6c98df2015-08-18 09:33:44 -070058 status =
59 WSAIoctl(sock, FIONBIO, &param, sizeof(param), NULL, 0, &ret, NULL, NULL);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010060 return status == 0;
61}
62
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010063static int set_dualstack(SOCKET sock) {
64 int status;
65 unsigned long param = 0;
Craig Tillerd6c98df2015-08-18 09:33:44 -070066 status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&param,
67 sizeof(param));
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010068 return status == 0;
69}
70
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010071int grpc_tcp_prepare_socket(SOCKET sock) {
Craig Tillerd6c98df2015-08-18 09:33:44 -070072 if (!set_non_block(sock)) return 0;
73 if (!set_dualstack(sock)) return 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010074 return 1;
75}
76
77typedef struct grpc_tcp {
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020078 /* This is our C++ class derivation emulation. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010079 grpc_endpoint base;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020080 /* The one socket this endpoint is using. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010081 grpc_winsocket *socket;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020082 /* Refcounting how many operations are in progress. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010083 gpr_refcount refcount;
84
Craig Tillerec9acab2015-08-21 13:06:00 -070085 grpc_iomgr_closure *read_cb;
86 grpc_iomgr_closure *write_cb;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010087 gpr_slice read_slice;
Craig Tillerec9acab2015-08-21 13:06:00 -070088 gpr_slice_buffer *write_slices;
89 gpr_slice_buffer *read_slices;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010090
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020091 /* The IO Completion Port runs from another thread. We need some mechanism
92 to protect ourselves when requesting a shutdown. */
93 gpr_mu mu;
94 int shutting_down;
Craig Tiller81bcc4c2015-07-20 14:04:18 -070095
96 char *peer_string;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010097} grpc_tcp;
98
Craig Tiller0882a352015-08-26 13:21:14 -070099static void tcp_free(grpc_tcp *tcp) {
100 grpc_winsocket_orphan(tcp->socket);
101 gpr_mu_destroy(&tcp->mu);
102 gpr_free(tcp->peer_string);
103 gpr_free(tcp);
104}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100105
Craig Tiller0882a352015-08-26 13:21:14 -0700106#define GRPC_TCP_REFCOUNT_DEBUG
107#ifdef GRPC_TCP_REFCOUNT_DEBUG
108#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__)
109#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
110static void tcp_unref(grpc_tcp *tcp, const char *reason, const char *file,
111 int line) {
112 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp,
113 reason, tcp->refcount.count, tcp->refcount.count - 1);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100114 if (gpr_unref(&tcp->refcount)) {
Craig Tiller0882a352015-08-26 13:21:14 -0700115 tcp_free(tcp);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100116 }
117}
118
Craig Tiller0882a352015-08-26 13:21:14 -0700119static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file,
120 int line) {
121 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp,
122 reason, tcp->refcount.count, tcp->refcount.count + 1);
123 gpr_ref(&tcp->refcount);
124}
125#else
126#define TCP_UNREF(tcp, reason) tcp_unref((tcp))
127#define TCP_REF(tcp, reason) tcp_ref((tcp))
128static void tcp_unref(grpc_tcp *tcp) {
129 if (gpr_unref(&tcp->refcount)) {
130 tcp_free(tcp);
131 }
132}
133
134static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); }
135#endif
136
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200137/* Asynchronous callback from the IOCP, or the background thread. */
Craig Tillerec9acab2015-08-21 13:06:00 -0700138static int on_read(grpc_tcp *tcp, int from_iocp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100139 grpc_winsocket *socket = tcp->socket;
140 gpr_slice sub;
141 gpr_slice *slice = NULL;
142 size_t nslices = 0;
Craig Tillerec9acab2015-08-21 13:06:00 -0700143 int success;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100144 grpc_winsocket_callback_info *info = &socket->read_info;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200145 int do_abort = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100146
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200147 gpr_mu_lock(&tcp->mu);
148 if (!from_iocp || tcp->shutting_down) {
149 /* If we are here with from_iocp set to true, it means we got raced to
150 shutting down the endpoint. No actual abort callback will happen
151 though, so we're going to do it from here. */
152 do_abort = 1;
153 }
154 gpr_mu_unlock(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100155
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200156 if (do_abort) {
Nicolas Noblee1445362015-05-11 17:40:26 -0700157 if (from_iocp) {
158 tcp->socket->read_info.outstanding = 0;
159 gpr_slice_unref(tcp->read_slice);
160 }
Craig Tillerec9acab2015-08-21 13:06:00 -0700161 return 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100162 }
163
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200164 GPR_ASSERT(tcp->socket->read_info.outstanding);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100165
166 if (socket->read_info.wsa_error != 0) {
Jan Tattermuschd9e79432015-07-12 01:27:30 -0700167 if (socket->read_info.wsa_error != WSAECONNRESET) {
168 char *utf8_message = gpr_format_message(info->wsa_error);
169 gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
170 gpr_free(utf8_message);
171 }
Craig Tillerec9acab2015-08-21 13:06:00 -0700172 success = 0;
Jan Tattermuscha4d9f262015-08-25 12:59:21 -0700173 gpr_slice_unref(tcp->read_slice);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100174 } else {
175 if (info->bytes_transfered != 0) {
Jan Tattermuschb9100952015-06-18 11:35:53 -0700176 sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
Craig Tillerec9acab2015-08-21 13:06:00 -0700177 gpr_slice_buffer_add(tcp->read_slices, sub);
178 success = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100179 } else {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100180 gpr_slice_unref(tcp->read_slice);
Craig Tillerec9acab2015-08-21 13:06:00 -0700181 success = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100182 }
183 }
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200184
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200185 tcp->socket->read_info.outstanding = 0;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200186
Craig Tillerec9acab2015-08-21 13:06:00 -0700187 return success;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100188}
189
Craig Tillerec9acab2015-08-21 13:06:00 -0700190static void on_read_cb(void *tcpp, int from_iocp) {
191 grpc_tcp *tcp = tcpp;
192 grpc_iomgr_closure *cb = tcp->read_cb;
193 int success = on_read(tcp, from_iocp);
194 tcp->read_cb = NULL;
Craig Tiller0882a352015-08-26 13:21:14 -0700195 TCP_UNREF(tcp, "read");
196 if (cb) {
197 cb->cb(cb->cb_arg, success);
198 }
Craig Tillerec9acab2015-08-21 13:06:00 -0700199}
200
Craig Tiller1c090692015-08-21 13:07:01 -0700201static grpc_endpoint_op_status win_read(grpc_endpoint *ep,
202 gpr_slice_buffer *read_slices,
203 grpc_iomgr_closure *cb) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700204 grpc_tcp *tcp = (grpc_tcp *)ep;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100205 grpc_winsocket *handle = tcp->socket;
206 grpc_winsocket_callback_info *info = &handle->read_info;
207 int status;
208 DWORD bytes_read = 0;
209 DWORD flags = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100210 WSABUF buffer;
211
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200212 GPR_ASSERT(!tcp->socket->read_info.outstanding);
213 if (tcp->shutting_down) {
Craig Tillerec9acab2015-08-21 13:06:00 -0700214 return GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200215 }
Craig Tiller0882a352015-08-26 13:21:14 -0700216
217 TCP_REF(tcp, "read");
218
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200219 tcp->socket->read_info.outstanding = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100220 tcp->read_cb = cb;
Craig Tillerec9acab2015-08-21 13:06:00 -0700221 tcp->read_slices = read_slices;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100222
223 tcp->read_slice = gpr_slice_malloc(8192);
224
225 buffer.len = GPR_SLICE_LENGTH(tcp->read_slice);
Nicolas Noblecfd60732015-03-18 16:27:43 -0700226 buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100227
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200228 /* First let's try a synchronous, non-blocking read. */
Craig Tillerd6c98df2015-08-18 09:33:44 -0700229 status =
230 WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100231 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
232
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200233 /* Did we get data immediately ? Yay. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100234 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tiller0882a352015-08-26 13:21:14 -0700235 int ok;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100236 info->bytes_transfered = bytes_read;
Craig Tiller0882a352015-08-26 13:21:14 -0700237 ok = on_read(tcp, 1);
238 TCP_UNREF(tcp, "read");
239 return ok ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100240 }
241
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200242 /* Otherwise, let's retry, by queuing a read. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100243 memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
244 status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
245 &info->overlapped, NULL);
246
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200247 if (status != 0) {
248 int wsa_error = WSAGetLastError();
249 if (wsa_error != WSA_IO_PENDING) {
Craig Tiller0882a352015-08-26 13:21:14 -0700250 int ok;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200251 info->wsa_error = wsa_error;
Craig Tiller0882a352015-08-26 13:21:14 -0700252 ok = on_read(tcp, 1);
253 return ok ? GRPC_ENDPOINT_DONE : GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200254 }
255 }
256
Craig Tillerec9acab2015-08-21 13:06:00 -0700257 grpc_socket_notify_on_read(tcp->socket, on_read_cb, tcp);
258 return GRPC_ENDPOINT_PENDING;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100259}
260
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200261/* Asynchronous callback from the IOCP, or the background thread. */
262static void on_write(void *tcpp, int from_iocp) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700263 grpc_tcp *tcp = (grpc_tcp *)tcpp;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100264 grpc_winsocket *handle = tcp->socket;
265 grpc_winsocket_callback_info *info = &handle->write_info;
Craig Tillerec9acab2015-08-21 13:06:00 -0700266 grpc_iomgr_closure *cb;
267 int success;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200268 int do_abort = 0;
269
270 gpr_mu_lock(&tcp->mu);
Nicolas Noblee1445362015-05-11 17:40:26 -0700271 cb = tcp->write_cb;
272 tcp->write_cb = NULL;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200273 if (!from_iocp || tcp->shutting_down) {
274 /* If we are here with from_iocp set to true, it means we got raced to
275 shutting down the endpoint. No actual abort callback will happen
276 though, so we're going to do it from here. */
277 do_abort = 1;
278 }
279 gpr_mu_unlock(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100280
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200281 if (do_abort) {
Nicolas Noblee1445362015-05-11 17:40:26 -0700282 if (from_iocp) {
283 tcp->socket->write_info.outstanding = 0;
Nicolas Noblee1445362015-05-11 17:40:26 -0700284 }
Craig Tiller0882a352015-08-26 13:21:14 -0700285 TCP_UNREF(tcp, "write");
Craig Tillerec9acab2015-08-21 13:06:00 -0700286 if (cb) {
287 cb->cb(cb->cb_arg, 0);
288 }
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100289 return;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100290 }
291
Nicolas Noblee1445362015-05-11 17:40:26 -0700292 GPR_ASSERT(tcp->socket->write_info.outstanding);
293
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100294 if (info->wsa_error != 0) {
Jan Tattermuschd9e79432015-07-12 01:27:30 -0700295 if (info->wsa_error != WSAECONNRESET) {
296 char *utf8_message = gpr_format_message(info->wsa_error);
297 gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
298 gpr_free(utf8_message);
299 }
Craig Tillerec9acab2015-08-21 13:06:00 -0700300 success = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100301 } else {
Craig Tillerec9acab2015-08-21 13:06:00 -0700302 GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
303 success = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100304 }
305
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200306 tcp->socket->write_info.outstanding = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100307
Craig Tiller0882a352015-08-26 13:21:14 -0700308 TCP_UNREF(tcp, "write");
Craig Tillerec9acab2015-08-21 13:06:00 -0700309 cb->cb(cb->cb_arg, success);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100310}
311
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200312/* Initiates a write. */
Craig Tillerec9acab2015-08-21 13:06:00 -0700313static grpc_endpoint_op_status win_write(grpc_endpoint *ep,
Craig Tiller1c090692015-08-21 13:07:01 -0700314 gpr_slice_buffer *slices,
315 grpc_iomgr_closure *cb) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700316 grpc_tcp *tcp = (grpc_tcp *)ep;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100317 grpc_winsocket *socket = tcp->socket;
318 grpc_winsocket_callback_info *info = &socket->write_info;
319 unsigned i;
320 DWORD bytes_sent;
321 int status;
322 WSABUF local_buffers[16];
323 WSABUF *allocated = NULL;
324 WSABUF *buffers = local_buffers;
325
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200326 GPR_ASSERT(!tcp->socket->write_info.outstanding);
327 if (tcp->shutting_down) {
Craig Tillerec9acab2015-08-21 13:06:00 -0700328 return GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200329 }
Craig Tiller0882a352015-08-26 13:21:14 -0700330 TCP_REF(tcp, "write");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100331
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200332 tcp->socket->write_info.outstanding = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100333 tcp->write_cb = cb;
Craig Tillerec9acab2015-08-21 13:06:00 -0700334 tcp->write_slices = slices;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200335
Craig Tillerec9acab2015-08-21 13:06:00 -0700336 if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) {
337 buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100338 allocated = buffers;
339 }
340
Craig Tillerec9acab2015-08-21 13:06:00 -0700341 for (i = 0; i < tcp->write_slices->count; i++) {
342 buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]);
343 buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices->slices[i]);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100344 }
345
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200346 /* First, let's try a synchronous, non-blocking write. */
Craig Tillerec9acab2015-08-21 13:06:00 -0700347 status = WSASend(socket->socket, buffers, tcp->write_slices->count,
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100348 &bytes_sent, 0, NULL, NULL);
349 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
350
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200351 /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy
352 connection that has its send queue filled up. But if we don't, then we can
353 avoid doing an async write operation at all. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100354 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tillerec9acab2015-08-21 13:06:00 -0700355 grpc_endpoint_op_status ret = GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100356 if (status == 0) {
Craig Tillerec9acab2015-08-21 13:06:00 -0700357 ret = GRPC_ENDPOINT_DONE;
358 GPR_ASSERT(bytes_sent == tcp->write_slices->length);
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100359 } else {
Jan Tattermuschd9e79432015-07-12 01:27:30 -0700360 if (socket->read_info.wsa_error != WSAECONNRESET) {
361 char *utf8_message = gpr_format_message(info->wsa_error);
362 gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
363 gpr_free(utf8_message);
364 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100365 }
366 if (allocated) gpr_free(allocated);
Nicolas "Pixel" Noblee34a45a2015-05-07 18:41:07 +0200367 tcp->socket->write_info.outstanding = 0;
Craig Tiller0882a352015-08-26 13:21:14 -0700368 TCP_UNREF(tcp, "write");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100369 return ret;
370 }
371
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200372 /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same
373 operation, this time asynchronously. */
zeliard3874ad02015-04-30 16:05:45 +0900374 memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED));
Craig Tillerec9acab2015-08-21 13:06:00 -0700375 status = WSASend(socket->socket, buffers, tcp->write_slices->count,
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100376 &bytes_sent, 0, &socket->write_info.overlapped, NULL);
377 if (allocated) gpr_free(allocated);
378
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200379 if (status != 0) {
380 int wsa_error = WSAGetLastError();
381 if (wsa_error != WSA_IO_PENDING) {
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200382 tcp->socket->write_info.outstanding = 0;
Craig Tiller0882a352015-08-26 13:21:14 -0700383 TCP_UNREF(tcp, "write");
Craig Tillerec9acab2015-08-21 13:06:00 -0700384 return GRPC_ENDPOINT_ERROR;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200385 }
386 }
387
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200388 /* As all is now setup, we can now ask for the IOCP notification. It may
389 trigger the callback immediately however, but no matter. */
Nicolas Noble45e67a32015-02-09 16:20:49 -0800390 grpc_socket_notify_on_write(socket, on_write, tcp);
Craig Tillerec9acab2015-08-21 13:06:00 -0700391 return GRPC_ENDPOINT_PENDING;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100392}
393
Nicolas "Pixel" Noblef29e9db2015-07-29 06:00:22 +0200394static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *ps) {
Jan Tattermuschdeab29e2015-08-03 12:32:44 -0700395 grpc_tcp *tcp;
Craig Tillerd6c98df2015-08-18 09:33:44 -0700396 (void)ps;
397 tcp = (grpc_tcp *)ep;
Nicolas "Pixel" Noblef29e9db2015-07-29 06:00:22 +0200398 grpc_iocp_add_socket(tcp->socket);
399}
400
401static void win_add_to_pollset_set(grpc_endpoint *ep, grpc_pollset_set *pss) {
Jan Tattermuschdeab29e2015-08-03 12:32:44 -0700402 grpc_tcp *tcp;
Craig Tillerd6c98df2015-08-18 09:33:44 -0700403 (void)pss;
404 tcp = (grpc_tcp *)ep;
Nicolas Noble45e67a32015-02-09 16:20:49 -0800405 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100406}
407
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200408/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks
409 for the potential read and write operations. It is up to the caller to
410 guarantee this isn't called in parallel to a read or write request, so
411 we're not going to protect against these. However the IO Completion Port
412 callback will happen from another thread, so we need to protect against
413 concurrent access of the data structure in that regard. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100414static void win_shutdown(grpc_endpoint *ep) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700415 grpc_tcp *tcp = (grpc_tcp *)ep;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200416 gpr_mu_lock(&tcp->mu);
417 /* At that point, what may happen is that we're already inside the IOCP
418 callback. See the comments in on_read and on_write. */
419 tcp->shutting_down = 1;
Craig Tiller0882a352015-08-26 13:21:14 -0700420 grpc_winsocket_shutdown(tcp->socket);
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200421 gpr_mu_unlock(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100422}
423
424static void win_destroy(grpc_endpoint *ep) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700425 grpc_tcp *tcp = (grpc_tcp *)ep;
Craig Tiller0882a352015-08-26 13:21:14 -0700426 TCP_UNREF(tcp, "destroy");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100427}
428
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700429static char *win_get_peer(grpc_endpoint *ep) {
430 grpc_tcp *tcp = (grpc_tcp *)ep;
431 return gpr_strdup(tcp->peer_string);
432}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100433
Craig Tillerd6c98df2015-08-18 09:33:44 -0700434static grpc_endpoint_vtable vtable = {
Craig Tiller1c090692015-08-21 13:07:01 -0700435 win_read, win_write, win_add_to_pollset, win_add_to_pollset_set,
436 win_shutdown, win_destroy, win_get_peer};
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100437
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700438grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) {
Craig Tillerd6c98df2015-08-18 09:33:44 -0700439 grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100440 memset(tcp, 0, sizeof(grpc_tcp));
441 tcp->base.vtable = &vtable;
442 tcp->socket = socket;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200443 gpr_mu_init(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100444 gpr_ref_init(&tcp->refcount, 1);
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700445 tcp->peer_string = gpr_strdup(peer_string);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100446 return &tcp->base;
447}
448
Craig Tillerd6c98df2015-08-18 09:33:44 -0700449#endif /* GPR_WINSOCK_SOCKET */