blob: 04922b40375c403eae861f69e486da1310946a7f [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
murgatroid9954070892016-08-08 17:01:18 -070019#include "src/core/lib/iomgr/port.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010020
murgatroid99623dd4f2016-08-08 17:31:27 -070021#ifdef GRPC_WINSOCK_SOCKET
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010022
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020023#include <limits.h>
24
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -070025#include "src/core/lib/iomgr/network_status_tracker.h"
Yuchen Zeng12dfdc32016-04-26 22:05:41 -070026#include "src/core/lib/iomgr/sockaddr_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010027
Craig Tiller28b72422016-10-26 21:15:29 -070028#include <grpc/slice_buffer.h>
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010029#include <grpc/support/alloc.h>
30#include <grpc/support/log.h>
Yuchen Zeng12dfdc32016-04-26 22:05:41 -070031#include <grpc/support/log_windows.h>
Masood Malekghassemi701af602015-06-03 15:01:17 -070032#include <grpc/support/string_util.h>
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010033#include <grpc/support/useful.h>
34
Craig Tiller9533d042016-03-25 17:11:06 -070035#include "src/core/lib/iomgr/iocp_windows.h"
36#include "src/core/lib/iomgr/sockaddr.h"
37#include "src/core/lib/iomgr/sockaddr_utils.h"
38#include "src/core/lib/iomgr/socket_windows.h"
39#include "src/core/lib/iomgr/tcp_client.h"
Yash Tibrewal547653e2017-09-25 21:20:19 -070040#include "src/core/lib/iomgr/tcp_windows.h"
Craig Tiller9533d042016-03-25 17:11:06 -070041#include "src/core/lib/iomgr/timer.h"
Craig Tillerfede4d42016-12-06 20:20:10 -080042#include "src/core/lib/slice/slice_internal.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010043
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020044#if defined(__MSYS__) && defined(GPR_ARCH_64)
45/* Nasty workaround for nasty bug when using the 64 bits msys compiler
46 in conjunction with Microsoft Windows headers. */
47#define GRPC_FIONBIO _IOW('f', 126, uint32_t)
48#else
49#define GRPC_FIONBIO FIONBIO
50#endif
51
ncteisen06bce6e2017-07-10 07:58:49 -070052grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
ncteisen0e3aee32017-06-08 16:32:24 -070053
Craig Tillerbaa14a92017-11-03 09:09:36 -070054static grpc_error* set_non_block(SOCKET sock) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010055 int status;
Nicolas "Pixel" Noblec4b18a52016-04-15 04:53:54 +020056 uint32_t param = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010057 DWORD ret;
Nicolas "Pixel" Noble344f55b2016-04-15 07:52:25 +020058 status = WSAIoctl(sock, GRPC_FIONBIO, &param, sizeof(param), NULL, 0, &ret,
59 NULL, NULL);
Craig Tiller3e149f32016-05-17 16:11:04 -070060 return status == 0
61 ? GRPC_ERROR_NONE
62 : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010063}
64
Craig Tillerbaa14a92017-11-03 09:09:36 -070065static grpc_error* set_dualstack(SOCKET sock) {
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010066 int status;
67 unsigned long param = 0;
Craig Tillerbaa14a92017-11-03 09:09:36 -070068 status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&param,
Craig Tillera82950e2015-09-22 12:33:20 -070069 sizeof(param));
Craig Tiller3e149f32016-05-17 16:11:04 -070070 return status == 0
71 ? GRPC_ERROR_NONE
72 : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010073}
74
Craig Tillerbaa14a92017-11-03 09:09:36 -070075grpc_error* grpc_tcp_prepare_socket(SOCKET sock) {
76 grpc_error* err;
Craig Tillera41ac572016-05-17 16:08:17 -070077 err = set_non_block(sock);
78 if (err != GRPC_ERROR_NONE) return err;
79 err = set_dualstack(sock);
80 if (err != GRPC_ERROR_NONE) return err;
81 return GRPC_ERROR_NONE;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010082}
83
Craig Tillera82950e2015-09-22 12:33:20 -070084typedef struct grpc_tcp {
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020085 /* This is our C++ class derivation emulation. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010086 grpc_endpoint base;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020087 /* The one socket this endpoint is using. */
Craig Tillerbaa14a92017-11-03 09:09:36 -070088 grpc_winsocket* socket;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +020089 /* Refcounting how many operations are in progress. */
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010090 gpr_refcount refcount;
91
Craig Tiller82f9bd82015-09-23 09:31:51 -070092 grpc_closure on_read;
93 grpc_closure on_write;
94
Craig Tillerbaa14a92017-11-03 09:09:36 -070095 grpc_closure* read_cb;
96 grpc_closure* write_cb;
Craig Tillerd41a4a72016-10-26 16:16:06 -070097 grpc_slice read_slice;
Craig Tillerbaa14a92017-11-03 09:09:36 -070098 grpc_slice_buffer* write_slices;
99 grpc_slice_buffer* read_slices;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100100
Craig Tillerbaa14a92017-11-03 09:09:36 -0700101 grpc_resource_user* resource_user;
Craig Tiller290e9a72016-10-28 08:17:00 -0700102
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200103 /* The IO Completion Port runs from another thread. We need some mechanism
104 to protect ourselves when requesting a shutdown. */
105 gpr_mu mu;
106 int shutting_down;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700107 grpc_error* shutdown_error;
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700108
Craig Tillerbaa14a92017-11-03 09:09:36 -0700109 char* peer_string;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100110} grpc_tcp;
111
Craig Tillerbaa14a92017-11-03 09:09:36 -0700112static void tcp_free(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
Craig Tillera82950e2015-09-22 12:33:20 -0700113 grpc_winsocket_destroy(tcp->socket);
114 gpr_mu_destroy(&tcp->mu);
115 gpr_free(tcp->peer_string);
Craig Tillerc037fc12016-11-04 14:04:54 -0700116 grpc_resource_user_unref(exec_ctx, tcp->resource_user);
Craig Tiller6ba80f92017-01-27 11:58:32 -0800117 if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error);
Craig Tillera82950e2015-09-22 12:33:20 -0700118 gpr_free(tcp);
Craig Tillerb0298592015-08-27 07:38:01 -0700119}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100120
ncteisen0e3aee32017-06-08 16:32:24 -0700121#ifndef NDEBUG
Craig Tillerfe6350d2016-11-04 14:05:45 -0700122#define TCP_UNREF(exec_ctx, tcp, reason) \
123 tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__)
Craig Tillerb0298592015-08-27 07:38:01 -0700124#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
Craig Tillerbaa14a92017-11-03 09:09:36 -0700125static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
126 const char* reason, const char* file, int line) {
ncteisen0e3aee32017-06-08 16:32:24 -0700127 if (GRPC_TRACER_ON(grpc_tcp_trace)) {
128 gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
ncteisend39010e2017-06-08 17:08:07 -0700129 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
130 "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
131 val - 1);
ncteisen0e3aee32017-06-08 16:32:24 -0700132 }
Craig Tillera82950e2015-09-22 12:33:20 -0700133 if (gpr_unref(&tcp->refcount)) {
Craig Tillerc037fc12016-11-04 14:04:54 -0700134 tcp_free(exec_ctx, tcp);
Craig Tillera82950e2015-09-22 12:33:20 -0700135 }
Craig Tiller0882a352015-08-26 13:21:14 -0700136}
137
Craig Tillerbaa14a92017-11-03 09:09:36 -0700138static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
Craig Tillera82950e2015-09-22 12:33:20 -0700139 int line) {
ncteisen0e3aee32017-06-08 16:32:24 -0700140 if (GRPC_TRACER_ON(grpc_tcp_trace)) {
141 gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
ncteisend39010e2017-06-08 17:08:07 -0700142 gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
143 "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
144 val + 1);
ncteisen0e3aee32017-06-08 16:32:24 -0700145 }
Craig Tillera82950e2015-09-22 12:33:20 -0700146 gpr_ref(&tcp->refcount);
Craig Tillerb0298592015-08-27 07:38:01 -0700147}
148#else
Craig Tillerc037fc12016-11-04 14:04:54 -0700149#define TCP_UNREF(exec_ctx, tcp, reason) tcp_unref((exec_ctx), (tcp))
Craig Tillerb0298592015-08-27 07:38:01 -0700150#define TCP_REF(tcp, reason) tcp_ref((tcp))
Craig Tillerbaa14a92017-11-03 09:09:36 -0700151static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
Craig Tillera82950e2015-09-22 12:33:20 -0700152 if (gpr_unref(&tcp->refcount)) {
Craig Tillerc037fc12016-11-04 14:04:54 -0700153 tcp_free(exec_ctx, tcp);
Craig Tillera82950e2015-09-22 12:33:20 -0700154 }
Craig Tillerb0298592015-08-27 07:38:01 -0700155}
156
Craig Tillerbaa14a92017-11-03 09:09:36 -0700157static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); }
Craig Tillerb0298592015-08-27 07:38:01 -0700158#endif
159
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200160/* Asynchronous callback from the IOCP, or the background thread. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700161static void on_read(grpc_exec_ctx* exec_ctx, void* tcpp, grpc_error* error) {
162 grpc_tcp* tcp = (grpc_tcp*)tcpp;
163 grpc_closure* cb = tcp->read_cb;
164 grpc_winsocket* socket = tcp->socket;
Craig Tillerd41a4a72016-10-26 16:16:06 -0700165 grpc_slice sub;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700166 grpc_winsocket_callback_info* info = &socket->read_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100167
Craig Tillera41ac572016-05-17 16:08:17 -0700168 GRPC_ERROR_REF(error);
169
170 if (error == GRPC_ERROR_NONE) {
Jan Tattermusch64124792016-03-25 09:16:22 -0700171 if (info->wsa_error != 0 && !tcp->shutting_down) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700172 char* utf8_message = gpr_format_message(info->wsa_error);
ncteisen4b36a3d2017-03-13 19:08:06 -0700173 error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message);
Craig Tillera41ac572016-05-17 16:08:17 -0700174 gpr_free(utf8_message);
Craig Tillera59c16c2016-10-31 07:25:01 -0700175 grpc_slice_unref_internal(exec_ctx, tcp->read_slice);
Craig Tillera82950e2015-09-22 12:33:20 -0700176 } else {
Nicolas "Pixel" Noble3a26c5b2015-09-22 00:07:14 +0200177 if (info->bytes_transfered != 0 && !tcp->shutting_down) {
Craig Tillerd41a4a72016-10-26 16:16:06 -0700178 sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
179 grpc_slice_buffer_add(tcp->read_slices, sub);
Craig Tillera82950e2015-09-22 12:33:20 -0700180 } else {
Craig Tillera59c16c2016-10-31 07:25:01 -0700181 grpc_slice_unref_internal(exec_ctx, tcp->read_slice);
Craig Tiller393a4d92017-01-27 12:01:44 -0800182 error = tcp->shutting_down
ncteisen4b36a3d2017-03-13 19:08:06 -0700183 ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
184 "TCP stream shutting down", &tcp->shutdown_error, 1)
185 : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream");
Craig Tillera82950e2015-09-22 12:33:20 -0700186 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100187 }
Craig Tillera82950e2015-09-22 12:33:20 -0700188 }
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200189
Craig Tillerb0298592015-08-27 07:38:01 -0700190 tcp->read_cb = NULL;
Craig Tillerc037fc12016-11-04 14:04:54 -0700191 TCP_UNREF(exec_ctx, tcp, "read");
ncteisen969b46e2017-06-08 14:57:11 -0700192 GRPC_CLOSURE_SCHED(exec_ctx, cb, error);
Craig Tillerb0298592015-08-27 07:38:01 -0700193}
194
Craig Tillerbaa14a92017-11-03 09:09:36 -0700195static void win_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
196 grpc_slice_buffer* read_slices, grpc_closure* cb) {
197 grpc_tcp* tcp = (grpc_tcp*)ep;
198 grpc_winsocket* handle = tcp->socket;
199 grpc_winsocket_callback_info* info = &handle->read_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100200 int status;
201 DWORD bytes_read = 0;
202 DWORD flags = 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100203 WSABUF buffer;
204
Craig Tillera82950e2015-09-22 12:33:20 -0700205 if (tcp->shutting_down) {
ncteisen969b46e2017-06-08 14:57:11 -0700206 GRPC_CLOSURE_SCHED(
ncteisen4b36a3d2017-03-13 19:08:06 -0700207 exec_ctx, cb,
208 GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
209 "TCP socket is shutting down", &tcp->shutdown_error, 1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700210 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700211 }
Craig Tillerb0298592015-08-27 07:38:01 -0700212
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100213 tcp->read_cb = cb;
Craig Tillerb0298592015-08-27 07:38:01 -0700214 tcp->read_slices = read_slices;
Craig Tillera59c16c2016-10-31 07:25:01 -0700215 grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100216
Craig Tiller423d6fd2017-04-12 13:15:45 -0700217 tcp->read_slice = GRPC_SLICE_MALLOC(8192);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100218
Craig Tiller618e67d2016-10-26 21:08:10 -0700219 buffer.len = (ULONG)GRPC_SLICE_LENGTH(
Craig Tiller620e9652015-12-14 12:02:50 -0800220 tcp->read_slice); // we know slice size fits in 32bit.
Craig Tillerbaa14a92017-11-03 09:09:36 -0700221 buffer.buf = (char*)GRPC_SLICE_START_PTR(tcp->read_slice);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100222
Craig Tiller82f9bd82015-09-23 09:31:51 -0700223 TCP_REF(tcp, "read");
224
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200225 /* First let's try a synchronous, non-blocking read. */
Craig Tillera82950e2015-09-22 12:33:20 -0700226 status =
227 WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL);
228 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100229
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200230 /* Did we get data immediately ? Yay. */
Craig Tillera82950e2015-09-22 12:33:20 -0700231 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tillera82950e2015-09-22 12:33:20 -0700232 info->bytes_transfered = bytes_read;
ncteisen969b46e2017-06-08 14:57:11 -0700233 GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700234 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700235 }
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200236
Craig Tiller45724b32015-09-22 10:42:19 -0700237 /* Otherwise, let's retry, by queuing a read. */
Craig Tillera82950e2015-09-22 12:33:20 -0700238 memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
239 status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
240 &info->overlapped, NULL);
Craig Tiller45724b32015-09-22 10:42:19 -0700241
Craig Tillera82950e2015-09-22 12:33:20 -0700242 if (status != 0) {
243 int wsa_error = WSAGetLastError();
244 if (wsa_error != WSA_IO_PENDING) {
Craig Tillera82950e2015-09-22 12:33:20 -0700245 info->wsa_error = wsa_error;
ncteisen969b46e2017-06-08 14:57:11 -0700246 GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read,
Craig Tillerd4654562017-01-03 08:45:56 -0800247 GRPC_WSA_ERROR(info->wsa_error, "WSARecv"));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700248 return;
Craig Tiller45724b32015-09-22 10:42:19 -0700249 }
Craig Tillera82950e2015-09-22 12:33:20 -0700250 }
Craig Tiller45724b32015-09-22 10:42:19 -0700251
Craig Tiller82f9bd82015-09-23 09:31:51 -0700252 grpc_socket_notify_on_read(exec_ctx, tcp->socket, &tcp->on_read);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100253}
254
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200255/* Asynchronous callback from the IOCP, or the background thread. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700256static void on_write(grpc_exec_ctx* exec_ctx, void* tcpp, grpc_error* error) {
257 grpc_tcp* tcp = (grpc_tcp*)tcpp;
258 grpc_winsocket* handle = tcp->socket;
259 grpc_winsocket_callback_info* info = &handle->write_info;
260 grpc_closure* cb;
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200261
Craig Tillera41ac572016-05-17 16:08:17 -0700262 GRPC_ERROR_REF(error);
263
Craig Tillera82950e2015-09-22 12:33:20 -0700264 gpr_mu_lock(&tcp->mu);
Nicolas Noblee1445362015-05-11 17:40:26 -0700265 cb = tcp->write_cb;
266 tcp->write_cb = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700267 gpr_mu_unlock(&tcp->mu);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100268
Craig Tillera41ac572016-05-17 16:08:17 -0700269 if (error == GRPC_ERROR_NONE) {
Craig Tillera82950e2015-09-22 12:33:20 -0700270 if (info->wsa_error != 0) {
Craig Tillera41ac572016-05-17 16:08:17 -0700271 error = GRPC_WSA_ERROR(info->wsa_error, "WSASend");
Craig Tillera82950e2015-09-22 12:33:20 -0700272 } else {
273 GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
Nicolas Noblee1445362015-05-11 17:40:26 -0700274 }
Craig Tillera82950e2015-09-22 12:33:20 -0700275 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100276
Craig Tillerc037fc12016-11-04 14:04:54 -0700277 TCP_UNREF(exec_ctx, tcp, "write");
ncteisen969b46e2017-06-08 14:57:11 -0700278 GRPC_CLOSURE_SCHED(exec_ctx, cb, error);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100279}
280
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200281/* Initiates a write. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700282static void win_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
283 grpc_slice_buffer* slices, grpc_closure* cb) {
284 grpc_tcp* tcp = (grpc_tcp*)ep;
285 grpc_winsocket* socket = tcp->socket;
286 grpc_winsocket_callback_info* info = &socket->write_info;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100287 unsigned i;
288 DWORD bytes_sent;
289 int status;
290 WSABUF local_buffers[16];
Craig Tillerbaa14a92017-11-03 09:09:36 -0700291 WSABUF* allocated = NULL;
292 WSABUF* buffers = local_buffers;
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800293 size_t len;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100294
Craig Tillera82950e2015-09-22 12:33:20 -0700295 if (tcp->shutting_down) {
ncteisen969b46e2017-06-08 14:57:11 -0700296 GRPC_CLOSURE_SCHED(
ncteisen4b36a3d2017-03-13 19:08:06 -0700297 exec_ctx, cb,
298 GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
299 "TCP socket is shutting down", &tcp->shutdown_error, 1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700300 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700301 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100302
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100303 tcp->write_cb = cb;
Craig Tillerb0298592015-08-27 07:38:01 -0700304 tcp->write_slices = slices;
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800305 GPR_ASSERT(tcp->write_slices->count <= UINT_MAX);
Craig Tillera82950e2015-09-22 12:33:20 -0700306 if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700307 buffers = (WSABUF*)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count);
Craig Tillera82950e2015-09-22 12:33:20 -0700308 allocated = buffers;
309 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100310
Craig Tillera82950e2015-09-22 12:33:20 -0700311 for (i = 0; i < tcp->write_slices->count; i++) {
Craig Tiller618e67d2016-10-26 21:08:10 -0700312 len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]);
Jan Tattermuschf67f0712015-12-07 18:09:49 -0800313 GPR_ASSERT(len <= ULONG_MAX);
Craig Tiller620e9652015-12-14 12:02:50 -0800314 buffers[i].len = (ULONG)len;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700315 buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]);
Craig Tillera82950e2015-09-22 12:33:20 -0700316 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100317
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200318 /* First, let's try a synchronous, non-blocking write. */
Jan Tattermusch4b3ecdf2015-12-04 09:33:05 -0800319 status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
Craig Tillera82950e2015-09-22 12:33:20 -0700320 &bytes_sent, 0, NULL, NULL);
321 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100322
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200323 /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy
324 connection that has its send queue filled up. But if we don't, then we can
325 avoid doing an async write operation at all. */
Craig Tillera82950e2015-09-22 12:33:20 -0700326 if (info->wsa_error != WSAEWOULDBLOCK) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700327 grpc_error* error = status == 0
Craig Tiller3e149f32016-05-17 16:11:04 -0700328 ? GRPC_ERROR_NONE
329 : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
ncteisen969b46e2017-06-08 14:57:11 -0700330 GRPC_CLOSURE_SCHED(exec_ctx, cb, error);
Nicolas "Pixel" Noblefa242cb2016-09-29 00:54:02 +0200331 if (allocated) gpr_free(allocated);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700332 return;
Craig Tillera82950e2015-09-22 12:33:20 -0700333 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100334
Craig Tillera82950e2015-09-22 12:33:20 -0700335 TCP_REF(tcp, "write");
Craig Tiller9f80fcf2015-08-28 08:22:48 -0700336
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200337 /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same
338 operation, this time asynchronously. */
Craig Tillera82950e2015-09-22 12:33:20 -0700339 memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED));
Jan Tattermusch4b3ecdf2015-12-04 09:33:05 -0800340 status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count,
Craig Tillera82950e2015-09-22 12:33:20 -0700341 &bytes_sent, 0, &socket->write_info.overlapped, NULL);
342 if (allocated) gpr_free(allocated);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100343
Craig Tillera82950e2015-09-22 12:33:20 -0700344 if (status != 0) {
345 int wsa_error = WSAGetLastError();
346 if (wsa_error != WSA_IO_PENDING) {
Craig Tillerc037fc12016-11-04 14:04:54 -0700347 TCP_UNREF(exec_ctx, tcp, "write");
ncteisen969b46e2017-06-08 14:57:11 -0700348 GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend"));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700349 return;
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200350 }
Craig Tillera82950e2015-09-22 12:33:20 -0700351 }
Nicolas "Pixel" Noble7f2e98c2015-05-08 01:41:21 +0200352
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200353 /* As all is now setup, we can now ask for the IOCP notification. It may
354 trigger the callback immediately however, but no matter. */
Craig Tiller82f9bd82015-09-23 09:31:51 -0700355 grpc_socket_notify_on_write(exec_ctx, socket, &tcp->on_write);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100356}
357
Craig Tillerbaa14a92017-11-03 09:09:36 -0700358static void win_add_to_pollset(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
359 grpc_pollset* ps) {
360 grpc_tcp* tcp;
Craig Tillera82950e2015-09-22 12:33:20 -0700361 (void)ps;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700362 tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700363 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noblef29e9db2015-07-29 06:00:22 +0200364}
365
Craig Tillerbaa14a92017-11-03 09:09:36 -0700366static void win_add_to_pollset_set(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
367 grpc_pollset_set* pss) {
368 grpc_tcp* tcp;
Craig Tillera82950e2015-09-22 12:33:20 -0700369 (void)pss;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700370 tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700371 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100372}
373
Craig Tillerbaa14a92017-11-03 09:09:36 -0700374static void win_delete_from_pollset_set(grpc_exec_ctx* exec_ctx,
375 grpc_endpoint* ep,
376 grpc_pollset_set* pss) {}
Yuchen Zeng01432b72017-10-02 16:14:06 -0700377
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200378/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks
379 for the potential read and write operations. It is up to the caller to
380 guarantee this isn't called in parallel to a read or write request, so
381 we're not going to protect against these. However the IO Completion Port
382 callback will happen from another thread, so we need to protect against
383 concurrent access of the data structure in that regard. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700384static void win_shutdown(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep,
385 grpc_error* why) {
386 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700387 gpr_mu_lock(&tcp->mu);
Nicolas "Pixel" Noble0f981e92015-05-03 10:40:56 +0200388 /* At that point, what may happen is that we're already inside the IOCP
389 callback. See the comments in on_read and on_write. */
Craig Tiller6ba80f92017-01-27 11:58:32 -0800390 if (!tcp->shutting_down) {
Craig Tiller393a4d92017-01-27 12:01:44 -0800391 tcp->shutting_down = 1;
392 tcp->shutdown_error = why;
393 } else {
394 GRPC_ERROR_UNREF(why);
Craig Tiller6ba80f92017-01-27 11:58:32 -0800395 }
Craig Tillera82950e2015-09-22 12:33:20 -0700396 grpc_winsocket_shutdown(tcp->socket);
397 gpr_mu_unlock(&tcp->mu);
Craig Tillerc037fc12016-11-04 14:04:54 -0700398 grpc_resource_user_shutdown(exec_ctx, tcp->resource_user);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100399}
400
Craig Tillerbaa14a92017-11-03 09:09:36 -0700401static void win_destroy(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) {
Makarand Dharmapurikar61494432016-06-22 13:34:59 -0700402 grpc_network_status_unregister_endpoint(ep);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700403 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillerc037fc12016-11-04 14:04:54 -0700404 TCP_UNREF(exec_ctx, tcp, "destroy");
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100405}
406
Craig Tillerbaa14a92017-11-03 09:09:36 -0700407static char* win_get_peer(grpc_endpoint* ep) {
408 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillera82950e2015-09-22 12:33:20 -0700409 return gpr_strdup(tcp->peer_string);
Craig Tiller81bcc4c2015-07-20 14:04:18 -0700410}
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100411
Craig Tillerbaa14a92017-11-03 09:09:36 -0700412static grpc_resource_user* win_get_resource_user(grpc_endpoint* ep) {
413 grpc_tcp* tcp = (grpc_tcp*)ep;
Craig Tillerc037fc12016-11-04 14:04:54 -0700414 return tcp->resource_user;
Craig Tiller290e9a72016-10-28 08:17:00 -0700415}
416
Craig Tillerbaa14a92017-11-03 09:09:36 -0700417static int win_get_fd(grpc_endpoint* ep) { return -1; }
murgatroid992e012342016-11-10 18:24:08 -0800418
Yuchen Zeng01432b72017-10-02 16:14:06 -0700419static grpc_endpoint_vtable vtable = {win_read,
420 win_write,
421 win_add_to_pollset,
422 win_add_to_pollset_set,
423 win_delete_from_pollset_set,
424 win_shutdown,
425 win_destroy,
426 win_get_resource_user,
427 win_get_peer,
428 win_get_fd};
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100429
Craig Tillerbaa14a92017-11-03 09:09:36 -0700430grpc_endpoint* grpc_tcp_create(grpc_exec_ctx* exec_ctx, grpc_winsocket* socket,
431 grpc_channel_args* channel_args,
432 const char* peer_string) {
433 grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL);
Craig Tiller4254d3b2017-04-05 14:03:49 -0700434 if (channel_args != NULL) {
435 for (size_t i = 0; i < channel_args->num_args; i++) {
436 if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) {
437 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
438 resource_quota = grpc_resource_quota_ref_internal(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700439 (grpc_resource_quota*)channel_args->args[i].value.pointer.p);
Craig Tiller4254d3b2017-04-05 14:03:49 -0700440 }
441 }
442 }
Craig Tillerbaa14a92017-11-03 09:09:36 -0700443 grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
Craig Tillera82950e2015-09-22 12:33:20 -0700444 memset(tcp, 0, sizeof(grpc_tcp));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100445 tcp->base.vtable = &vtable;
446 tcp->socket = socket;
Craig Tillera82950e2015-09-22 12:33:20 -0700447 gpr_mu_init(&tcp->mu);
Jan Tattermusch6f8507f2016-11-22 15:13:34 +0100448 gpr_ref_init(&tcp->refcount, 1);
ncteisen969b46e2017-06-08 14:57:11 -0700449 GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx);
450 GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx);
Craig Tillera82950e2015-09-22 12:33:20 -0700451 tcp->peer_string = gpr_strdup(peer_string);
Craig Tillerc037fc12016-11-04 14:04:54 -0700452 tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string);
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -0700453 /* Tell network status tracking code about the new endpoint */
454 grpc_network_status_register_endpoint(&tcp->base);
Jan Tattermusch1b1e8d62017-10-24 11:15:17 +0200455 grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
Makarand Dharmapurikar0579cfc2016-06-20 15:45:24 -0700456
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100457 return &tcp->base;
458}
459
murgatroid99623dd4f2016-08-08 17:31:27 -0700460#endif /* GRPC_WINSOCK_SOCKET */