blob: 5c288a778f0dd0f0f982a977aa7ebe288d8791be [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_SOCKET_TCP_SOCKET_IO_COMPLETION_PORT_WIN_H_
#define NET_SOCKET_TCP_SOCKET_IO_COMPLETION_PORT_WIN_H_
#include <memory>
#include "base/win/windows_types.h"
#include "net/base/net_export.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/tcp_socket_win.h"
namespace net {
class NetLog;
struct NetLogSource;
class SocketPerformanceWatcher;
// An implementation of TCPSocketWin which uses an IO completion port to be
// notified of completed reads and writes. The goal is to avoid the PostTask
// overhead associated with the use of base::ObjectWatcher in
// TCPSocketDefaultWin.
class NET_EXPORT TcpSocketIoCompletionPortWin : public TCPSocketWin {
public:
// Disables usage of FILE_SKIP_COMPLETION_PORT_ON_SUCCESS in a scope. This
// only affect sockets on which `Read()` or `Write()` hasn't been called yet.
class NET_EXPORT DisableSkipCompletionPortOnSuccessForTesting {
public:
DisableSkipCompletionPortOnSuccessForTesting();
~DisableSkipCompletionPortOnSuccessForTesting();
DisableSkipCompletionPortOnSuccessForTesting(
const DisableSkipCompletionPortOnSuccessForTesting&) = delete;
DisableSkipCompletionPortOnSuccessForTesting& operator=(
const DisableSkipCompletionPortOnSuccessForTesting&) = delete;
};
TcpSocketIoCompletionPortWin(
std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
NetLog* net_log,
const NetLogSource& source);
TcpSocketIoCompletionPortWin(
std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
NetLogWithSource net_log_source);
~TcpSocketIoCompletionPortWin() override;
// TCPSocketWin:
int Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) override;
int ReadIfReady(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) override;
int CancelReadIfReady() override;
int Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) override;
protected:
// TCPSocketWin:
scoped_refptr<Core> CreateCore() override;
bool HasPendingRead() const override;
void OnClosed() override;
private:
class CoreImpl;
// Attempts to initialize overlapped IO for `socket_`, if not already
// initialized. This entails:
// - Registering `core_` as an IO handler.
// - Attempting to activate `FILE_SKIP_COMPLETION_PORT_ON_SUCCESS`.
// Returns true on success.
bool EnsureOverlappedIOInitialized();
// Handles a completed read/write operation on `socket_`. `bytes_transferred`
// is the number of bytes actually read/written. `error` is the error code for
// the operation. `buffer` is the buffer used to initiate the read/write
// operation and `buffer_length` is its size (`bytes_transferred` should be <=
// `buffer_length`). Returns the value to pass to the completion callback
// associated with the operation (<0 is an error code, >=0 is a number of
// bytes transferred).
int DidCompleteRead(DWORD bytes_transferred,
DWORD error,
scoped_refptr<IOBuffer> buffer,
int buffer_length);
int DidCompleteWrite(DWORD bytes_transferred,
DWORD error,
scoped_refptr<IOBuffer> buffer,
int buffer_length);
// Handles a read request for the TCP socket. This function is used by both
// Read() and ReadIfReady() to perform a read operation. The behavior of the
// function varies based on the `allow_zero_byte_overlapped_read` parameter:
//
// - If allow_zero_byte_overlapped_read is true (called from ReadIfReady):
// 1. Attempts to perform a non-overlapped read using WSARecv.
// 2. If the operation returns WSAEWOULDBLOCK (indicating no data is
// available), issues a zero-byte overlapped read to wait for incoming
// data. This is signaled via the completion routine when data becomes
// available, allowing the caller to issue another ReadIfReady() call
// to retrieve the data.
//
// - If allow_zero_byte_overlapped_read is false (called from Read):
// 1. Directly performs an overlapped read with the caller's buffer, using
// WSARecv.
// 2. If the operation completes immediately, the data is copied to the
// caller's buffer by the kernel, and the result is returned.
// 3. If the operation is pending (WSA_IO_PENDING), the read is completed
// asynchronously, and the completion routine is invoked when the data is
// available. The caller's buffer is held until the operation completes.
//
// The function ensures compatibility with both Read() and ReadIfReady() by:
//
// - Allowing the OVERLAPPED structure to be passed conditionally.
// - Handling completion differently based on the caller's context.
// - Tracking pending operations using the `IOContext` structure in the
// CoreImpl.
//
// Parameters:
// - buffer: IOBuffer to store the read data.
// - buf_len: Length of the buffer.
// - callback: Callback to invoke upon completion of the read operation.
// - allow_zero_byte_overlapped_read: Determines whether zero-byte
// overlapped reads are allowed (true for ReadIfReady, false for Read).
//
// Returns:
// - The number of bytes read if the operation completes immediately.
// - ERR_IO_PENDING if the operation is pending and will complete
// asynchronously.
// - A network error code if the read operation fails immediately.
int HandleReadRequest(IOBuffer* buffer,
int buf_len,
CompletionOnceCallback callback,
bool allow_zero_byte_overlapped_read);
CoreImpl& GetCoreImpl();
// Number of read operations waiting for an I/O completion packet.
int num_pending_reads_ = 0;
// Whether queuing a completion packet is skipped when an operation on
// `socket_` succeeds immediately.
bool skip_completion_port_on_success_ = false;
};
} // namespace net
#endif // NET_SOCKET_TCP_SOCKET_IO_COMPLETION_PORT_WIN_H_