blob: bb696450223f3f5aab683c52392083eef29b0666 [file] [edit]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef QUICHE_QUIC_QBONE_BONNET_ASYNC_WRITE_PACKET_EXCHANGER_H_
#define QUICHE_QUIC_QBONE_BONNET_ASYNC_WRITE_PACKET_EXCHANGER_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <queue>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "quiche/quic/qbone/bonnet/qbone_client_packet_exchanger.h"
#include "quiche/quic/qbone/bonnet/tun_device_packet_exchanger.h"
#include "quiche/quic/qbone/platform/kernel_interface.h"
#include "quiche/quic/qbone/platform/netlink_interface.h"
#include "quiche/common/quiche_callbacks.h"
namespace quic {
// Packet exchanger that handles write operations asynchronously on a separate
// worker thread.
class AsyncWritePacketExchanger : public QboneClientPacketExchanger {
public:
// Executor that runs given callbacks on the main silo thread (the thread that
// owns and interacts with the exchanger).
class SiloExecutor {
public:
virtual ~SiloExecutor() = default;
// Stops the executor and either blocks until all pending callbacks are
// completed or cancels them. No callbacks will be executed after this
// returns. Must be called from the silo thread.
virtual void Stop() = 0;
// Return true iff called from the silo thread. Thread-safe.
virtual bool IsOnSiloThread() const = 0;
// Schedules the given callback to be executed on the silo thread. No
// guarantees about when the callback will be executed or the order in
// which it will be executed relative to other callbacks, but all callbacks
// will eventually be executed if Stop() is not called. Thread-safe.
virtual void ExecuteInSilo(quiche::SingleUseCallback<void()> callback) = 0;
};
AsyncWritePacketExchanger(
size_t mtu,
KernelInterface* absl_nonnull kernel ABSL_ATTRIBUTE_LIFETIME_BOUND,
NetlinkInterface* absl_nonnull netlink ABSL_ATTRIBUTE_LIFETIME_BOUND,
Visitor* absl_nonnull visitor ABSL_ATTRIBUTE_LIFETIME_BOUND, bool is_tap,
absl::string_view ifname, int64_t max_buffer_size_bytes,
int64_t max_result_buffer_size_bytes,
absl_nonnull std::unique_ptr<SiloExecutor> silo_executor);
~AsyncWritePacketExchanger() override;
// QboneClientPacketExchanger:
void Start(int read_fd, int write_fd,
QboneClientPacketExchanger* absl_nullable exchanger) override;
void Stop() override;
int OnReadFromNetworkReady(int max_packets_to_read) override;
void WritePacketToNetwork(absl::Span<const std::byte> packet) override;
private:
class WriteThread;
class IntermediateVisitor;
struct InternalWriteResult {
std::vector<std::byte> packet;
absl::Duration latency;
};
void OnWriteResultsReadyForSiloThread();
const int64_t max_buffer_size_bytes_;
const int64_t max_result_buffer_size_bytes_;
absl_nonnull std::unique_ptr<SiloExecutor> silo_executor_;
Visitor& visitor_;
std::unique_ptr<IntermediateVisitor> intermediate_visitor_;
TunDevicePacketExchanger thread_local_exchanger_;
absl::Mutex write_queue_mutex_;
int64_t write_queue_size_ ABSL_GUARDED_BY(write_queue_mutex_) = 0;
std::queue<std::vector<std::byte>> write_queue_
ABSL_GUARDED_BY(write_queue_mutex_);
absl::Mutex result_queue_mutex_;
int64_t result_queue_size_ ABSL_GUARDED_BY(result_queue_mutex_) = 0;
std::queue<absl::StatusOr<InternalWriteResult>> result_queue_
ABSL_GUARDED_BY(result_queue_mutex_);
std::unique_ptr<WriteThread> write_thread_;
};
} // namespace quic
#endif // QUICHE_QUIC_QBONE_BONNET_ASYNC_WRITE_PACKET_EXCHANGER_H_