mirror of https://github.com/grpc/grpc.git
[EventEngine] WindowsEventEngine Listener v2 (#32488)
Continued from https://github.com/grpc/grpc/pull/32295 after landing the cleanup and logging pieces separately.pull/32493/head
parent
2ce147a131
commit
8f7cd1eeed
19 changed files with 590 additions and 11 deletions
@ -0,0 +1,365 @@ |
|||||||
|
// Copyright 2023 The gRPC Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WINDOWS |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
|
||||||
|
#include "src/core/lib/event_engine/tcp_socket_utils.h" |
||||||
|
#include "src/core/lib/event_engine/trace.h" |
||||||
|
#include "src/core/lib/event_engine/windows/iocp.h" |
||||||
|
#include "src/core/lib/event_engine/windows/win_socket.h" |
||||||
|
#include "src/core/lib/event_engine/windows/windows_endpoint.h" |
||||||
|
#include "src/core/lib/event_engine/windows/windows_listener.h" |
||||||
|
#include "src/core/lib/gprpp/crash.h" |
||||||
|
#include "src/core/lib/gprpp/sync.h" |
||||||
|
#include "src/core/lib/iomgr/error.h" |
||||||
|
|
||||||
|
namespace grpc_event_engine { |
||||||
|
namespace experimental { |
||||||
|
|
||||||
|
// ---- SinglePortSocketListener::AsyncIOState ----
|
||||||
|
|
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::AsyncIOState:: |
||||||
|
AsyncIOState(SinglePortSocketListener* port_listener, |
||||||
|
std::unique_ptr<WinSocket> listener_socket) |
||||||
|
: port_listener(port_listener), |
||||||
|
listener_socket(std::move(listener_socket)) {} |
||||||
|
|
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::AsyncIOState:: |
||||||
|
~AsyncIOState() { |
||||||
|
closesocket(accept_socket); |
||||||
|
} |
||||||
|
|
||||||
|
void WindowsEventEngineListener::SinglePortSocketListener:: |
||||||
|
OnAcceptCallbackWrapper::Run() { |
||||||
|
GPR_ASSERT(io_state_ != nullptr); |
||||||
|
grpc_core::ReleasableMutexLock lock(&io_state_->mu); |
||||||
|
if (io_state_->listener_socket->IsShutdown()) { |
||||||
|
GRPC_EVENT_ENGINE_TRACE( |
||||||
|
"SinglePortSocketListener::%p listener socket is shut down. Shutting " |
||||||
|
"down listener.", |
||||||
|
io_state_->port_listener); |
||||||
|
lock.Release(); |
||||||
|
io_state_.reset(); |
||||||
|
return; |
||||||
|
} |
||||||
|
io_state_->port_listener->OnAcceptCallbackLocked(); |
||||||
|
} |
||||||
|
|
||||||
|
void WindowsEventEngineListener::SinglePortSocketListener:: |
||||||
|
OnAcceptCallbackWrapper::Prime(std::shared_ptr<AsyncIOState> io_state) { |
||||||
|
io_state_ = std::move(io_state); |
||||||
|
} |
||||||
|
|
||||||
|
// ---- SinglePortSocketListener ----
|
||||||
|
|
||||||
|
WindowsEventEngineListener::SinglePortSocketListener:: |
||||||
|
~SinglePortSocketListener() { |
||||||
|
io_state_->listener_socket->Shutdown(DEBUG_LOCATION, |
||||||
|
"~SinglePortSocketListener"); |
||||||
|
GRPC_EVENT_ENGINE_TRACE("~SinglePortSocketListener::%p", this); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr< |
||||||
|
std::unique_ptr<WindowsEventEngineListener::SinglePortSocketListener>> |
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::Create( |
||||||
|
WindowsEventEngineListener* listener, SOCKET sock, |
||||||
|
EventEngine::ResolvedAddress addr) { |
||||||
|
// We need to grab the AcceptEx pointer for that port, as it may be
|
||||||
|
// interface-dependent. We'll cache it to avoid doing that again.
|
||||||
|
GUID guid = WSAID_ACCEPTEX; |
||||||
|
DWORD ioctl_num_bytes; |
||||||
|
LPFN_ACCEPTEX AcceptEx; |
||||||
|
int status = |
||||||
|
WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), |
||||||
|
&AcceptEx, sizeof(AcceptEx), &ioctl_num_bytes, NULL, NULL); |
||||||
|
if (status != 0) { |
||||||
|
auto error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); |
||||||
|
closesocket(sock); |
||||||
|
return error; |
||||||
|
} |
||||||
|
auto result = SinglePortSocketListener::PrepareListenerSocket(sock, addr); |
||||||
|
GRPC_RETURN_IF_ERROR(result.status()); |
||||||
|
GPR_ASSERT(result->port >= 0); |
||||||
|
// Using `new` to access non-public constructor
|
||||||
|
return absl::WrapUnique(new SinglePortSocketListener( |
||||||
|
listener, AcceptEx, /*win_socket=*/listener->iocp_->Watch(sock), |
||||||
|
result->port, result->hostbyname)); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status WindowsEventEngineListener::SinglePortSocketListener::Start() { |
||||||
|
grpc_core::MutexLock lock(&io_state_->mu); |
||||||
|
return StartLocked(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status |
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::StartLocked() { |
||||||
|
SOCKET accept_socket = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||||
|
IOCP::GetDefaultSocketFlags()); |
||||||
|
if (accept_socket == INVALID_SOCKET) { |
||||||
|
return GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); |
||||||
|
} |
||||||
|
auto fail = [&](absl::Status error) -> absl::Status { |
||||||
|
if (accept_socket != INVALID_SOCKET) closesocket(accept_socket); |
||||||
|
return error; |
||||||
|
}; |
||||||
|
auto error = PrepareSocket(accept_socket); |
||||||
|
if (!error.ok()) return fail(error); |
||||||
|
// Start the "accept" asynchronously.
|
||||||
|
DWORD addrlen = sizeof(sockaddr_in6) + 16; |
||||||
|
DWORD bytes_received = 0; |
||||||
|
int success = |
||||||
|
AcceptEx(io_state_->listener_socket->raw_socket(), accept_socket, |
||||||
|
addresses_, 0, addrlen, addrlen, &bytes_received, |
||||||
|
io_state_->listener_socket->read_info()->overlapped()); |
||||||
|
// It is possible to get an accept immediately without delay. However, we
|
||||||
|
// will still get an IOCP notification for it. So let's just ignore it.
|
||||||
|
if (success != 0) { |
||||||
|
int last_error = WSAGetLastError(); |
||||||
|
if (last_error != ERROR_IO_PENDING) { |
||||||
|
return fail(GRPC_WSA_ERROR(last_error, "AcceptEx")); |
||||||
|
} |
||||||
|
} |
||||||
|
// We're ready to do the accept. Calling NotifyOnRead may immediately process
|
||||||
|
// an accept that happened in the meantime.
|
||||||
|
io_state_->accept_socket = accept_socket; |
||||||
|
io_state_->listener_socket->NotifyOnRead(&io_state_->on_accept_cb); |
||||||
|
GRPC_EVENT_ENGINE_TRACE( |
||||||
|
"SinglePortSocketListener::%p listening. listener_socket::%p", this, |
||||||
|
io_state_->listener_socket.get()); |
||||||
|
return absl::OkStatus(); |
||||||
|
} |
||||||
|
|
||||||
|
void WindowsEventEngineListener::SinglePortSocketListener:: |
||||||
|
OnAcceptCallbackLocked() { |
||||||
|
auto close_socket_and_restart = |
||||||
|
[&](bool do_close_socket = true) |
||||||
|
ABSL_EXCLUSIVE_LOCKS_REQUIRED(io_state_->mu) { |
||||||
|
if (do_close_socket) closesocket(io_state_->accept_socket); |
||||||
|
io_state_->accept_socket = INVALID_SOCKET; |
||||||
|
GPR_ASSERT(GRPC_LOG_IF_ERROR("SinglePortSocketListener::Start", |
||||||
|
StartLocked())); |
||||||
|
}; |
||||||
|
const auto& overlapped_result = |
||||||
|
io_state_->listener_socket->read_info()->result(); |
||||||
|
if (overlapped_result.wsa_error != 0) { |
||||||
|
gpr_log(GPR_ERROR, "%s", |
||||||
|
GRPC_WSA_ERROR(overlapped_result.wsa_error, |
||||||
|
"Skipping on_accept due to error") |
||||||
|
.ToString() |
||||||
|
.c_str()); |
||||||
|
return close_socket_and_restart(); |
||||||
|
} |
||||||
|
SOCKET tmp_listener_socket = io_state_->listener_socket->raw_socket(); |
||||||
|
int err = |
||||||
|
setsockopt(io_state_->accept_socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, |
||||||
|
reinterpret_cast<char*>(&tmp_listener_socket), |
||||||
|
sizeof(tmp_listener_socket)); |
||||||
|
if (err != 0) { |
||||||
|
gpr_log(GPR_ERROR, "%s", |
||||||
|
GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt").ToString().c_str()); |
||||||
|
return close_socket_and_restart(); |
||||||
|
} |
||||||
|
EventEngine::ResolvedAddress peer_address; |
||||||
|
int peer_name_len = EventEngine::ResolvedAddress::MAX_SIZE_BYTES; |
||||||
|
err = getpeername(io_state_->accept_socket, |
||||||
|
const_cast<sockaddr*>(peer_address.address()), |
||||||
|
&peer_name_len); |
||||||
|
if (err != 0) { |
||||||
|
gpr_log( |
||||||
|
GPR_ERROR, "%s", |
||||||
|
GRPC_WSA_ERROR(WSAGetLastError(), "getpeername").ToString().c_str()); |
||||||
|
return close_socket_and_restart(); |
||||||
|
} |
||||||
|
peer_address = |
||||||
|
EventEngine::ResolvedAddress(peer_address.address(), peer_name_len); |
||||||
|
auto addr_uri = ResolvedAddressToURI(peer_address); |
||||||
|
std::string peer_name = "unknown"; |
||||||
|
if (!addr_uri.ok()) { |
||||||
|
// TODO(hork): test an early exit/restart here with end2end tests
|
||||||
|
gpr_log(GPR_ERROR, "invalid peer name: %s", |
||||||
|
addr_uri.status().ToString().c_str()); |
||||||
|
} else { |
||||||
|
peer_name = *addr_uri; |
||||||
|
} |
||||||
|
auto endpoint = std::make_unique<WindowsEndpoint>( |
||||||
|
peer_address, listener_->iocp_->Watch(io_state_->accept_socket), |
||||||
|
listener_->memory_allocator_factory_->CreateMemoryAllocator( |
||||||
|
absl::StrFormat("listener endpoint %s", peer_name)), |
||||||
|
listener_->config_, listener_->executor_); |
||||||
|
listener_->accept_cb_( |
||||||
|
std::move(endpoint), |
||||||
|
listener_->memory_allocator_factory_->CreateMemoryAllocator( |
||||||
|
absl::StrFormat("listener accept cb for %s", peer_name))); |
||||||
|
close_socket_and_restart(/*do_close_socket=*/false); |
||||||
|
} |
||||||
|
|
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::SinglePortSocketListener( |
||||||
|
WindowsEventEngineListener* listener, LPFN_ACCEPTEX AcceptEx, |
||||||
|
std::unique_ptr<WinSocket> listener_socket, int port, |
||||||
|
EventEngine::ResolvedAddress hostbyname) |
||||||
|
: AcceptEx(AcceptEx), |
||||||
|
listener_(listener), |
||||||
|
io_state_( |
||||||
|
std::make_shared<AsyncIOState>(this, std::move(listener_socket))), |
||||||
|
port_(port), |
||||||
|
listener_sockname_(hostbyname) { |
||||||
|
io_state_->on_accept_cb.Prime(io_state_); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr<WindowsEventEngineListener::SinglePortSocketListener:: |
||||||
|
PrepareListenerSocketResult> |
||||||
|
WindowsEventEngineListener::SinglePortSocketListener::PrepareListenerSocket( |
||||||
|
SOCKET sock, const EventEngine::ResolvedAddress& addr) { |
||||||
|
auto fail = [&](absl::Status error) -> absl::Status { |
||||||
|
GPR_ASSERT(!error.ok()); |
||||||
|
auto addr_uri = ResolvedAddressToURI(addr); |
||||||
|
error = grpc_error_set_int( |
||||||
|
grpc_error_set_str( |
||||||
|
GRPC_ERROR_CREATE_REFERENCING("Failed to prepare server socket", |
||||||
|
&error, 1), |
||||||
|
grpc_core::StatusStrProperty::kTargetAddress, |
||||||
|
addr_uri.ok() ? *addr_uri : addr_uri.status().ToString()), |
||||||
|
grpc_core::StatusIntProperty::kFd, static_cast<intptr_t>(sock)); |
||||||
|
if (sock != INVALID_SOCKET) closesocket(sock); |
||||||
|
return error; |
||||||
|
}; |
||||||
|
auto error = PrepareSocket(sock); |
||||||
|
if (!error.ok()) return fail(error); |
||||||
|
if (bind(sock, addr.address(), addr.size()) == SOCKET_ERROR) { |
||||||
|
return fail(GRPC_WSA_ERROR(WSAGetLastError(), "bind")); |
||||||
|
} |
||||||
|
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { |
||||||
|
return fail(GRPC_WSA_ERROR(WSAGetLastError(), "listen")); |
||||||
|
} |
||||||
|
int sockname_temp_len = sizeof(struct sockaddr_storage); |
||||||
|
EventEngine::ResolvedAddress sockname_temp; |
||||||
|
if (getsockname(sock, const_cast<sockaddr*>(sockname_temp.address()), |
||||||
|
&sockname_temp_len) == SOCKET_ERROR) { |
||||||
|
return fail(GRPC_WSA_ERROR(WSAGetLastError(), "getsockname")); |
||||||
|
} |
||||||
|
sockname_temp = |
||||||
|
EventEngine::ResolvedAddress(sockname_temp.address(), sockname_temp_len); |
||||||
|
return PrepareListenerSocketResult{ResolvedAddressGetPort(sockname_temp), |
||||||
|
sockname_temp}; |
||||||
|
} |
||||||
|
|
||||||
|
// ---- WindowsEventEngineListener ----
|
||||||
|
|
||||||
|
WindowsEventEngineListener::WindowsEventEngineListener( |
||||||
|
IOCP* iocp, AcceptCallback accept_cb, |
||||||
|
absl::AnyInvocable<void(absl::Status)> on_shutdown, |
||||||
|
std::unique_ptr<MemoryAllocatorFactory> memory_allocator_factory, |
||||||
|
std::shared_ptr<EventEngine> engine, Executor* executor, |
||||||
|
const EndpointConfig& config) |
||||||
|
: iocp_(iocp), |
||||||
|
config_(config), |
||||||
|
engine_(std::move(engine)), |
||||||
|
executor_(executor), |
||||||
|
memory_allocator_factory_(std::move(memory_allocator_factory)), |
||||||
|
accept_cb_(std::move(accept_cb)), |
||||||
|
on_shutdown_(std::move(on_shutdown)) {} |
||||||
|
|
||||||
|
WindowsEventEngineListener::~WindowsEventEngineListener() { |
||||||
|
GRPC_EVENT_ENGINE_TRACE( |
||||||
|
"%s", |
||||||
|
absl::StrFormat("WindowsEventEngineListener::%p shutting down", this) |
||||||
|
.c_str()); |
||||||
|
// Shut down each port listener before destroying this EventEngine::Listener
|
||||||
|
for (auto& port_listener : port_listeners_) { |
||||||
|
port_listener.reset(); |
||||||
|
} |
||||||
|
on_shutdown_(absl::OkStatus()); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr<int> WindowsEventEngineListener::Bind( |
||||||
|
const EventEngine::ResolvedAddress& addr) { |
||||||
|
if (started_.load()) { |
||||||
|
return absl::FailedPreconditionError( |
||||||
|
absl::StrFormat("WindowsEventEngineListener::%p is already started, " |
||||||
|
"ports can no longer be bound", |
||||||
|
this)); |
||||||
|
} |
||||||
|
int out_port = ResolvedAddressGetPort(addr); |
||||||
|
EventEngine::ResolvedAddress out_addr(addr); |
||||||
|
EventEngine::ResolvedAddress tmp_addr; |
||||||
|
// Check if this is a wildcard port, and if so, try to keep the port the same
|
||||||
|
// as some previously created listener.
|
||||||
|
if (out_port == 0) { |
||||||
|
grpc_core::MutexLock lock(&socket_listeners_mu_); |
||||||
|
for (const auto& port_listener : port_listeners_) { |
||||||
|
tmp_addr = port_listener->listener_sockname(); |
||||||
|
out_port = ResolvedAddressGetPort(tmp_addr); |
||||||
|
if (out_port > 0) { |
||||||
|
ResolvedAddressSetPort(out_addr, out_port); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (ResolvedAddressToV4Mapped(out_addr, &tmp_addr)) { |
||||||
|
out_addr = tmp_addr; |
||||||
|
} |
||||||
|
// Treat :: or 0.0.0.0 as a family-agnostic wildcard.
|
||||||
|
if (ResolvedAddressIsWildcard(out_addr)) { |
||||||
|
out_addr = ResolvedAddressMakeWild6(out_port); |
||||||
|
} |
||||||
|
// open the socket
|
||||||
|
SOCKET sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, |
||||||
|
IOCP::GetDefaultSocketFlags()); |
||||||
|
if (sock == INVALID_SOCKET) { |
||||||
|
auto error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); |
||||||
|
return GRPC_ERROR_CREATE_REFERENCING("Failed to add port to server", &error, |
||||||
|
1); |
||||||
|
} |
||||||
|
auto port_listener = AddSinglePortSocketListener(sock, out_addr); |
||||||
|
GRPC_RETURN_IF_ERROR(port_listener.status()); |
||||||
|
return (*port_listener)->port(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status WindowsEventEngineListener::Start() { |
||||||
|
GPR_ASSERT(!started_.exchange(true)); |
||||||
|
grpc_core::MutexLock lock(&socket_listeners_mu_); |
||||||
|
for (auto& port_listener : port_listeners_) { |
||||||
|
GRPC_RETURN_IF_ERROR(port_listener->Start()); |
||||||
|
} |
||||||
|
return absl::OkStatus(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr<WindowsEventEngineListener::SinglePortSocketListener*> |
||||||
|
WindowsEventEngineListener::AddSinglePortSocketListener( |
||||||
|
SOCKET sock, EventEngine::ResolvedAddress addr) { |
||||||
|
auto single_port_listener = |
||||||
|
SinglePortSocketListener::Create(this, sock, addr); |
||||||
|
GRPC_RETURN_IF_ERROR(single_port_listener.status()); |
||||||
|
auto* single_port_listener_ptr = single_port_listener->get(); |
||||||
|
grpc_core::MutexLock lock(&socket_listeners_mu_); |
||||||
|
port_listeners_.emplace_back(std::move(*single_port_listener)); |
||||||
|
if (started_.load()) { |
||||||
|
gpr_log(GPR_ERROR, |
||||||
|
"WindowsEventEngineListener::%p Bind was called concurrently while " |
||||||
|
"the Listener was starting. This is invalid usage, all ports must " |
||||||
|
"be bound before the Listener is started.", |
||||||
|
this); |
||||||
|
single_port_listener_ptr->Start(); |
||||||
|
} |
||||||
|
return single_port_listener_ptr; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc_event_engine
|
||||||
|
|
||||||
|
#endif // GPR_WINDOWS
|
@ -0,0 +1,151 @@ |
|||||||
|
// Copyright 2023 The gRPC Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WINDOWS_LISTENER_H |
||||||
|
#define GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WINDOWS_LISTENER_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WINDOWS |
||||||
|
|
||||||
|
#include <list> |
||||||
|
|
||||||
|
#include "absl/base/thread_annotations.h" |
||||||
|
#include "absl/status/statusor.h" |
||||||
|
|
||||||
|
#include <grpc/event_engine/event_engine.h> |
||||||
|
#include <grpc/event_engine/memory_allocator.h> |
||||||
|
|
||||||
|
#include "src/core/lib/event_engine/common_closures.h" |
||||||
|
#include "src/core/lib/event_engine/windows/iocp.h" |
||||||
|
#include "src/core/lib/gprpp/sync.h" |
||||||
|
|
||||||
|
namespace grpc_event_engine { |
||||||
|
namespace experimental { |
||||||
|
|
||||||
|
class WindowsEventEngineListener : public EventEngine::Listener { |
||||||
|
public: |
||||||
|
WindowsEventEngineListener( |
||||||
|
IOCP* iocp, AcceptCallback accept_cb, |
||||||
|
absl::AnyInvocable<void(absl::Status)> on_shutdown, |
||||||
|
std::unique_ptr<MemoryAllocatorFactory> memory_allocator_factory, |
||||||
|
std::shared_ptr<EventEngine> engine, Executor* executor_, |
||||||
|
const EndpointConfig& config); |
||||||
|
~WindowsEventEngineListener() override; |
||||||
|
absl::StatusOr<int> Bind(const EventEngine::ResolvedAddress& addr) override; |
||||||
|
absl::Status Start() override; |
||||||
|
|
||||||
|
private: |
||||||
|
/// Responsible for listening on a single port.
|
||||||
|
class SinglePortSocketListener { |
||||||
|
public: |
||||||
|
~SinglePortSocketListener(); |
||||||
|
// This factory will create a bound, listening WinSocket, registered with
|
||||||
|
// the listener's IOCP poller.
|
||||||
|
static absl::StatusOr<std::unique_ptr<SinglePortSocketListener>> Create( |
||||||
|
WindowsEventEngineListener* listener, SOCKET sock, |
||||||
|
EventEngine::ResolvedAddress addr); |
||||||
|
|
||||||
|
// Two-stage initialization, allows creation of all bound sockets before the
|
||||||
|
// listener is started.
|
||||||
|
absl::Status Start(); |
||||||
|
absl::Status StartLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(io_state_->mu); |
||||||
|
|
||||||
|
// Accessor methods
|
||||||
|
EventEngine::ResolvedAddress listener_sockname() { |
||||||
|
return listener_sockname_; |
||||||
|
}; |
||||||
|
int port() { return port_; } |
||||||
|
|
||||||
|
private: |
||||||
|
struct AsyncIOState; |
||||||
|
|
||||||
|
class OnAcceptCallbackWrapper : public EventEngine::Closure { |
||||||
|
public: |
||||||
|
void Run() override; |
||||||
|
void Prime(std::shared_ptr<AsyncIOState> io_state); |
||||||
|
|
||||||
|
private: |
||||||
|
std::shared_ptr<AsyncIOState> io_state_; |
||||||
|
}; |
||||||
|
|
||||||
|
// A class to manage the data that must outlive the Endpoint.
|
||||||
|
//
|
||||||
|
// Once a listener is done and destroyed, there still may be overlapped
|
||||||
|
// operations pending. To clean up safely, this data must outlive the
|
||||||
|
// Listener, and be destroyed asynchronously when all pending overlapped
|
||||||
|
// events are complete.
|
||||||
|
struct AsyncIOState { |
||||||
|
AsyncIOState(SinglePortSocketListener* port_listener, |
||||||
|
std::unique_ptr<WinSocket> listener_socket); |
||||||
|
~AsyncIOState(); |
||||||
|
SinglePortSocketListener* const port_listener; |
||||||
|
OnAcceptCallbackWrapper on_accept_cb; |
||||||
|
// Synchronize accept handling on the same socket.
|
||||||
|
grpc_core::Mutex mu; |
||||||
|
// This will hold the socket for the next accept.
|
||||||
|
SOCKET accept_socket ABSL_GUARDED_BY(mu) = INVALID_SOCKET; |
||||||
|
// The listener winsocket.
|
||||||
|
std::unique_ptr<WinSocket> listener_socket ABSL_GUARDED_BY(mu); |
||||||
|
}; |
||||||
|
|
||||||
|
SinglePortSocketListener(WindowsEventEngineListener* listener, |
||||||
|
LPFN_ACCEPTEX AcceptEx, |
||||||
|
std::unique_ptr<WinSocket> listener_socket, |
||||||
|
int port, EventEngine::ResolvedAddress hostbyname); |
||||||
|
|
||||||
|
// Bind a recently-created socket for listening
|
||||||
|
struct PrepareListenerSocketResult { |
||||||
|
int port; |
||||||
|
EventEngine::ResolvedAddress hostbyname; |
||||||
|
}; |
||||||
|
static absl::StatusOr<PrepareListenerSocketResult> PrepareListenerSocket( |
||||||
|
SOCKET sock, const EventEngine::ResolvedAddress& addr); |
||||||
|
|
||||||
|
void OnAcceptCallbackLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(io_state_->mu); |
||||||
|
|
||||||
|
// The cached AcceptEx for that port.
|
||||||
|
LPFN_ACCEPTEX AcceptEx; |
||||||
|
// This seemingly magic number comes from AcceptEx's documentation. each
|
||||||
|
// address buffer needs to have at least 16 more bytes at their end.
|
||||||
|
uint8_t addresses_[(sizeof(sockaddr_in6) + 16) * 2] = {}; |
||||||
|
// The parent listener
|
||||||
|
WindowsEventEngineListener* listener_; |
||||||
|
// shared state for asynchronous cleanup of overlapped operations
|
||||||
|
std::shared_ptr<AsyncIOState> io_state_; |
||||||
|
// The actual TCP port number.
|
||||||
|
int port_; |
||||||
|
EventEngine::ResolvedAddress listener_sockname_; |
||||||
|
}; |
||||||
|
absl::StatusOr<SinglePortSocketListener*> AddSinglePortSocketListener( |
||||||
|
SOCKET sock, EventEngine::ResolvedAddress addr); |
||||||
|
|
||||||
|
IOCP* const iocp_; |
||||||
|
const EndpointConfig& config_; |
||||||
|
std::shared_ptr<EventEngine> engine_; |
||||||
|
Executor* executor_; |
||||||
|
const std::unique_ptr<MemoryAllocatorFactory> memory_allocator_factory_; |
||||||
|
AcceptCallback accept_cb_; |
||||||
|
absl::AnyInvocable<void(absl::Status)> on_shutdown_; |
||||||
|
std::atomic<bool> started_{false}; |
||||||
|
grpc_core::Mutex socket_listeners_mu_; |
||||||
|
std::list<std::unique_ptr<SinglePortSocketListener>> port_listeners_ |
||||||
|
ABSL_GUARDED_BY(socket_listeners_mu_); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace experimental
|
||||||
|
} // namespace grpc_event_engine
|
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // GRPC_SRC_CORE_LIB_EVENT_ENGINE_WINDOWS_WINDOWS_LISTENER_H
|
Loading…
Reference in new issue