Reland "[EventEngine] WindowsEventEngine Endpoint and Socket fixes" (#32440)

Relands #32385 (reverted in #32419) with fixes.

The Windows build is clean on a test cherrypick: cl/511291828

<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->

---------

Co-authored-by: drfloob <drfloob@users.noreply.github.com>
pull/32447/head
AJ Heller 2 years ago committed by GitHub
parent 734b551148
commit b53f6fde67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/core/BUILD
  2. 3
      src/core/lib/event_engine/resolved_address.cc
  3. 8
      src/core/lib/event_engine/shim.cc
  4. 18
      src/core/lib/event_engine/windows/iocp.cc
  5. 62
      src/core/lib/event_engine/windows/win_socket.cc
  6. 44
      src/core/lib/event_engine/windows/win_socket.h
  7. 203
      src/core/lib/event_engine/windows/windows_endpoint.cc
  8. 65
      src/core/lib/event_engine/windows/windows_endpoint.h
  9. 46
      src/core/lib/event_engine/windows/windows_engine.cc
  10. 8
      src/core/lib/event_engine/windows/windows_engine.h
  11. 14
      src/core/lib/iomgr/tcp_client_windows.cc
  12. 5
      test/core/event_engine/test_suite/BUILD
  13. 3
      test/core/event_engine/test_suite/tests/client_test.cc
  14. 62
      test/core/event_engine/windows/iocp_test.cc
  15. 14
      test/core/event_engine/windows/win_socket_test.cc
  16. 12
      test/core/event_engine/windows/windows_endpoint_test.cc

@ -2018,8 +2018,10 @@ grpc_cc_library(
"error",
"event_engine_executor",
"event_engine_poller",
"event_engine_tcp_socket_utils",
"event_engine_time_util",
"event_engine_trace",
"//:debug_location",
"//:event_engine_base_hdrs",
"//:gpr",
"//:gpr_platform",

@ -31,7 +31,8 @@ namespace experimental {
EventEngine::ResolvedAddress::ResolvedAddress(const sockaddr* address,
socklen_t size)
: size_(size) {
GPR_ASSERT(size <= sizeof(address_));
GPR_DEBUG_ASSERT(size >= 0);
GPR_ASSERT(static_cast<size_t>(size) <= sizeof(address_));
memcpy(&address_, address, size);
}

@ -25,7 +25,9 @@ namespace experimental {
bool UseEventEngineClient() {
// TODO(hork, eryu): Adjust the ifdefs accordingly when event engines become
// available for other platforms.
#if defined(GRPC_POSIX_SOCKET_TCP) and !defined(GRPC_CFSTREAM)
#if defined(GRPC_POSIX_SOCKET_TCP) && !defined(GRPC_CFSTREAM)
return grpc_core::IsEventEngineClientEnabled();
#elif defined(GPR_WINDOWS)
return grpc_core::IsEventEngineClientEnabled();
#else
return false;
@ -35,7 +37,7 @@ bool UseEventEngineClient() {
bool UseEventEngineListener() {
// TODO(hork, eryu): Adjust the ifdefs accordingly when event engines become
// available for other platforms.
#if defined(GRPC_POSIX_SOCKET_TCP) and !defined(GRPC_CFSTREAM)
#if defined(GRPC_POSIX_SOCKET_TCP) && !defined(GRPC_CFSTREAM)
return grpc_core::IsEventEngineListenerEnabled();
#else
return false;
@ -43,7 +45,7 @@ bool UseEventEngineListener() {
}
bool EventEngineSupportsFd() {
#if defined(GRPC_POSIX_SOCKET_TCP) and !defined(GRPC_CFSTREAM)
#if defined(GRPC_POSIX_SOCKET_TCP) && !defined(GRPC_CFSTREAM)
return true;
#else
return false;

@ -92,21 +92,15 @@ Poller::WorkResult IOCP::Work(EventEngine::Duration timeout,
}
GRPC_EVENT_ENGINE_POLLER_TRACE("IOCP::%p got event on OVERLAPPED::%p", this,
overlapped);
WinSocket* socket = reinterpret_cast<WinSocket*>(completion_key);
// TODO(hork): move the following logic into the WinSocket impl.
// Safety note: socket is guaranteed to exist when managed by a
// WindowsEndpoint. If an overlapped event came in, then either a read event
// handler is registered, which keeps the socket alive, or the WindowsEndpoint
// (which keeps the socket alive) has done an asynchronous WSARecv and is
// about to register for notification of an overlapped event.
auto* socket = reinterpret_cast<WinSocket*>(completion_key);
WinSocket::OpState* info = socket->GetOpInfoForOverlapped(overlapped);
GPR_ASSERT(info != nullptr);
if (socket->IsShutdown()) {
info->SetError(WSAESHUTDOWN);
} else {
info->GetOverlappedResult();
}
if (info->closure() != nullptr) {
schedule_poll_again();
executor_->Run(info->closure());
return Poller::WorkResult::kOk;
}
// No callback registered. Set ready and return an empty set
info->SetReady();
schedule_poll_again();
return Poller::WorkResult::kOk;

@ -18,8 +18,10 @@
#include <grpc/support/log_windows.h>
#include "src/core/lib/event_engine/executor/executor.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/win_socket.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/error.h"
@ -34,29 +36,29 @@
namespace grpc_event_engine {
namespace experimental {
// ---- WinSocket ----
WinSocket::WinSocket(SOCKET socket, Executor* executor) noexcept
: socket_(socket),
executor_(executor),
read_info_(OpState(this)),
write_info_(OpState(this)) {}
read_info_(this),
write_info_(this) {}
WinSocket::~WinSocket() {
GPR_ASSERT(is_shutdown_.load());
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WinSocket::%p destroyed", this);
}
SOCKET WinSocket::socket() { return socket_; }
SOCKET WinSocket::raw_socket() { return socket_; }
void WinSocket::MaybeShutdown(absl::Status why) {
void WinSocket::Shutdown() {
// if already shutdown, return early. Otherwise, set the shutdown flag.
if (is_shutdown_.exchange(true)) {
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WinSocket::%p already shutting down",
this);
return;
}
GRPC_EVENT_ENGINE_ENDPOINT_TRACE(
"WinSocket::%p shutting down now. Reason: %s", this,
why.ToString().c_str());
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WinSocket::%p shutting down now.", this);
// Grab the function pointer for DisconnectEx for that specific socket.
// It may change depending on the interface.
GUID guid = WSAID_DISCONNECTEX;
@ -78,6 +80,14 @@ void WinSocket::MaybeShutdown(absl::Status why) {
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WinSocket::%p socket closed", this);
}
void WinSocket::Shutdown(const grpc_core::DebugLocation& location,
absl::string_view reason) {
GRPC_EVENT_ENGINE_ENDPOINT_TRACE(
"WinSocket::%p Shut down from %s:%d. Reason: %s", this, location.file(),
location.line(), reason.data());
Shutdown();
}
void WinSocket::NotifyOnReady(OpState& info, EventEngine::Closure* closure) {
if (IsShutdown()) {
info.SetError(WSAESHUTDOWN);
@ -87,7 +97,8 @@ void WinSocket::NotifyOnReady(OpState& info, EventEngine::Closure* closure) {
if (std::exchange(info.has_pending_iocp_, false)) {
executor_->Run(closure);
} else {
info.closure_ = closure;
EventEngine::Closure* prev = nullptr;
GPR_ASSERT(info.closure_.compare_exchange_strong(prev, closure));
}
}
@ -102,37 +113,46 @@ void WinSocket::NotifyOnWrite(EventEngine::Closure* on_write) {
// ---- WinSocket::OpState ----
WinSocket::OpState::OpState(WinSocket* win_socket) noexcept
: win_socket_(win_socket), closure_(nullptr) {
: win_socket_(win_socket) {
memset(&overlapped_, 0, sizeof(OVERLAPPED));
}
void WinSocket::OpState::SetReady() {
GPR_ASSERT(!has_pending_iocp_);
if (closure_) {
win_socket_->executor_->Run(closure_);
auto* closure = closure_.exchange(nullptr);
if (closure) {
win_socket_->executor_->Run(closure);
} else {
has_pending_iocp_ = true;
}
}
void WinSocket::OpState::SetError(int wsa_error) {
bytes_transferred_ = 0;
wsa_error_ = wsa_error;
result_ = OverlappedResult{/*wsa_error=*/wsa_error, /*bytes_transferred=*/0};
}
void WinSocket::OpState::SetResult(OverlappedResult result) {
result_ = result;
}
void WinSocket::OpState::GetOverlappedResult() {
GetOverlappedResult(win_socket_->raw_socket());
}
void WinSocket::OpState::GetOverlappedResult(SOCKET sock) {
if (win_socket_->IsShutdown()) {
result_ = OverlappedResult{/*wsa_error=*/WSA_OPERATION_ABORTED,
/*bytes_transferred=*/0};
return;
}
DWORD flags = 0;
DWORD bytes;
BOOL success = WSAGetOverlappedResult(win_socket_->socket(), &overlapped_,
&bytes, FALSE, &flags);
bytes_transferred_ = bytes;
wsa_error_ = success ? 0 : WSAGetLastError();
BOOL success =
WSAGetOverlappedResult(sock, &overlapped_, &bytes, FALSE, &flags);
result_ = OverlappedResult{/*wsa_error=*/success ? 0 : WSAGetLastError(),
/*bytes_transferred=*/bytes};
}
void WinSocket::SetReadable() { read_info_.SetReady(); }
void WinSocket::SetWritable() { write_info_.SetReady(); }
bool WinSocket::IsShutdown() { return is_shutdown_.load(); }
WinSocket::OpState* WinSocket::GetOpInfoForOverlapped(OVERLAPPED* overlapped) {

@ -24,6 +24,7 @@
#include <grpc/event_engine/event_engine.h>
#include "src/core/lib/event_engine/executor/executor.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/sync.h"
namespace grpc_event_engine {
@ -31,6 +32,11 @@ namespace experimental {
class WinSocket {
public:
struct OverlappedResult {
int wsa_error;
DWORD bytes_transferred;
};
// State related to a Read or Write socket operation
class OpState {
public:
@ -42,25 +48,28 @@ class WinSocket {
void SetReady();
// Set error results for a completed op
void SetError(int wsa_error);
// Retrieve results of overlapped operation (via Winsock API)
// Set an OverlappedResult. Useful when WSARecv returns immediately.
void SetResult(OverlappedResult result);
// Retrieve the results of an overlapped operation (via Winsock API) and
// store them locally.
void GetOverlappedResult();
// Retrieve the results of an overlapped operation (via Winsock API) and
// store them locally. This overload allows acceptance of connections on new
// sockets.
void GetOverlappedResult(SOCKET sock);
// Retrieve the cached result from GetOverlappedResult
const OverlappedResult& result() const { return result_; }
// OVERLAPPED, needed for Winsock API calls
LPOVERLAPPED overlapped() { return &overlapped_; }
// Data from the previous operation, set via GetOverlappedResult
DWORD bytes_transferred() const { return bytes_transferred_; }
// Previous error if set.
int wsa_error() const { return wsa_error_; }
EventEngine::Closure* closure() { return closure_; }
private:
friend class WinSocket;
OVERLAPPED overlapped_;
WinSocket* win_socket_ = nullptr;
EventEngine::Closure* closure_ = nullptr;
std::atomic<EventEngine::Closure*> closure_{nullptr};
bool has_pending_iocp_ = false;
DWORD bytes_transferred_;
int wsa_error_;
OverlappedResult result_;
};
WinSocket(SOCKET socket, Executor* executor) noexcept;
@ -71,14 +80,15 @@ class WinSocket {
// - The IOCP hasn't completed yet, and we're queuing it for later.
void NotifyOnRead(EventEngine::Closure* on_read);
void NotifyOnWrite(EventEngine::Closure* on_write);
void SetReadable();
void SetWritable();
// Schedule a shutdown of the socket operations. Will call the pending
// operations to abort them. We need to do that this way because of the
// various callsites of that function, which happens to be in various
// mutex hold states, and that'd be unsafe to call them directly.
void MaybeShutdown(absl::Status why);
bool IsShutdown();
// Shutdown socket operations, but do not delete the WinSocket.
// Connections will be disconnected, and the socket will be closed.
// If the socket is managed by a shared_ptr (most should be), then the
// WinSocket will be deleted when the last outstanding overlapped event comes
// back.
void Shutdown();
void Shutdown(const grpc_core::DebugLocation& location,
absl::string_view reason);
// Return the appropriate OpState for a given OVERLAPPED
// Returns nullptr if the overlapped does not match either read or write ops.
@ -87,7 +97,7 @@ class WinSocket {
OpState* read_info() { return &read_info_; }
OpState* write_info() { return &write_info_; }
// Accessor method for underlying socket
SOCKET socket();
SOCKET raw_socket();
private:
void NotifyOnReady(OpState& info, EventEngine::Closure* closure);

@ -33,21 +33,10 @@
namespace grpc_event_engine {
namespace experimental {
// TODO(hork): The previous implementation required internal ref counting. Add
// this when it becomes necessary.
// TODO(hork): The previous implementation required a 2-phase shutdown. Add this
// when it becomes necessary.
namespace {
constexpr int64_t kDefaultTargetReadSize = 8192;
constexpr size_t kDefaultTargetReadSize = 8192;
constexpr int kMaxWSABUFCount = 16;
void AbortOnEvent(absl::Status) {
grpc_core::Crash(
"INTERNAL ERROR: Asked to handle read/write event with an invalid "
"callback");
}
} // namespace
WindowsEndpoint::WindowsEndpoint(
@ -55,15 +44,13 @@ WindowsEndpoint::WindowsEndpoint(
std::unique_ptr<WinSocket> socket, MemoryAllocator&& allocator,
const EndpointConfig& /* config */, Executor* executor)
: peer_address_(peer_address),
socket_(std::move(socket)),
allocator_(std::move(allocator)),
handle_read_event_(this),
handle_write_event_(this),
executor_(executor) {
executor_(executor),
io_state_(std::make_shared<AsyncIOState>(this, std::move(socket))) {
char addr[EventEngine::ResolvedAddress::MAX_SIZE_BYTES];
int addr_len = sizeof(addr);
if (getsockname(socket_->socket(), reinterpret_cast<sockaddr*>(addr),
&addr_len) < 0) {
if (getsockname(io_state_->socket->raw_socket(),
reinterpret_cast<sockaddr*>(addr), &addr_len) < 0) {
grpc_core::Crash(absl::StrFormat(
"Unrecoverable error: Failed to get local socket name. %s",
GRPC_WSA_ERROR(WSAGetLastError(), "getsockname").ToString().c_str()));
@ -75,50 +62,67 @@ WindowsEndpoint::WindowsEndpoint(
}
WindowsEndpoint::~WindowsEndpoint() {
socket_->MaybeShutdown(absl::OkStatus());
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WindowsEndpoint::%p destoyed", this);
}
void WindowsEndpoint::Read(absl::AnyInvocable<void(absl::Status)> on_read,
SliceBuffer* buffer, const ReadArgs* args) {
// TODO(hork): last_read_buffer from iomgr: Is it only garbage, or optimized?
SliceBuffer* buffer, const ReadArgs* /* args */) {
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WindowsEndpoint::%p reading", this);
if (io_state_->socket->IsShutdown()) {
executor_->Run([on_read = std::move(on_read)]() mutable {
on_read(absl::UnavailableError("Socket is shutting down."));
});
return;
}
// Prepare the WSABUF struct
WSABUF wsa_buffers[kMaxWSABUFCount];
int min_read_size = kDefaultTargetReadSize;
if (args != nullptr && args->read_hint_bytes > 0) {
min_read_size = args->read_hint_bytes;
}
// TODO(hork): introduce a last_read_buffer to save unused sliced.
buffer->Clear();
// TODO(hork): sometimes args->read_hint_bytes is 1, which is not useful.
// Choose an appropriate size.
size_t min_read_size = kDefaultTargetReadSize;
if (buffer->Length() < min_read_size && buffer->Count() < kMaxWSABUFCount) {
buffer->AppendIndexed(Slice(allocator_.MakeSlice(min_read_size)));
}
GPR_ASSERT(buffer->Count() <= kMaxWSABUFCount);
for (int i = 0; i < buffer->Count(); i++) {
Slice tmp = buffer->RefSlice(i);
wsa_buffers[i].buf = (char*)tmp.begin();
wsa_buffers[i].len = tmp.size();
for (size_t i = 0; i < buffer->Count(); i++) {
auto& slice = buffer->MutableSliceAt(i);
wsa_buffers[i].buf = (char*)slice.begin();
wsa_buffers[i].len = slice.size();
}
DWORD bytes_read = 0;
DWORD flags = 0;
// First let's try a synchronous, non-blocking read.
int status = WSARecv(socket_->socket(), wsa_buffers, (DWORD)buffer->Count(),
&bytes_read, &flags, nullptr, nullptr);
int status =
WSARecv(io_state_->socket->raw_socket(), wsa_buffers,
(DWORD)buffer->Count(), &bytes_read, &flags, nullptr, nullptr);
int wsa_error = status == 0 ? 0 : WSAGetLastError();
// Did we get data immediately ? Yay.
if (wsa_error != WSAEWOULDBLOCK) {
io_state_->socket->read_info()->SetResult(
{/*wsa_error=*/wsa_error, /*bytes_read=*/bytes_read});
absl::Status result;
if (bytes_read == 0) {
result = absl::UnavailableError("End of TCP stream");
grpc_core::StatusSetInt(&result, grpc_core::StatusIntProperty::kRpcStatus,
GRPC_STATUS_UNAVAILABLE);
buffer->Clear();
} else {
result = absl::OkStatus();
// prune slicebuffer
if (bytes_read != buffer->Length()) {
buffer->RemoveLastNBytes(buffer->Length() - bytes_read);
}
executor_->Run([on_read = std::move(on_read)]() mutable {
on_read(absl::OkStatus());
});
}
executor_->Run(
[result, on_read = std::move(on_read)]() mutable { on_read(result); });
return;
}
// Otherwise, let's retry, by queuing a read.
memset(socket_->read_info()->overlapped(), 0, sizeof(OVERLAPPED));
status =
WSARecv(socket_->socket(), wsa_buffers, (DWORD)buffer->Count(),
&bytes_read, &flags, socket_->read_info()->overlapped(), nullptr);
memset(io_state_->socket->read_info()->overlapped(), 0, sizeof(OVERLAPPED));
status = WSARecv(io_state_->socket->raw_socket(), wsa_buffers,
(DWORD)buffer->Count(), &bytes_read, &flags,
io_state_->socket->read_info()->overlapped(), nullptr);
wsa_error = status == 0 ? 0 : WSAGetLastError();
if (wsa_error != 0 && wsa_error != WSA_IO_PENDING) {
// Async read returned immediately with an error
@ -129,16 +133,21 @@ void WindowsEndpoint::Read(absl::AnyInvocable<void(absl::Status)> on_read,
});
return;
}
handle_read_event_.Prime(buffer, std::move(on_read));
socket_->NotifyOnRead(&handle_read_event_);
io_state_->handle_read_event.Prime(io_state_, buffer, std::move(on_read));
io_state_->socket->NotifyOnRead(&io_state_->handle_read_event);
}
void WindowsEndpoint::Write(absl::AnyInvocable<void(absl::Status)> on_writable,
SliceBuffer* data, const WriteArgs* /* args */) {
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WindowsEndpoint::%p writing", this);
if (io_state_->socket->IsShutdown()) {
executor_->Run([on_writable = std::move(on_writable)]() mutable {
on_writable(absl::UnavailableError("Socket is shutting down."));
});
return;
}
if (grpc_event_engine_endpoint_data_trace.enabled()) {
for (int i = 0; i < data->Count(); i++) {
for (size_t i = 0; i < data->Count(); i++) {
auto str = data->RefSlice(i).as_string_view();
gpr_log(GPR_INFO, "WindowsEndpoint::%p WRITE (peer=%s): %.*s", this,
peer_address_string_.c_str(), str.length(), str.data());
@ -146,16 +155,16 @@ void WindowsEndpoint::Write(absl::AnyInvocable<void(absl::Status)> on_writable,
}
GPR_ASSERT(data->Count() <= UINT_MAX);
absl::InlinedVector<WSABUF, kMaxWSABUFCount> buffers(data->Count());
for (int i = 0; i < data->Count(); i++) {
auto slice = data->RefSlice(i);
for (size_t i = 0; i < data->Count(); i++) {
auto& slice = data->MutableSliceAt(i);
GPR_ASSERT(slice.size() <= ULONG_MAX);
buffers[i].len = slice.size();
buffers[i].buf = (char*)slice.begin();
}
// First, let's try a synchronous, non-blocking write.
DWORD bytes_sent;
int status = WSASend(socket_->socket(), buffers.data(), (DWORD)buffers.size(),
&bytes_sent, 0, nullptr, nullptr);
int status = WSASend(io_state_->socket->raw_socket(), buffers.data(),
(DWORD)buffers.size(), &bytes_sent, 0, nullptr, nullptr);
size_t async_buffers_offset = 0;
if (status == 0) {
if (bytes_sent == data->Length()) {
@ -166,7 +175,7 @@ void WindowsEndpoint::Write(absl::AnyInvocable<void(absl::Status)> on_writable,
}
// The data was not completely delivered, we should send the rest of it by
// doing an async write operation.
for (int i = 0; i < data->Count(); i++) {
for (size_t i = 0; i < data->Count(); i++) {
if (buffers[i].len > bytes_sent) {
buffers[i].buf += bytes_sent;
buffers[i].len -= bytes_sent;
@ -187,9 +196,10 @@ void WindowsEndpoint::Write(absl::AnyInvocable<void(absl::Status)> on_writable,
return;
}
}
auto write_info = socket_->write_info();
auto write_info = io_state_->socket->write_info();
memset(write_info->overlapped(), 0, sizeof(OVERLAPPED));
status = WSASend(socket_->socket(), &buffers[async_buffers_offset],
status =
WSASend(io_state_->socket->raw_socket(), &buffers[async_buffers_offset],
(DWORD)(data->Count() - async_buffers_offset), nullptr, 0,
write_info->overlapped(), nullptr);
@ -204,8 +214,8 @@ void WindowsEndpoint::Write(absl::AnyInvocable<void(absl::Status)> on_writable,
}
// As all is now setup, we can now ask for the IOCP notification. It may
// trigger the callback immediately however, but no matter.
handle_write_event_.Prime(data, std::move(on_writable));
socket_->NotifyOnWrite(&handle_write_event_);
io_state_->handle_write_event.Prime(io_state_, data, std::move(on_writable));
io_state_->socket->NotifyOnWrite(&io_state_->handle_write_event);
}
const EventEngine::ResolvedAddress& WindowsEndpoint::GetPeerAddress() const {
return peer_address_;
@ -214,38 +224,70 @@ const EventEngine::ResolvedAddress& WindowsEndpoint::GetLocalAddress() const {
return local_address_;
}
// ---- Handle{Read|Write}Closure
// ---- Handle{Read|Write}Closure ----
namespace {
void AbortOnEvent(absl::Status) {
grpc_core::Crash(
"INTERNAL ERROR: Asked to handle read/write event with an invalid "
"callback");
}
} // namespace
void WindowsEndpoint::HandleReadClosure::Reset() {
cb_ = &AbortOnEvent;
buffer_ = nullptr;
}
void WindowsEndpoint::HandleWriteClosure::Reset() {
cb_ = &AbortOnEvent;
buffer_ = nullptr;
}
void WindowsEndpoint::HandleReadClosure::Prime(
std::shared_ptr<AsyncIOState> io_state, SliceBuffer* buffer,
absl::AnyInvocable<void(absl::Status)> cb) {
io_state_ = std::move(io_state);
cb_ = std::move(cb);
buffer_ = buffer;
}
WindowsEndpoint::BaseEventClosure::BaseEventClosure(WindowsEndpoint* endpoint)
: cb_(&AbortOnEvent), endpoint_(endpoint) {}
void WindowsEndpoint::HandleWriteClosure::Prime(
std::shared_ptr<AsyncIOState> io_state, SliceBuffer* buffer,
absl::AnyInvocable<void(absl::Status)> cb) {
io_state_ = std::move(io_state);
cb_ = std::move(cb);
buffer_ = buffer;
}
void WindowsEndpoint::HandleReadClosure::Run() {
// Deletes the shared_ptr when this closure returns
auto io_state = std::move(io_state_);
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WindowsEndpoint::%p Handling Read Event",
endpoint_);
io_state->endpoint);
absl::Status status;
auto* read_info = endpoint_->socket_->read_info();
auto cb_cleanup = absl::MakeCleanup([this, &status]() {
auto cb = std::move(cb_);
cb_ = &AbortOnEvent;
Reset();
cb(status);
});
if (read_info->wsa_error() != 0) {
status = GRPC_WSA_ERROR(read_info->wsa_error(), "Async Read Error");
const auto result = io_state->socket->read_info()->result();
if (result.wsa_error != 0) {
status = GRPC_WSA_ERROR(result.wsa_error, "Async Read Error");
buffer_->Clear();
return;
}
if (read_info->bytes_transferred() > 0) {
GPR_ASSERT(read_info->bytes_transferred() <= buffer_->Length());
if (read_info->bytes_transferred() != buffer_->Length()) {
buffer_->RemoveLastNBytes(buffer_->Length() -
read_info->bytes_transferred());
if (result.bytes_transferred > 0) {
GPR_ASSERT(result.bytes_transferred <= buffer_->Length());
if (result.bytes_transferred != buffer_->Length()) {
buffer_->RemoveLastNBytes(buffer_->Length() - result.bytes_transferred);
}
GPR_ASSERT(read_info->bytes_transferred() == buffer_->Length());
GPR_ASSERT(result.bytes_transferred == buffer_->Length());
if (grpc_event_engine_endpoint_data_trace.enabled()) {
for (int i = 0; i < buffer_->Count(); i++) {
for (size_t i = 0; i < buffer_->Count(); i++) {
auto str = buffer_->RefSlice(i).as_string_view();
gpr_log(GPR_INFO, "WindowsEndpoint::%p READ (peer=%s): %.*s", this,
endpoint_->peer_address_string_.c_str(), str.length(),
gpr_log(GPR_INFO, "WindowsEndpoint::%p READ (peer=%s): %.*s",
io_state->endpoint,
io_state->endpoint->peer_address_string_.c_str(), str.length(),
str.data());
}
}
@ -253,25 +295,36 @@ void WindowsEndpoint::HandleReadClosure::Run() {
}
// Either the endpoint is shut down or we've seen the end of the stream
buffer_->Clear();
// TODO(hork): different error message if shut down
status = absl::UnavailableError("End of TCP stream");
}
void WindowsEndpoint::HandleWriteClosure::Run() {
// Deletes the shared_ptr when this closure returns
auto io_state = std::move(io_state_);
GRPC_EVENT_ENGINE_ENDPOINT_TRACE("WindowsEndpoint::%p Handling Write Event",
endpoint_);
auto* write_info = endpoint_->socket_->write_info();
io_state_->endpoint);
auto cb = std::move(cb_);
cb_ = &AbortOnEvent;
const auto result = io_state->socket->write_info()->result();
Reset();
absl::Status status;
if (write_info->wsa_error() != 0) {
status = GRPC_WSA_ERROR(write_info->wsa_error(), "WSASend");
if (result.wsa_error != 0) {
status = GRPC_WSA_ERROR(result.wsa_error, "WSASend");
} else {
GPR_ASSERT(write_info->bytes_transferred() == buffer_->Length());
GPR_ASSERT(result.bytes_transferred == buffer_->Length());
}
cb(status);
}
// ---- AsyncIOState ----
WindowsEndpoint::AsyncIOState::AsyncIOState(WindowsEndpoint* endpoint,
std::unique_ptr<WinSocket> socket)
: endpoint(endpoint), socket(std::move(socket)) {}
WindowsEndpoint::AsyncIOState::~AsyncIOState() {
socket->Shutdown(DEBUG_LOCATION, "~AsyncIOState");
}
} // namespace experimental
} // namespace grpc_event_engine

@ -39,51 +39,60 @@ class WindowsEndpoint : public EventEngine::Endpoint {
const EventEngine::ResolvedAddress& GetLocalAddress() const override;
private:
// Base class for the Read- and Write-specific event handler callbacks
class BaseEventClosure : public EventEngine::Closure {
public:
explicit BaseEventClosure(WindowsEndpoint* endpoint);
// Calls the bound application callback, inline.
// If called through IOCP, this will be run from within an Executor.
virtual void Run() = 0;
// Prepare the closure by setting the application callback and SliceBuffer
void Prime(SliceBuffer* buffer, absl::AnyInvocable<void(absl::Status)> cb) {
cb_ = std::move(cb);
buffer_ = buffer;
}
protected:
absl::AnyInvocable<void(absl::Status)> cb_;
SliceBuffer* buffer_;
WindowsEndpoint* endpoint_;
};
struct AsyncIOState;
// Permanent closure type for Read callbacks
class HandleReadClosure : public BaseEventClosure {
class HandleReadClosure : public EventEngine::Closure {
public:
explicit HandleReadClosure(WindowsEndpoint* endpoint)
: BaseEventClosure(endpoint) {}
void Run() override;
void Prime(std::shared_ptr<AsyncIOState> io_state, SliceBuffer* buffer,
absl::AnyInvocable<void(absl::Status)> cb);
// Resets the per-request data
void Reset();
private:
std::shared_ptr<AsyncIOState> io_state_;
absl::AnyInvocable<void(absl::Status)> cb_;
SliceBuffer* buffer_ = nullptr;
};
// Permanent closure type for Write callbacks
class HandleWriteClosure : public BaseEventClosure {
class HandleWriteClosure : public EventEngine::Closure {
public:
explicit HandleWriteClosure(WindowsEndpoint* endpoint)
: BaseEventClosure(endpoint) {}
void Run() override;
void Prime(std::shared_ptr<AsyncIOState> io_state, SliceBuffer* buffer,
absl::AnyInvocable<void(absl::Status)> cb);
// Resets the per-request data
void Reset();
private:
std::shared_ptr<AsyncIOState> io_state_;
absl::AnyInvocable<void(absl::Status)> cb_;
SliceBuffer* buffer_ = nullptr;
};
// A class to manage the data that must outlive the Endpoint.
//
// Once an endpoint is done and destroyed, there still may be overlapped
// operations pending. To clean up safely, this data must outlive the
// Endpoint, and be destroyed asynchronously when all pending overlapped
// events are complete.
struct AsyncIOState {
AsyncIOState(WindowsEndpoint* endpoint, std::unique_ptr<WinSocket> socket);
~AsyncIOState();
WindowsEndpoint* const endpoint;
std::unique_ptr<WinSocket> socket;
HandleReadClosure handle_read_event;
HandleWriteClosure handle_write_event;
};
EventEngine::ResolvedAddress peer_address_;
std::string peer_address_string_;
EventEngine::ResolvedAddress local_address_;
std::string local_address_string_;
std::unique_ptr<WinSocket> socket_;
MemoryAllocator allocator_;
HandleReadClosure handle_read_event_;
HandleWriteClosure handle_write_event_;
Executor* executor_;
std::shared_ptr<AsyncIOState> io_state_;
};
} // namespace experimental

@ -183,28 +183,33 @@ bool WindowsEventEngine::IsWorkerThread() { grpc_core::Crash("unimplemented"); }
void WindowsEventEngine::OnConnectCompleted(
std::shared_ptr<ConnectionState> state) {
absl::StatusOr<std::unique_ptr<WindowsEndpoint>> endpoint;
EventEngine::OnConnectCallback cb;
{
// Connection attempt complete!
grpc_core::MutexLock lock(&state->mu);
cb = std::move(state->on_connected_user_callback);
state->on_connected = nullptr;
{
grpc_core::MutexLock handle_lock(&connection_mu_);
known_connection_handles_.erase(state->connection_handle);
}
const auto& overlapped_result = state->socket->write_info()->result();
// return early if we cannot cancel the connection timeout timer.
if (!Cancel(state->timer_handle)) return;
auto write_info = state->socket->write_info();
if (write_info->wsa_error() != 0) {
auto error = GRPC_WSA_ERROR(write_info->wsa_error(), "ConnectEx");
state->socket->MaybeShutdown(error);
state->on_connected_user_callback(error);
return;
}
// This code should be running in an executor thread already, so the callback
// can be run directly.
if (overlapped_result.wsa_error != 0) {
state->socket->Shutdown(DEBUG_LOCATION, "ConnectEx failure");
endpoint = GRPC_WSA_ERROR(overlapped_result.wsa_error, "ConnectEx");
} else {
// This code should be running in an executor thread already, so the
// callback can be run directly.
ChannelArgsEndpointConfig cfg;
state->on_connected_user_callback(std::make_unique<WindowsEndpoint>(
endpoint = std::make_unique<WindowsEndpoint>(
state->address, std::move(state->socket), std::move(state->allocator),
cfg, executor_.get()));
cfg, executor_.get());
}
}
cb(std::move(endpoint));
}
EventEngine::ConnectionHandle WindowsEventEngine::Connect(
@ -276,18 +281,18 @@ EventEngine::ConnectionHandle WindowsEventEngine::Connect(
auto watched_socket = iocp_.Watch(sock);
auto* info = watched_socket->write_info();
bool success =
ConnectEx(watched_socket->socket(), address.address(), address.size(),
ConnectEx(watched_socket->raw_socket(), address.address(), address.size(),
nullptr, 0, nullptr, info->overlapped());
// It wouldn't be unusual to get a success immediately. But we'll still get an
// IOCP notification, so let's ignore it.
if (!success) {
int last_error = WSAGetLastError();
if (last_error != ERROR_IO_PENDING) {
auto status = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx");
Run([on_connect = std::move(on_connect), status]() mutable {
Run([on_connect = std::move(on_connect),
status = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx")]() mutable {
on_connect(status);
});
watched_socket->MaybeShutdown(status);
watched_socket->Shutdown(DEBUG_LOCATION, "ConnectEx");
return EventEngine::kInvalidConnectionHandle;
}
}
@ -360,8 +365,7 @@ bool WindowsEventEngine::CancelConnectFromDeadlineTimer(
bool WindowsEventEngine::CancelConnectInternalStateLocked(
ConnectionState* connection_state) {
connection_state->socket->MaybeShutdown(
absl::CancelledError("CancelConnect"));
connection_state->socket->Shutdown(DEBUG_LOCATION, "CancelConnect");
// Release the connection_state shared_ptr. connection_state is now invalid.
delete connection_state->on_connected;
GRPC_EVENT_ENGINE_TRACE("Successfully cancelled connection %s",
@ -373,10 +377,10 @@ bool WindowsEventEngine::CancelConnectInternalStateLocked(
absl::StatusOr<std::unique_ptr<EventEngine::Listener>>
WindowsEventEngine::CreateListener(
Listener::AcceptCallback on_accept,
absl::AnyInvocable<void(absl::Status)> on_shutdown,
const EndpointConfig& config,
std::unique_ptr<MemoryAllocatorFactory> memory_allocator_factory) {
Listener::AcceptCallback /* on_accept */,
absl::AnyInvocable<void(absl::Status)> /* on_shutdown */,
const EndpointConfig& /* config */,
std::unique_ptr<MemoryAllocatorFactory> /* memory_allocator_factory */) {
grpc_core::Crash("unimplemented");
}

@ -88,6 +88,12 @@ class WindowsEventEngine : public EventEngine,
absl::AnyInvocable<void()> closure) override;
bool Cancel(TaskHandle handle) override;
// Retrieve the base executor.
// This is public because most classes that know the concrete
// WindowsEventEngine type are effectively friends.
// Not intended for external use.
Executor* executor() { return executor_.get(); }
private:
// State of an active connection.
// Managed by a shared_ptr, owned exclusively by the timeout callback and the
@ -133,7 +139,7 @@ class WindowsEventEngine : public EventEngine,
bool CancelConnectInternalStateLocked(ConnectionState* connection_state)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(connection_state->mu);
class TimerClosure;
struct TimerClosure;
EventEngine::TaskHandle RunAfterInternal(Duration when,
absl::AnyInvocable<void()> cb);
grpc_core::Mutex task_mu_;

@ -31,7 +31,9 @@
#include <grpc/support/log_windows.h>
#include "src/core/lib/address_utils/sockaddr_utils.h"
#include "src/core/lib/event_engine/shim.h"
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/iomgr/event_engine_shims/tcp_client.h"
#include "src/core/lib/iomgr/iocp_windows.h"
#include "src/core/lib/iomgr/sockaddr.h"
#include "src/core/lib/iomgr/sockaddr_windows.h"
@ -126,6 +128,10 @@ static int64_t tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint,
const EndpointConfig& config,
const grpc_resolved_address* addr,
grpc_core::Timestamp deadline) {
if (grpc_event_engine::experimental::UseEventEngineClient()) {
return grpc_event_engine::experimental::event_engine_tcp_client_connect(
on_done, endpoint, config, addr, deadline);
}
SOCKET sock = INVALID_SOCKET;
BOOL success;
int status;
@ -232,7 +238,13 @@ failure:
return 0;
}
static bool tcp_cancel_connect(int64_t /*connection_handle*/) { return false; }
static bool tcp_cancel_connect(int64_t connection_handle) {
if (grpc_event_engine::experimental::UseEventEngineClient()) {
return grpc_event_engine::experimental::
event_engine_tcp_client_cancel_connect(connection_handle);
}
return false;
}
grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect,
tcp_cancel_connect};

@ -64,11 +64,12 @@ grpc_cc_test(
],
uses_polling = False,
deps = [
# TODO(hork): enable when the listener (or an oracle) is available
# "//test/core/event_engine/test_suite/tests:client",
"//test/core/event_engine/test_suite/tests:timer",
"//src/core:windows_event_engine",
"//test/core/event_engine:event_engine_test_utils",
# TODO(hork): enable when the listener (or an oracle) is available
# "//test/core/event_engine/test_suite/tests:client",
# "//test/core/event_engine/test_suite/tests:server",
],
)

@ -106,7 +106,7 @@ TEST_F(EventEngineClientTest, ConnectToNonExistentListenerTest) {
TEST_F(EventEngineClientTest, ConnectExchangeBidiDataTransferTest) {
grpc_core::ExecCtx ctx;
auto oracle_ee = this->NewOracleEventEngine();
std::shared_ptr<EventEngine> oracle_ee(this->NewOracleEventEngine());
std::shared_ptr<EventEngine> test_ee(this->NewEventEngine());
auto memory_quota = std::make_unique<grpc_core::MemoryQuota>("bar");
std::string target_addr = absl::StrCat(
@ -168,6 +168,7 @@ TEST_F(EventEngineClientTest, ConnectExchangeBidiDataTransferTest) {
}
client_endpoint.reset();
server_endpoint.reset();
listener.reset();
WaitForSingleOwner(std::move(test_ee));
}

@ -75,10 +75,8 @@ TEST_F(IOCPTest, ClientReceivesNotificationOfServerSend) {
char read_char_buffer[2048];
read_wsabuf.buf = read_char_buffer;
DWORD bytes_rcvd;
memset(wrapped_client_socket->read_info()->overlapped(), 0,
sizeof(OVERLAPPED));
int status =
WSARecv(wrapped_client_socket->socket(), &read_wsabuf, 1, &bytes_rcvd,
int status = WSARecv(
wrapped_client_socket->raw_socket(), &read_wsabuf, 1, &bytes_rcvd,
&flags, wrapped_client_socket->read_info()->overlapped(), NULL);
// Expecting error 997, WSA_IO_PENDING
EXPECT_EQ(status, -1);
@ -87,11 +85,10 @@ TEST_F(IOCPTest, ClientReceivesNotificationOfServerSend) {
if (last_error != WSA_IO_PENDING) {
LogErrorMessage(last_error, "WSARecv");
}
on_read =
new AnyInvocableClosure([win_socket = wrapped_client_socket.get(),
&read_called, &read_wsabuf, &bytes_rcvd]() {
on_read = new AnyInvocableClosure([win_socket = wrapped_client_socket.get(),
&read_called, &read_wsabuf]() {
gpr_log(GPR_DEBUG, "Notified on read");
EXPECT_GE(win_socket->read_info()->bytes_transferred(), 10);
EXPECT_GE(win_socket->read_info()->result().bytes_transferred, 10u);
EXPECT_STREQ(read_wsabuf.buf, "hello!");
read_called.Notify();
});
@ -104,11 +101,9 @@ TEST_F(IOCPTest, ClientReceivesNotificationOfServerSend) {
write_wsabuf.len = 2048;
write_wsabuf.buf = write_char_buffer;
DWORD bytes_sent;
memset(wrapped_server_socket->write_info()->overlapped(), 0,
sizeof(OVERLAPPED));
int status =
WSASend(wrapped_server_socket->socket(), &write_wsabuf, 1, &bytes_sent,
0, wrapped_server_socket->write_info()->overlapped(), NULL);
int status = WSASend(
wrapped_server_socket->raw_socket(), &write_wsabuf, 1, &bytes_sent, 0,
wrapped_server_socket->write_info()->overlapped(), NULL);
EXPECT_EQ(status, 0);
if (status != 0) {
LogErrorMessage(WSAGetLastError(), "WSASend");
@ -137,8 +132,9 @@ TEST_F(IOCPTest, ClientReceivesNotificationOfServerSend) {
delete on_read;
delete on_write;
wrapped_client_socket->MaybeShutdown(absl::OkStatus());
wrapped_server_socket->MaybeShutdown(absl::OkStatus());
wrapped_client_socket->Shutdown();
wrapped_server_socket->Shutdown();
iocp.Shutdown();
executor.Quiesce();
}
@ -159,10 +155,8 @@ TEST_F(IOCPTest, IocpWorkTimeoutDueToNoNotificationRegistered) {
char read_char_buffer[2048];
read_wsabuf.buf = read_char_buffer;
DWORD bytes_rcvd;
memset(wrapped_client_socket->read_info()->overlapped(), 0,
sizeof(OVERLAPPED));
int status =
WSARecv(wrapped_client_socket->socket(), &read_wsabuf, 1, &bytes_rcvd,
int status = WSARecv(
wrapped_client_socket->raw_socket(), &read_wsabuf, 1, &bytes_rcvd,
&flags, wrapped_client_socket->read_info()->overlapped(), NULL);
// Expecting error 997, WSA_IO_PENDING
EXPECT_EQ(status, -1);
@ -171,11 +165,10 @@ TEST_F(IOCPTest, IocpWorkTimeoutDueToNoNotificationRegistered) {
if (last_error != WSA_IO_PENDING) {
LogErrorMessage(last_error, "WSARecv");
}
on_read =
new AnyInvocableClosure([win_socket = wrapped_client_socket.get(),
&read_called, &read_wsabuf, &bytes_rcvd]() {
on_read = new AnyInvocableClosure([win_socket = wrapped_client_socket.get(),
&read_called, &read_wsabuf]() {
gpr_log(GPR_DEBUG, "Notified on read");
EXPECT_GE(win_socket->read_info()->bytes_transferred(), 10);
EXPECT_GE(win_socket->read_info()->result().bytes_transferred, 10u);
EXPECT_STREQ(read_wsabuf.buf, "hello!");
read_called.Notify();
});
@ -207,7 +200,8 @@ TEST_F(IOCPTest, IocpWorkTimeoutDueToNoNotificationRegistered) {
// wait for the callbacks to run
read_called.WaitForNotification();
delete on_read;
wrapped_client_socket->MaybeShutdown(absl::OkStatus());
wrapped_client_socket->Shutdown();
iocp.Shutdown();
executor.Quiesce();
}
@ -281,12 +275,11 @@ TEST_F(IOCPTest, StressTestThousandsOfSockets) {
std::vector<std::thread> threads;
threads.reserve(thread_count);
for (int thread_n = 0; thread_n < thread_count; thread_n++) {
threads.emplace_back([thread_n, sockets_per_thread, &read_count,
&write_count] {
threads.emplace_back([sockets_per_thread, &read_count, &write_count] {
ThreadPool executor;
IOCP iocp(&executor);
// Start a looping worker thread with a moderate timeout
std::thread iocp_worker([&iocp, &executor] {
std::thread iocp_worker([&iocp] {
Poller::WorkResult result;
do {
result = iocp.Work(std::chrono::seconds(1), []() {});
@ -302,14 +295,14 @@ TEST_F(IOCPTest, StressTestThousandsOfSockets) {
[&read_count,
win_socket = std::move(wrapped_client_socket)]() mutable {
read_count.fetch_add(1);
win_socket->MaybeShutdown(absl::OkStatus());
win_socket->Shutdown();
}));
auto* pserver = wrapped_server_socket.get();
pserver->NotifyOnWrite(SelfDeletingClosure::Create(
[&write_count,
win_socket = std::move(wrapped_server_socket)]() mutable {
write_count.fetch_add(1);
win_socket->MaybeShutdown(absl::OkStatus());
win_socket->Shutdown();
}));
{
// Set the client to receive
@ -319,10 +312,9 @@ TEST_F(IOCPTest, StressTestThousandsOfSockets) {
read_wsabuf.buf = read_char_buffer;
DWORD bytes_rcvd;
DWORD flags = 0;
memset(pclient->read_info()->overlapped(), 0, sizeof(OVERLAPPED));
int status =
WSARecv(pclient->socket(), &read_wsabuf, 1, &bytes_rcvd, &flags,
pclient->read_info()->overlapped(), NULL);
WSARecv(pclient->raw_socket(), &read_wsabuf, 1, &bytes_rcvd,
&flags, pclient->read_info()->overlapped(), NULL);
// Expecting error 997, WSA_IO_PENDING
EXPECT_EQ(status, -1);
int last_error = WSAGetLastError();
@ -338,9 +330,9 @@ TEST_F(IOCPTest, StressTestThousandsOfSockets) {
write_wsabuf.len = 20;
write_wsabuf.buf = write_char_buffer;
DWORD bytes_sent;
memset(pserver->write_info()->overlapped(), 0, sizeof(OVERLAPPED));
int status = WSASend(pserver->socket(), &write_wsabuf, 1, &bytes_sent,
0, pserver->write_info()->overlapped(), NULL);
int status =
WSASend(pserver->raw_socket(), &write_wsabuf, 1, &bytes_sent, 0,
pserver->write_info()->overlapped(), NULL);
if (status != 0) {
int wsa_error = WSAGetLastError();
if (wsa_error != WSA_IO_PENDING) {

@ -52,7 +52,7 @@ TEST_F(WinSocketTest, ManualReadEventTriggeredWithoutIO) {
AnyInvocableClosure on_write([] { FAIL() << "No Write expected"; });
wrapped_client_socket.NotifyOnWrite(&on_write);
ASSERT_FALSE(read_called);
wrapped_client_socket.SetReadable();
wrapped_client_socket.read_info()->SetReady();
absl::Time deadline = absl::Now() + absl::Seconds(10);
while (!read_called) {
absl::SleepFor(absl::Milliseconds(42));
@ -61,8 +61,8 @@ TEST_F(WinSocketTest, ManualReadEventTriggeredWithoutIO) {
}
}
ASSERT_TRUE(read_called);
wrapped_client_socket.MaybeShutdown(absl::CancelledError("done"));
wrapped_server_socket.MaybeShutdown(absl::CancelledError("done"));
wrapped_client_socket.Shutdown();
wrapped_server_socket.Shutdown();
executor.Quiesce();
}
@ -71,11 +71,13 @@ TEST_F(WinSocketTest, NotificationCalledImmediatelyOnShutdownWinSocket) {
SOCKET sockpair[2];
CreateSockpair(sockpair, IOCP::GetDefaultSocketFlags());
WinSocket wrapped_client_socket(sockpair[0], &executor);
wrapped_client_socket.MaybeShutdown(absl::CancelledError("testing"));
wrapped_client_socket.Shutdown();
bool read_called = false;
AnyInvocableClosure closure([&wrapped_client_socket, &read_called] {
ASSERT_EQ(wrapped_client_socket.read_info()->bytes_transferred(), 0);
ASSERT_EQ(wrapped_client_socket.read_info()->wsa_error(), WSAESHUTDOWN);
ASSERT_EQ(wrapped_client_socket.read_info()->result().bytes_transferred,
0u);
ASSERT_EQ(wrapped_client_socket.read_info()->result().wsa_error,
WSAESHUTDOWN);
read_called = true;
});
wrapped_client_socket.NotifyOnRead(&closure);

@ -62,8 +62,8 @@ TEST_F(WindowsEndpointTest, BasicCommunication) {
grpc_core::Notification read_done;
SliceBuffer read_buffer;
server.Read(
[&read_done, &message, &read_buffer](absl::Status status) {
ASSERT_EQ(read_buffer.Count(), 1);
[&read_done, &message, &read_buffer](absl::Status) {
ASSERT_EQ(read_buffer.Count(), 1u);
auto slice = read_buffer.TakeFirst();
EXPECT_EQ(slice.as_string_view(), message);
read_done.Notify();
@ -72,7 +72,7 @@ TEST_F(WindowsEndpointTest, BasicCommunication) {
grpc_core::Notification write_done;
SliceBuffer write_buffer;
write_buffer.Append(Slice::FromCopiedString(message));
client.Write([&write_done](absl::Status status) { write_done.Notify(); },
client.Write([&write_done](absl::Status) { write_done.Notify(); },
&write_buffer, nullptr);
iocp.Work(5s, []() {});
// Cleanup
@ -115,7 +115,7 @@ TEST_F(WindowsEndpointTest, Conversation) {
// if exchange%2 == 0, client -> server
// if exchange%2 == 1, server -> client
// if exchange == messages.length, done
std::atomic<int> exchange{0};
std::atomic<size_t> exchange{0};
// Initiates a Write and corresponding Read on two endpoints.
void WriteAndQueueReader(WindowsEndpoint* writer, WindowsEndpoint* reader) {
@ -129,8 +129,8 @@ TEST_F(WindowsEndpointTest, Conversation) {
// Asserts that the received string matches, then queues the next Write/Read
// pair
void ReadCB(absl::Status status) {
ASSERT_EQ(read_buffer.Count(), 1);
void ReadCB(absl::Status) {
ASSERT_EQ(read_buffer.Count(), 1u);
ASSERT_EQ(read_buffer.TakeFirst().as_string_view(), messages[exchange]);
if (++exchange == messages.size()) {
done.Notify();

Loading…
Cancel
Save