1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
// 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_
|