blob: 9cd6e18cf5c3381a75617def780e4a56c3c07d93 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_DATA_PIPE_TEE_H_
#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_DATA_PIPE_TEE_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/data_pipe_producer.h"
#include "mojo/public/cpp/system/simple_watcher.h"
namespace content {
// `PrefetchDataPipeTee` duplicates the `source_` data pipe into multiple cloned
// data pipes.
// `PrefetchDataPipeTee` is kept alive by the second part of `ProducerPair`
// until all cloned data pipes are closed.
//
// To limit the buffer size and fallback gracefully for large data, the number
// of cloned data pipes is limited:
// - Up to 1 while the `source_` data pipe is being read, and
// (not counting closed pipes)
// - Up to 1 once the data size exceeds the limit
// (counting closed pipes after that)
// See the comment at `State` below for details.
//
// This limitation should be OK for the purpose of Unified Prefetch Cache Phase
// 1, because
// - the first cloned data pipe is for prerendering,
// - the second cloned data pipe is for main navigation, and
// - at the time of the main navigation starts, the first cloned data pipe is
// already closed.
class CONTENT_EXPORT PrefetchDataPipeTee final
: public base::RefCounted<PrefetchDataPipeTee> {
public:
explicit PrefetchDataPipeTee(mojo::ScopedDataPipeConsumerHandle source,
size_t buffer_limit);
PrefetchDataPipeTee(const PrefetchDataPipeTee&) = delete;
PrefetchDataPipeTee& operator=(const PrefetchDataPipeTee&) = delete;
// Returns a cloned data pipe, or a null handle when failed.
mojo::ScopedDataPipeConsumerHandle Clone();
// Public for unit tests.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange(PrefetchDataPipeTee_State)
enum class State {
// Reading data from `source_`, and adding the data to `buffer_`.
// `buffer_` represents the whole data read from `source_` so far.
// The number of active cloned data pipes (not counting already closed ones)
// is limited to up to 1 (which is `target_`), and if `target_` is not null,
// the data is also written to `target_`.
kLoading = 0,
// The data size read from `source_` exceeded the limit, and there is no
// target.
// `buffer_` represents the whole data read from `source_` so far.
//
// Reading data from `source_` is blocked, and when a new target is added by
// `Clone()`, `buffer_` is written to the new target and cleared, and
// transition to `kSizeExceeded`.
kSizeExceededNoTarget = 1,
// The data size read from `source_` exceeded the limit.
// Reading data from `source_` might or might not be completed.
// `buffer_` just stores the current chunk read from `source_` while writing
// to a target, and the early parts of the data from `source_` are already
// discarded.
//
// Data read from `source_` is written to a target (`target_`, if any) in a
// streaming fashion (i.e. the current chunk is tentatively stored in
// `buffer_` but not accumulated).
// If there is no target anymore, then the data can be just discarded (we
// can't transition to `kSizeExceededNoTarget` because th data from
// `source_` is already discarded).
kSizeExceeded = 2,
// Reading data from `source_` is completed and the data is fully stored in
// `buffer_` without reaching the buffer limit.
// `target_` is null.
// Any number of cloned data pipes can be created.
kLoaded = 3,
kMaxValue = kLoaded,
};
// LINT.ThenChange(//tools/metrics/histograms/enums.xml:PrefetchDataPipeTeeState)
private:
friend class base::RefCounted<PrefetchDataPipeTee>;
~PrefetchDataPipeTee();
// Represents a cloned output data pipe:
// - `mojo::DataPipeProducer` holds the data pipe, and
// - `scoped_refptr<PrefetchDataPipeTee>` keeps `PrefetchDataPipeTee` alive as
// long as the output data pipe is still active, and should point to `this`
// unless the `mojo::DataPipeProducer` is null.
using ProducerPair = std::pair<std::unique_ptr<mojo::DataPipeProducer>,
scoped_refptr<PrefetchDataPipeTee>>;
void StartSourceWatcher();
void OnReadable(MojoResult result, const mojo::HandleSignalsState& state);
// Set a new target, and returns the old target.
ProducerPair ResetTarget(ProducerPair target);
void OnWriteDataPipeClosed(MojoResult result,
const mojo::HandleSignalsState& state);
// Writes data to `target`. This blocks reading data from `source_` until its
// completion (i.e. `OnDataWritten()` is called).
void WriteData(ProducerPair target, base::span<const char> data);
void OnDataWritten(ProducerPair target, MojoResult result);
State state_ = State::kLoading;
// Number of cloned data pipes waiting for `OnDataWritten()`.
uint32_t pending_writes_ = 0;
// The data pipe to be read, and its watcher.
mojo::ScopedDataPipeConsumerHandle source_;
mojo::SimpleWatcher source_watcher_;
std::string buffer_;
// `buffer_.size()` is limited up to `buffer_limit_`.
const size_t buffer_limit_;
// The cloned data pipe while `state_` is `kLoading` or `kSizeExceeded`.
// In these states, the number of cloned data pipes is at most 1.
// Should be modified only by `ResetTarget()`.
ProducerPair target_;
mojo::SimpleWatcher target_watcher_;
// How many times `Clone()` is called.
int count_clone_called_ = 0;
base::WeakPtrFactory<PrefetchDataPipeTee> weak_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_DATA_PIPE_TEE_H_