blob: b3cb442f18f83675c01304d7e4f8a132eeb8bc49 [file] [log] [blame]
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010016 *
17 */
18
Alexander Polcyndb3e8982018-02-21 16:59:24 -080019#include <grpc/support/port_platform.h>
20
murgatroid9954070892016-08-08 17:01:18 -070021#include "src/core/lib/iomgr/port.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010022
murgatroid99623dd4f2016-08-08 17:31:27 -070023#ifdef GRPC_WINSOCK_SOCKET
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010024
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020025#include <limits.h>
26
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -070027#include "src/core/lib/iomgr/network_status_tracker.h"
Yuchen Zeng12dfdc32016-04-26 22:05:41 -070028#include "src/core/lib/iomgr/sockaddr_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010029
Craig Tiller28b72422016-10-26 21:15:29 -070030#include <grpc/slice_buffer.h>
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010031#include <grpc/support/alloc.h>
32#include <grpc/support/log.h>
Yuchen Zeng12dfdc32016-04-26 22:05:41 -070033#include <grpc/support/log_windows.h>
Masood Malekghassemi701af602015-06-03 15:01:17 -070034#include <grpc/support/string_util.h>
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010035
Vijay Paid4d0a302018-01-25 13:24:03 -080036#include "src/core/lib/gpr/useful.h"
Craig Tiller9533d042016-03-25 17:11:06 -070037#include "src/core/lib/iomgr/iocp_windows.h"
38#include "src/core/lib/iomgr/sockaddr.h"
39#include "src/core/lib/iomgr/sockaddr_utils.h"
40#include "src/core/lib/iomgr/socket_windows.h"
41#include "src/core/lib/iomgr/tcp_client.h"
Yash Tibrewal547653e2017-09-25 21:20:19 -070042#include "src/core/lib/iomgr/tcp_windows.h"
Craig Tiller9533d042016-03-25 17:11:06 -070043#include "src/core/lib/iomgr/timer.h"
Craig Tillerfede4d42016-12-06 20:20:10 -080044#include "src/core/lib/slice/slice_internal.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010045
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020046#if defined(__MSYS__) && defined(GPR_ARCH_64)
47/* Nasty workaround for nasty bug when using the 64 bits msys compiler
48 in conjunction with Microsoft Windows headers. */
49#define GRPC_FIONBIO _IOW('f', 126, uint32_t)
50#else
51#define GRPC_FIONBIO FIONBIO
52#endif
53
kpayson64539f5062018-03-12 19:16:30 -070054extern grpc_core::TraceFlag grpc_tcp_trace;
ncteisen0e3aee32017-06-08 16:32:24 -070055
Alex Polcyn5cd8b1e2018-06-16 04:08:55 +000056grpc_error* grpc_tcp_set_non_block(SOCKET sock) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010057 int status;
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020058 uint32_t param = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010059 DWORD ret;
Nicolas "Pixel" Noble344f55b2016-04-15 07:52:25 +020060 status = WSAIoctl(sock, GRPC_FIONBIO, &param, sizeof(param), NULL, 0, &ret,
61 NULL, NULL);
Craig Tiller3e149f32016-05-17 16:11:04 -070062 return status == 0
63 ? GRPC_ERROR_NONE
64 : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010065}
66
Craig Tillerbaa14a92017-11-03 09:09:36 -070067static grpc_error* set_dualstack(SOCKET sock) {
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010068 int status;
69 unsigned long param = 0;
Craig Tillerbaa14a92017-11-03 09:09:36 -070070 status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&param,
Craig Tillera82950e2015-09-22 12:33:20 -070071 sizeof(param));
Craig Tiller3e149f32016-05-17 16:11:04 -070072 return status == 0
73 ? GRPC_ERROR_NONE
74 : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010075}
76
Ara Ayvazyanf5f5ee32018-04-26 16:23:06 -070077static grpc_error* enable_loopback_fast_path(SOCKET sock) {
78 int status;
79 uint32_t param = 1;
80 DWORD ret;
81 status = WSAIoctl(sock, /*SIO_LOOPBACK_FAST_PATH==*/_WSAIOW(IOC_VENDOR, 16),
82 &param, sizeof(param), NULL, 0, &ret, 0, 0);
83 if (status == SOCKET_ERROR) {
84 status = WSAGetLastError();
85 }
86 return status == 0 || status == WSAEOPNOTSUPP
87 ? GRPC_ERROR_NONE
88 : GRPC_WSA_ERROR(status, "WSAIoctl(SIO_LOOPBACK_FAST_PATH)");
89}
90
Craig Tillerbaa14a92017-11-03 09:09:36 -070091grpc_error* grpc_tcp_prepare_socket(SOCKET sock) {
92 grpc_error* err;
Alex Polcyn5cd8b1e2018-06-16 04:08:55 +000093 err = grpc_tcp_set_non_block(sock);
Craig Tillera41ac572016-05-17 16:08:17 -070094 if (err != GRPC_ERROR_NONE) return err;
95 err = set_dualstack(sock);
96 if (err != GRPC_ERROR_NONE) return err;
Ara Ayvazyanf5f5ee32018-04-26 16:23:06 -070097 err = enable_loopback_fast_path(sock);
98 if (err != GRPC_ERROR_NONE) return err;
Craig Tillera41ac572016-05-17 16:08:17 -070099 return GRPC_ERROR_NONE;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100100}
101
Craig Tillera82950e2015-09-22 12:33:20 -0700102typedef struct grpc_tcp {
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200103 /* This is our C++ class derivation emulation. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100104 grpc_endpoint base;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200105 /* The one socket this endpoint is using. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700106 grpc_winsocket* socket;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200107 /* Refcounting how many operations are in progress. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100108 gpr_refcount refcount;
109
Craig Tiller82f9bd82015-09-23 09:31:51 -0700110 grpc_closure on_read;
111 grpc_closure on_write;
112
Craig Tillerbaa14a92017-11-03 09:09:36 -0700113 grpc_closure* read_cb;
114 grpc_closure* write_cb;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700115 grpc_slice read_slice;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700116 grpc_slice_buffer* write_slices;
117 grpc_slice_buffer* read_slices;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100118
Craig Tillerbaa14a92017-11-03 09:09:36 -0700119 grpc_resource_user* resource_user;
Craig Tiller290e9a72016-10-28 08:17:00 -0700120
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200121 /* The IO Completion Port runs from another thread. We need some mechanism
122 to protect ourselves when requesting a shutdown. */
123 gpr_mu mu;
124 int shutting_down;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700125 grpc_error* shutdown_error;
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700126
Craig Tillerbaa14a92017-11-03 09:09:36 -0700127 char* peer_string;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100128} grpc_tcp;
129
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800130static void tcp_free(grpc_tcp* tcp) {
Craig Tillera82950e2015-09-22 12:33:20 -0700131 grpc_winsocket_destroy(tcp->socket);
132 gpr_mu_destroy(&tcp->mu);
133 gpr_free(tcp->peer_string);
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800134 grpc_resource_user_unref(tcp->resource_user);
Craig Tiller6ba80f92017-01-27 11:58:32 -0800135 if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error);
Craig Tillera82950e2015-09-22 12:33:20 -0700136 gpr_free(tcp);
Craig Tillerb0298592015-08-27 07:38:01 -0700137}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100138
ncteisen0e3aee32017-06-08 16:32:24 -0700139#ifndef NDEBUG
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800140#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__)
Craig Tillerb0298592015-08-27 07:38:01 -0700141#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800142static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file,
143 int line) {
ncteisen9ffb1492017-11-10 14:00:49 -0800144 if (grpc_tcp_trace.enabled()) {
ncteisen0e3aee32017-06-08 16:32:24 -0700145 gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
ncteisend39010e2017-06-08 17:08:07 -0700146 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
147 "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
148 val - 1);
ncteisen0e3aee32017-06-08 16:32:24 -0700149 }
Craig Tillera82950e2015-09-22 12:33:20 -0700150 if (gpr_unref(&tcp->refcount)) {
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800151 tcp_free(tcp);
Craig Tillera82950e2015-09-22 12:33:20 -0700152 }
Craig Tiller0882a352015-08-26 13:21:14 -0700153}
154
Craig Tillerbaa14a92017-11-03 09:09:36 -0700155static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
Craig Tillera82950e2015-09-22 12:33:20 -0700156 int line) {
ncteisen9ffb1492017-11-10 14:00:49 -0800157 if (grpc_tcp_trace.enabled()) {
ncteisen0e3aee32017-06-08 16:32:24 -0700158 gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
ncteisend39010e2017-06-08 17:08:07 -0700159 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
160 "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
161 val + 1);
ncteisen0e3aee32017-06-08 16:32:24 -0700162 }
Craig Tillera82950e2015-09-22 12:33:20 -0700163 gpr_ref(&tcp->refcount);
Craig Tillerb0298592015-08-27 07:38:01 -0700164}
165#else
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800166#define TCP_UNREF(tcp, reason) tcp_unref((tcp))
Craig Tillerb0298592015-08-27 07:38:01 -0700167#define TCP_REF(tcp, reason) tcp_ref((tcp))
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800168static void tcp_unref(grpc_tcp* tcp) {
Craig Tillera82950e2015-09-22 12:33:20 -0700169 if (gpr_unref(&tcp->refcount)) {
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800170 tcp_free(tcp);
Craig Tillera82950e2015-09-22 12:33:20 -0700171 }
Craig Tillerb0298592015-08-27 07:38:01 -0700172}
173
Craig Tillerbaa14a92017-11-03 09:09:36 -0700174static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); }
Craig Tillerb0298592015-08-27 07:38:01 -0700175#endif
176
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200177/* Asynchronous callback from the IOCP, or the background thread. */
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800178static void on_read(void* tcpp, grpc_error* error) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700179 grpc_tcp* tcp = (grpc_tcp*)tcpp;
180 grpc_closure* cb = tcp->read_cb;
181 grpc_winsocket* socket = tcp->socket;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700182 grpc_slice sub;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700183 grpc_winsocket_callback_info* info = &socket->read_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100184
Craig Tillera41ac572016-05-17 16:08:17 -0700185 GRPC_ERROR_REF(error);
186
187 if (error == GRPC_ERROR_NONE) {
Jan Tattermusch64124792016-03-25 09:16:22 -0700188 if (info->wsa_error != 0 && !tcp->shutting_down) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700189 char* utf8_message = gpr_format_message(info->wsa_error);
ncteisen4b36a3d2017-03-13 19:08:06 -0700190 error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message);
Craig Tillera41ac572016-05-17 16:08:17 -0700191 gpr_free(utf8_message);
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800192 grpc_slice_unref_internal(tcp->read_slice);
Craig Tillera82950e2015-09-22 12:33:20 -0700193 } else {
Nicolas "Pixel" Noble3a26c5b2015-09-22 00:07:14 +0200194 if (info->bytes_transfered != 0 && !tcp->shutting_down) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700195 sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
196 grpc_slice_buffer_add(tcp->read_slices, sub);
Craig Tillera82950e2015-09-22 12:33:20 -0700197 } else {
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800198 grpc_slice_unref_internal(tcp->read_slice);
Craig Tiller393a4d92017-01-27 12:01:44 -0800199 error = tcp->shutting_down
ncteisen4b36a3d2017-03-13 19:08:06 -0700200 ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
201 "TCP stream shutting down", &tcp->shutdown_error, 1)
202 : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream");
Craig Tillera82950e2015-09-22 12:33:20 -0700203 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100204 }
Craig Tillera82950e2015-09-22 12:33:20 -0700205 }
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200206
Craig Tillerb0298592015-08-27 07:38:01 -0700207 tcp->read_cb = NULL;
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800208 TCP_UNREF(tcp, "read");
209 GRPC_CLOSURE_SCHED(cb, error);
Craig Tillerb0298592015-08-27 07:38:01 -0700210}
211
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800212static void win_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices,
213 grpc_closure* cb) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700214 grpc_tcp* tcp = (grpc_tcp*)ep;
215 grpc_winsocket* handle = tcp->socket;
216 grpc_winsocket_callback_info* info = &handle->read_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100217 int status;
218 DWORD bytes_read = 0;
219 DWORD flags = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100220 WSABUF buffer;
221
Craig Tillera82950e2015-09-22 12:33:20 -0700222 if (tcp->shutting_down) {
ncteisen969b46e2017-06-08 14:57:11 -0700223 GRPC_CLOSURE_SCHED(
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800224 cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
225 "TCP socket is shutting down", &tcp->shutdown_error, 1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700226 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700227 }
Craig Tillerb0298592015-08-27 07:38:01 -0700228
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100229 tcp->read_cb = cb;
Craig Tillerb0298592015-08-27 07:38:01 -0700230 tcp->read_slices = read_slices;
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800231 grpc_slice_buffer_reset_and_unref_internal(read_slices);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100232
Craig Tiller423d6fd2017-04-12 13:15:45 -0700233 tcp->read_slice = GRPC_SLICE_MALLOC(8192);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100234
Craig Tiller618e67d2016-10-26 21:08:10 -0700235 buffer.len = (ULONG)GRPC_SLICE_LENGTH(
Craig Tiller620e9652015-12-14 12:02:50 -0800236 tcp->read_slice); // we know slice size fits in 32bit.
Craig Tillerbaa14a92017-11-03 09:09:36 -0700237 buffer.buf = (char*)GRPC_SLICE_START_PTR(tcp->read_slice);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100238
Craig Tiller82f9bd82015-09-23 09:31:51 -0700239 TCP_REF(tcp, "read");
240
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200241 /* First let's try a synchronous, non-blocking read. */
Craig Tillera82950e2015-09-22 12:33:20 -0700242 status =
243 WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL);
244 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100245
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200246 /* Did we get data immediately ? Yay. */
Craig Tillera82950e2015-09-22 12:33:20 -0700247 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tillera82950e2015-09-22 12:33:20 -0700248 info->bytes_transfered = bytes_read;
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800249 GRPC_CLOSURE_SCHED(&tcp->on_read, GRPC_ERROR_NONE);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700250 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700251 }
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200252
Craig Tiller45724b32015-09-22 10:42:19 -0700253 /* Otherwise, let's retry, by queuing a read. */
Craig Tillera82950e2015-09-22 12:33:20 -0700254 memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
255 status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
256 &info->overlapped, NULL);
Craig Tiller45724b32015-09-22 10:42:19 -0700257
Craig Tillera82950e2015-09-22 12:33:20 -0700258 if (status != 0) {
259 int wsa_error = WSAGetLastError();
260 if (wsa_error != WSA_IO_PENDING) {
Craig Tillera82950e2015-09-22 12:33:20 -0700261 info->wsa_error = wsa_error;
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800262 GRPC_CLOSURE_SCHED(&tcp->on_read,
Craig Tillerd4654562017-01-03 08:45:56 -0800263 GRPC_WSA_ERROR(info->wsa_error, "WSARecv"));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700264 return;
Craig Tiller45724b32015-09-22 10:42:19 -0700265 }
Craig Tillera82950e2015-09-22 12:33:20 -0700266 }
Craig Tiller45724b32015-09-22 10:42:19 -0700267
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800268 grpc_socket_notify_on_read(tcp->socket, &tcp->on_read);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100269}
270
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200271/* Asynchronous callback from the IOCP, or the background thread. */
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800272static void on_write(void* tcpp, grpc_error* error) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700273 grpc_tcp* tcp = (grpc_tcp*)tcpp;
274 grpc_winsocket* handle = tcp->socket;
275 grpc_winsocket_callback_info* info = &handle->write_info;
276 grpc_closure* cb;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200277
Craig Tillera41ac572016-05-17 16:08:17 -0700278 GRPC_ERROR_REF(error);
279
Craig Tillera82950e2015-09-22 12:33:20 -0700280 gpr_mu_lock(&tcp->mu);
Nicolas Noblee1445362015-05-11 17:40:26 -0700281 cb = tcp->write_cb;
282 tcp->write_cb = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700283 gpr_mu_unlock(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100284
Craig Tillera41ac572016-05-17 16:08:17 -0700285 if (error == GRPC_ERROR_NONE) {
Craig Tillera82950e2015-09-22 12:33:20 -0700286 if (info->wsa_error != 0) {
Craig Tillera41ac572016-05-17 16:08:17 -0700287 error = GRPC_WSA_ERROR(info->wsa_error, "WSASend");
Craig Tillera82950e2015-09-22 12:33:20 -0700288 } else {
289 GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
Nicolas Noblee1445362015-05-11 17:40:26 -0700290 }
Craig Tillera82950e2015-09-22 12:33:20 -0700291 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100292
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800293 TCP_UNREF(tcp, "write");
294 GRPC_CLOSURE_SCHED(cb, error);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100295}
296
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200297/* Initiates a write. */
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800298static void win_write(grpc_endpoint* ep, grpc_slice_buffer* slices,
Jan Tattermusch97ba9432018-08-27 12:43:20 +0200299 grpc_closure* cb) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700300 grpc_tcp* tcp = (grpc_tcp*)ep;
301 grpc_winsocket* socket = tcp->socket;
302 grpc_winsocket_callback_info* info = &socket->write_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100303 unsigned i;
304 DWORD bytes_sent;
305 int status;
306 WSABUF local_buffers[16];
Craig Tillerbaa14a92017-11-03 09:09:36 -0700307 WSABUF* allocated = NULL;
308 WSABUF* buffers = local_buffers;
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800309 size_t len;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100310
Craig Tillera82950e2015-09-22 12:33:20 -0700311 if (tcp->shutting_down) {
ncteisen969b46e2017-06-08 14:57:11 -0700312 GRPC_CLOSURE_SCHED(
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800313 cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
314 "TCP socket is shutting down", &tcp->shutdown_error, 1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700315 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700316 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100317
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100318 tcp->write_cb = cb;
Craig Tillerb0298592015-08-27 07:38:01 -0700319 tcp->write_slices = slices;
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800320 GPR_ASSERT(tcp->write_slices->count <= UINT_MAX);
Craig Tillera82950e2015-09-22 12:33:20 -0700321 if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700322 buffers = (WSABUF*)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count);
Craig Tillera82950e2015-09-22 12:33:20 -0700323 allocated = buffers;
324 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100325
Craig Tillera82950e2015-09-22 12:33:20 -0700326 for (i = 0; i < tcp->write_slices->count; i++) {
Craig Tiller618e67d2016-10-26 21:08:10 -0700327 len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]);
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800328 GPR_ASSERT(len <= ULONG_MAX);
Craig Tiller620e9652015-12-14 12:02:50 -0800329 buffers[i].len = (ULONG)len;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700330 buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]);
Craig Tillera82950e2015-09-22 12:33:20 -0700331 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100332
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200333 /* First, let's try a synchronous, non-blocking write. */
Jan Tattermusch4b3ecdf2015-12-04 09:33:05 -0800334 status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
Craig Tillera82950e2015-09-22 12:33:20 -0700335 &bytes_sent, 0, NULL, NULL);
336 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100337
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200338 /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy
339 connection that has its send queue filled up. But if we don't, then we can
340 avoid doing an async write operation at all. */
Craig Tillera82950e2015-09-22 12:33:20 -0700341 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700342 grpc_error* error = status == 0
Craig Tiller3e149f32016-05-17 16:11:04 -0700343 ? GRPC_ERROR_NONE
344 : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800345 GRPC_CLOSURE_SCHED(cb, error);
Nicolas "Pixel" Noblefa242cb2016-09-29 00:54:02 +0200346 if (allocated) gpr_free(allocated);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700347 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700348 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100349
Craig Tillera82950e2015-09-22 12:33:20 -0700350 TCP_REF(tcp, "write");
Craig Tiller9f80fcf2015-08-28 08:22:48 -0700351
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200352 /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same
353 operation, this time asynchronously. */
Craig Tillera82950e2015-09-22 12:33:20 -0700354 memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED));
Jan Tattermusch4b3ecdf2015-12-04 09:33:05 -0800355 status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
Craig Tillera82950e2015-09-22 12:33:20 -0700356 &bytes_sent, 0, &socket->write_info.overlapped, NULL);
357 if (allocated) gpr_free(allocated);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100358
Craig Tillera82950e2015-09-22 12:33:20 -0700359 if (status != 0) {
360 int wsa_error = WSAGetLastError();
361 if (wsa_error != WSA_IO_PENDING) {
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800362 TCP_UNREF(tcp, "write");
363 GRPC_CLOSURE_SCHED(cb, GRPC_WSA_ERROR(wsa_error, "WSASend"));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700364 return;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200365 }
Craig Tillera82950e2015-09-22 12:33:20 -0700366 }
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200367
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200368 /* As all is now setup, we can now ask for the IOCP notification. It may
369 trigger the callback immediately however, but no matter. */
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800370 grpc_socket_notify_on_write(socket, &tcp->on_write);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100371}
372
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800373static void win_add_to_pollset(grpc_endpoint* ep, grpc_pollset* ps) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700374 grpc_tcp* tcp;
Craig Tillera82950e2015-09-22 12:33:20 -0700375 (void)ps;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700376 tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700377 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noblef29e9db2015-07-29 06:00:22 +0200378}
379
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800380static void win_add_to_pollset_set(grpc_endpoint* ep, grpc_pollset_set* pss) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700381 grpc_tcp* tcp;
Craig Tillera82950e2015-09-22 12:33:20 -0700382 (void)pss;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700383 tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700384 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100385}
386
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800387static void win_delete_from_pollset_set(grpc_endpoint* ep,
Craig Tillerbaa14a92017-11-03 09:09:36 -0700388 grpc_pollset_set* pss) {}
Yuchen Zeng01432b72017-10-02 16:14:06 -0700389
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200390/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks
391 for the potential read and write operations. It is up to the caller to
392 guarantee this isn't called in parallel to a read or write request, so
393 we're not going to protect against these. However the IO Completion Port
394 callback will happen from another thread, so we need to protect against
395 concurrent access of the data structure in that regard. */
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800396static void win_shutdown(grpc_endpoint* ep, grpc_error* why) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700397 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700398 gpr_mu_lock(&tcp->mu);
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200399 /* At that point, what may happen is that we're already inside the IOCP
400 callback. See the comments in on_read and on_write. */
Craig Tiller6ba80f92017-01-27 11:58:32 -0800401 if (!tcp->shutting_down) {
Craig Tiller393a4d92017-01-27 12:01:44 -0800402 tcp->shutting_down = 1;
403 tcp->shutdown_error = why;
404 } else {
405 GRPC_ERROR_UNREF(why);
Craig Tiller6ba80f92017-01-27 11:58:32 -0800406 }
Craig Tillera82950e2015-09-22 12:33:20 -0700407 grpc_winsocket_shutdown(tcp->socket);
408 gpr_mu_unlock(&tcp->mu);
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800409 grpc_resource_user_shutdown(tcp->resource_user);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100410}
411
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800412static void win_destroy(grpc_endpoint* ep) {
Makarand Dharmapurikar61494432016-06-22 13:34:59 -0700413 grpc_network_status_unregister_endpoint(ep);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700414 grpc_tcp* tcp = (grpc_tcp*)ep;
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800415 TCP_UNREF(tcp, "destroy");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100416}
417
Craig Tillerbaa14a92017-11-03 09:09:36 -0700418static char* win_get_peer(grpc_endpoint* ep) {
419 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700420 return gpr_strdup(tcp->peer_string);
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700421}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100422
Craig Tillerbaa14a92017-11-03 09:09:36 -0700423static grpc_resource_user* win_get_resource_user(grpc_endpoint* ep) {
424 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillerc037fc12016-11-04 14:04:54 -0700425 return tcp->resource_user;
Craig Tiller290e9a72016-10-28 08:17:00 -0700426}
427
Craig Tillerbaa14a92017-11-03 09:09:36 -0700428static int win_get_fd(grpc_endpoint* ep) { return -1; }
murgatroid992e012342016-11-10 18:24:08 -0800429
Yuchen Zeng01432b72017-10-02 16:14:06 -0700430static grpc_endpoint_vtable vtable = {win_read,
431 win_write,
432 win_add_to_pollset,
433 win_add_to_pollset_set,
434 win_delete_from_pollset_set,
435 win_shutdown,
436 win_destroy,
437 win_get_resource_user,
438 win_get_peer,
439 win_get_fd};
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100440
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800441grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket,
Craig Tillerbaa14a92017-11-03 09:09:36 -0700442 grpc_channel_args* channel_args,
443 const char* peer_string) {
444 grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL);
Craig Tiller4254d3b2017-04-05 14:03:49 -0700445 if (channel_args != NULL) {
446 for (size_t i = 0; i < channel_args->num_args; i++) {
447 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800448 grpc_resource_quota_unref_internal(resource_quota);
Craig Tiller4254d3b2017-04-05 14:03:49 -0700449 resource_quota = grpc_resource_quota_ref_internal(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700450 (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
Craig Tiller4254d3b2017-04-05 14:03:49 -0700451 }
452 }
453 }
Craig Tillerbaa14a92017-11-03 09:09:36 -0700454 grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
Craig Tillera82950e2015-09-22 12:33:20 -0700455 memset(tcp, 0, sizeof(grpc_tcp));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100456 tcp->base.vtable = &vtable;
457 tcp->socket = socket;
Craig Tillera82950e2015-09-22 12:33:20 -0700458 gpr_mu_init(&tcp->mu);
Jan Tattermusch6f8507f2016-11-22 15:13:34 +0100459 gpr_ref_init(&tcp->refcount, 1);
ncteisen969b46e2017-06-08 14:57:11 -0700460 GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx);
461 GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx);
Craig Tillera82950e2015-09-22 12:33:20 -0700462 tcp->peer_string = gpr_strdup(peer_string);
Craig Tillerc037fc12016-11-04 14:04:54 -0700463 tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -0700464 /* Tell network status tracking code about the new endpoint */
465 grpc_network_status_register_endpoint(&tcp->base);
Yash Tibrewal8cf14702017-12-06 09:47:54 -0800466 grpc_resource_quota_unref_internal(resource_quota);
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -0700467
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100468 return &tcp->base;
469}
470
murgatroid99623dd4f2016-08-08 17:31:27 -0700471#endif /* GRPC_WINSOCK_SOCKET */