blob: ef18a2722edfc260ce9160396b6f46d1d1ef07e3 [file]
// 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.
#include "remoting/base/ipc_fifo_buffer.h"
#include <limits>
#include "base/check.h"
#include "base/containers/span.h"
#include "base/logging.h"
namespace remoting {
// =============================================================================
// IpcFifoBufferWriter
// =============================================================================
IpcFifoBufferWriter::IpcFifoBufferWriter(
mojo::ScopedDataPipeProducerHandle producer_handle)
: producer_handle_(std::move(producer_handle)) {
CHECK(producer_handle_.is_valid());
DETACH_FROM_SEQUENCE(sequence_checker_);
}
IpcFifoBufferWriter::~IpcFifoBufferWriter() = default;
mojo::ScopedDataPipeProducerHandle IpcFifoBufferWriter::TakeProducerHandle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::move(producer_handle_);
}
FifoBufferWriter::Result IpcFifoBufferWriter::Write(
base::span<const uint8_t> data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (data.empty()) {
return FifoBufferWriter::Result::kSuccess;
}
size_t bytes_written = 0;
MojoResult result = producer_handle_->WriteData(
data, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE, bytes_written);
if (result == MOJO_RESULT_OK) {
return FifoBufferWriter::Result::kSuccess;
}
if (result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_OUT_OF_RANGE) {
return FifoBufferWriter::Result::kFull;
}
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
// Peer closed. Fail gracefully.
return FifoBufferWriter::Result::kFailed;
}
return FifoBufferWriter::Result::kFailed;
}
// =============================================================================
// IpcFifoBufferReader
// =============================================================================
IpcFifoBufferReader::IpcFifoBufferReader(
mojo::ScopedDataPipeConsumerHandle consumer_handle)
: consumer_handle_(std::move(consumer_handle)) {
CHECK(consumer_handle_.is_valid());
DETACH_FROM_SEQUENCE(sequence_checker_);
}
IpcFifoBufferReader::~IpcFifoBufferReader() = default;
mojo::ScopedDataPipeConsumerHandle IpcFifoBufferReader::TakeConsumerHandle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::move(consumer_handle_);
}
std::optional<size_t> IpcFifoBufferReader::Read(
base::span<uint8_t> destination) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (destination.empty()) {
return 0;
}
size_t bytes_read = 0;
MojoResult result = consumer_handle_->ReadData(MOJO_READ_DATA_FLAG_NONE,
destination, bytes_read);
if (result == MOJO_RESULT_OK) {
return bytes_read;
}
if (result == MOJO_RESULT_SHOULD_WAIT) {
return 0;
}
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
// Peer closed. Fail gracefully.
return std::nullopt;
}
LOG(ERROR) << "Failed to read from Mojo Data Pipe: " << result;
return std::nullopt;
}
std::optional<size_t> IpcFifoBufferReader::Skip(size_t bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (bytes == 0) {
return 0;
}
size_t bytes_discarded = 0;
MojoResult result = consumer_handle_->DiscardData(bytes, bytes_discarded);
if (result == MOJO_RESULT_OK) {
return bytes_discarded;
}
if (result == MOJO_RESULT_SHOULD_WAIT) {
return 0;
}
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
// Peer closed. Fail gracefully.
return std::nullopt;
}
LOG(ERROR) << "Failed to discard data from Mojo Data Pipe: " << result;
return std::nullopt;
}
void IpcFifoBufferReader::Clear() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[[maybe_unused]] size_t discarded = 0;
consumer_handle_->DiscardData(std::numeric_limits<size_t>::max(), discarded);
}
std::optional<size_t> IpcFifoBufferReader::GetBufferedBytes() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
size_t bytes_available = 0;
MojoResult result = consumer_handle_->ReadData(
MOJO_READ_DATA_FLAG_QUERY, base::span<uint8_t>(), bytes_available);
if (result == MOJO_RESULT_OK) {
if (bytes_available > 0) {
return bytes_available;
}
// If 0 bytes are available, the pipe might be empty or closed.
MojoHandleSignalsState signals = consumer_handle_->QuerySignalsState();
if (signals.satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED) {
return std::nullopt;
}
return 0;
}
if (result == MOJO_RESULT_SHOULD_WAIT) {
// Pipe is empty.
return 0;
}
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
// Peer closed.
return std::nullopt;
}
LOG(ERROR) << "Failed to query Mojo Data Pipe: " << result;
return std::nullopt;
}
bool CreateIpcFifoBuffer(size_t capacity,
std::unique_ptr<IpcFifoBufferWriter>& writer,
std::unique_ptr<IpcFifoBufferReader>& reader) {
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
MojoResult result = mojo::CreateDataPipe(capacity, producer, consumer);
if (result != MOJO_RESULT_OK) {
LOG(ERROR) << "Failed to create Mojo Data Pipe: " << result;
return false;
}
writer = std::make_unique<IpcFifoBufferWriter>(std::move(producer));
reader = std::make_unique<IpcFifoBufferReader>(std::move(consumer));
return true;
}
} // namespace remoting