|
|
@ -108,9 +108,10 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s, |
|
|
|
size_t i; |
|
|
|
size_t i; |
|
|
|
gpr_mu_lock(&s->mu); |
|
|
|
gpr_mu_lock(&s->mu); |
|
|
|
/* First, shutdown all fd's. This will queue abortion calls for all
|
|
|
|
/* First, shutdown all fd's. This will queue abortion calls for all
|
|
|
|
of the pending accepts. */ |
|
|
|
of the pending accepts due to the normal operation mechanism. */ |
|
|
|
for (i = 0; i < s->nports; i++) { |
|
|
|
for (i = 0; i < s->nports; i++) { |
|
|
|
server_port *sp = &s->ports[i]; |
|
|
|
server_port *sp = &s->ports[i]; |
|
|
|
|
|
|
|
sp->shutting_down = 1; |
|
|
|
grpc_winsocket_shutdown(sp->socket); |
|
|
|
grpc_winsocket_shutdown(sp->socket); |
|
|
|
} |
|
|
|
} |
|
|
|
/* This happens asynchronously. Wait while that happens. */ |
|
|
|
/* This happens asynchronously. Wait while that happens. */ |
|
|
@ -243,62 +244,49 @@ static void on_accept(void *arg, int from_iocp) { |
|
|
|
grpc_winsocket_callback_info *info = &sp->socket->read_info; |
|
|
|
grpc_winsocket_callback_info *info = &sp->socket->read_info; |
|
|
|
grpc_endpoint *ep = NULL; |
|
|
|
grpc_endpoint *ep = NULL; |
|
|
|
|
|
|
|
|
|
|
|
/* The shutdown sequence is done in two parts. This is the second
|
|
|
|
/* The general mechanism for shutting down is to queue abortion calls. While
|
|
|
|
part here, acknowledging the IOCP notification, and doing nothing |
|
|
|
this is necessary in the read/write case, it's useless for the accept |
|
|
|
else, especially not queuing a new accept. */ |
|
|
|
case. Let's do nothing. */ |
|
|
|
if (sp->shutting_down) { |
|
|
|
if (!from_iocp) return; |
|
|
|
GPR_ASSERT(from_iocp); |
|
|
|
|
|
|
|
sp->shutting_down = 0; |
|
|
|
/* The IOCP notified us of a completed operation. Let's grab the results,
|
|
|
|
sp->socket->read_info.outstanding = 0; |
|
|
|
and act accordingly. */ |
|
|
|
gpr_mu_lock(&sp->server->mu); |
|
|
|
DWORD transfered_bytes = 0; |
|
|
|
if (0 == --sp->server->active_ports) { |
|
|
|
DWORD flags; |
|
|
|
gpr_cv_broadcast(&sp->server->cv); |
|
|
|
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
|
|
|
} |
|
|
|
&transfered_bytes, FALSE, &flags); |
|
|
|
gpr_mu_unlock(&sp->server->mu); |
|
|
|
if (!wsa_success) { |
|
|
|
return; |
|
|
|
if (sp->shutting_down) { |
|
|
|
} |
|
|
|
/* During the shutdown case, we ARE expecting an error. So that's swell,
|
|
|
|
|
|
|
|
and we can wake up the shutdown thread. */ |
|
|
|
if (from_iocp) { |
|
|
|
sp->shutting_down = 0; |
|
|
|
/* The IOCP notified us of a completed operation. Let's grab the results,
|
|
|
|
sp->socket->read_info.outstanding = 0; |
|
|
|
and act accordingly. */ |
|
|
|
gpr_mu_lock(&sp->server->mu); |
|
|
|
DWORD transfered_bytes = 0; |
|
|
|
if (0 == --sp->server->active_ports) { |
|
|
|
DWORD flags; |
|
|
|
gpr_cv_broadcast(&sp->server->cv); |
|
|
|
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
|
|
|
} |
|
|
|
&transfered_bytes, FALSE, &flags); |
|
|
|
gpr_mu_unlock(&sp->server->mu); |
|
|
|
if (!wsa_success) { |
|
|
|
return; |
|
|
|
|
|
|
|
} else { |
|
|
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
|
|
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
|
|
|
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); |
|
|
|
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); |
|
|
|
gpr_free(utf8_message); |
|
|
|
gpr_free(utf8_message); |
|
|
|
closesocket(sock); |
|
|
|
closesocket(sock); |
|
|
|
} else { |
|
|
|
|
|
|
|
/* TODO(ctiller): add sockaddr address to label */ |
|
|
|
|
|
|
|
ep = grpc_tcp_create(grpc_winsocket_create(sock, "server")); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
/* If we're not notified from the IOCP, it means we are asked to shutdown.
|
|
|
|
if (!sp->shutting_down) { |
|
|
|
This will initiate that shutdown. Calling closesocket will trigger an |
|
|
|
/* TODO(ctiller): add sockaddr address to label */ |
|
|
|
IOCP notification, that will call this function a second time, from |
|
|
|
ep = grpc_tcp_create(grpc_winsocket_create(sock, "server")); |
|
|
|
the IOCP thread. Of course, this only works if the socket was, in fact, |
|
|
|
|
|
|
|
listening. If that's not the case, we'd wait indefinitely. That's a bit |
|
|
|
|
|
|
|
of a degenerate case, but it can happen if you create a server, but |
|
|
|
|
|
|
|
don't start it. So let's support that by recursing once. */ |
|
|
|
|
|
|
|
sp->shutting_down = 1; |
|
|
|
|
|
|
|
sp->new_socket = INVALID_SOCKET; |
|
|
|
|
|
|
|
if (sock != INVALID_SOCKET) { |
|
|
|
|
|
|
|
closesocket(sock); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
on_accept(sp, 1); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* The only time we should call our callback, is where we successfully
|
|
|
|
/* The only time we should call our callback, is where we successfully
|
|
|
|
managed to accept a connection, and created an endpoint. */ |
|
|
|
managed to accept a connection, and created an endpoint. */ |
|
|
|
if (ep) sp->server->cb(sp->server->cb_arg, ep); |
|
|
|
if (ep) sp->server->cb(sp->server->cb_arg, ep); |
|
|
|
/* As we were notified from the IOCP of one and exactly one accept,
|
|
|
|
/* As we were notified from the IOCP of one and exactly one accept,
|
|
|
|
the former socked we created has now either been destroy or assigned |
|
|
|
the former socked we created has now either been destroy or assigned |
|
|
|
to the new connection. We need to create a new one for the next |
|
|
|
to the new connection. We need to create a new one for the next |
|
|
|
connection. */ |
|
|
|
connection. */ |
|
|
|
start_accept(sp); |
|
|
|
start_accept(sp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|