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
content / browser / preloading / prefetch / prefetch_data_pipe_tee.h [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 "base/metrics/histogram_functions.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_