Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1204)

Side by Side Diff: media/blink/multibuffer.cc

Issue 1165903002: Multi reader/writer cache/buffer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: lru unit tests Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/blink/multibuffer.h"
6
7 namespace media {
8
9 template<class T>
10 static int32_t ClosestPreviousEntry(const std::map<int32_t, T>& index,
11 int32_t pos) {
12 typename std::map<int32_t, T>::const_iterator i = index.upper_bound(pos);
13 if (i == index.begin()) {
14 return -1;
15 }
16 --i;
17 return i->first;
18 }
19
20 MultiBuffer::MultiBuffer(int32_t block_size_shift) :
21 size_(0),
22 max_size_(0),
23 block_size_shift_(block_size_shift) {
24 }
25 MultiBuffer::~MultiBuffer() {
26 writer_index_.DeleteWaiters();
27 }
28
29 void MultiBuffer::AddWriter(int32_t pos, Waiter* writer) {
30 writer_index_.WaitFor(pos, writer);
31 }
32
33 void MultiBuffer::RemoveWriter(int32_t pos, Waiter* writer) {
34 writer_index_.StopWaitFor(pos, writer);
35 }
36
37
38 void MultiBuffer::WaitFor(int32_t pos, Waiter* reader) {
39 DCHECK(pinned_[pos]);
40 DCHECK(data_.find(pos) == data_.end());
41 reader_index_.WaitFor(pos, reader);
42
43 int32_t closest_writer = ClosestPreviousEntry(writer_index_.map(), pos);
44 int32_t closest_block = ClosestPreviousEntry(data_, pos);
45 if (closest_block >= closest_writer) {
46 closest_writer = -1;
47 }
48 if (closest_writer != -1 &&
49 pos - closest_writer < kMaxWaitForWriterOffset) {
50 // There is already a write available, wake it up.
51 writer_index_.WakeUp(closest_writer);
52 } else {
53 StartWriter(pos);
54 }
55 }
56
57 void MultiBuffer::DeferredWaitFor(int32_t pos, Waiter* reader) {
58 DCHECK(pinned_[pos]);
59 DCHECK(data_.find(pos) == data_.end());
60 deferred_reader_index_.WaitFor(pos, reader);
61 }
62
63 void MultiBuffer::StopWaitFor(int32_t pos, Waiter* reader) {
64 reader_index_.StopWaitFor(pos, reader);
65 deferred_reader_index_.StopWaitFor(pos, reader);
66 }
67
68 bool MultiBuffer::Contains(int32_t pos) const {
69 return data_.find(pos) != data_.end();
70 }
71
72 bool MultiBuffer::WantNow(int32_t pos) const {
73 return reader_index_.map().find(pos) != reader_index_.map().end();
74 }
75
76 bool MultiBuffer::WantSoon(int32_t pos) const {
77 return deferred_reader_index_.map().find(pos) !=
78 deferred_reader_index_.map().end();
79 }
80
81 void MultiBuffer::Prune() {
82 int count = 0;
83 while (size_ > max_size_ && !lru_.Empty()) {
84 int32_t to_free = lru_.Pop();
85 DCHECK(data_[to_free]);
86 DCHECK_EQ(pinned_[to_free], 0);
87 data_.erase(to_free);
88 size_--;
89 if (++count > kMaxFreesPerAdd) break;
90 }
91 }
92
93 void MultiBuffer::AddData(int32_t pos, scoped_refptr<DataBuffer> data) {
94 if (data_[pos]) {
95 // We already have this data.
96 return;
97 }
98 data_[pos] = data;
99 if (!pinned_[pos]) {
100 lru_.Insert(pos);
101 }
102 size_++;
103 Prune();
104 reader_index_.WakeUp(pos);
105 }
106
107 void MultiBuffer::PinRange(int32_t from, int32_t to, int32_t howmuch) {
108 pinned_.IncrementRange(from, to, howmuch);
109
110 // Iterate over all the modified ranges and check if
111 // any of them have transitioned in or out of the
112 // unlocked state. If so, we iterate over all buffers
113 // in that range and add/remove them from the LRU as
114 // approperiate. We iterate *backwards* through the
115 // ranges, with the idea that data in a continous range
116 // should be freed from the end first.
117 RangeMap<int32_t, int32_t>::MapType::const_iterator i;
118 i = pinned_.map().lower_bound(to);
119 while (i != pinned_.map().begin()) {
120 if (i->first < from) break;
121 RangeMap<int32_t, int32_t>::MapType::const_iterator j = i;
122 i--;
123 if (i->second == 0 || i->second == howmuch) {
124 bool pin = i->second == howmuch;
125 int32_t begin = std::max(i->first, from);
126 int32_t end = j == pinned_.map().end() ? to : std::min(to, j->first);
127 DataMap::iterator k = data_.lower_bound(end);
128 while (k != data_.begin()) {
129 k--;
130 if (k->first < begin) break;
131 if (pin) {
132 lru_.Remove(k->first);
133 } else {
134 lru_.Insert(k->first);
135 }
136 }
137 }
138 }
139 }
140
141 void MultiBuffer::IncrementMaxSize(int32_t size) {
142 max_size_ += size;
143 DCHECK_GE(max_size_, 0);
144 // Pruning only happens when blocks are added.
145 }
146
147 // MultiBufferWriter
148
149 MultiBufferWriter::MultiBufferWriter(MultiBuffer* multibuffer, int32_t pos) :
150 multibuffer_(multibuffer),
151 pos_(pos) {
152 multibuffer_->AddWriter(pos_, this);
153 }
154
155 MultiBufferWriter::~MultiBufferWriter() {
156 multibuffer_->RemoveWriter(pos_, this);
157 }
158
159 void MultiBufferWriter::Write(scoped_refptr<DataBuffer> data) {
160 multibuffer_->RemoveWriter(pos_, this);
161 if (multibuffer_->Contains(pos_)) {
162 delete this;
163 return;
164 }
165 multibuffer_->AddData(pos_, data);
166 pos_++;
167 multibuffer_->AddWriter(pos_, this);
liberato (no reviews please) 2015/06/09 15:00:53 it seems like there are many calls to AddWriter()
hubbe 2015/06/09 21:23:48 They seem evenly matched to me...
liberato (no reviews please) 2015/06/09 21:50:47 Indeed, you're right. i missed the first line of
168 if (!multibuffer_->Contains(pos_)) {
169 if (multibuffer_->WantNow(pos_)) {
170 return;
171 }
172 if (multibuffer_->WantSoon(pos_)) {
173 SetDeferred(true);
174 return;
175 }
176 }
177 delete this;
liberato (no reviews please) 2015/06/09 15:00:53 who calls Write()? how would they know that it ch
hubbe 2015/06/09 21:23:48 Updated comments in header file, hope it explains.
178 }
179
180 void MultiBufferWriter::Continue() {
181 SetDeferred(false);
182 }
183
184
185 MultiBufferReader::MultiBufferReader(MultiBuffer* multibuffer,
186 int64_t start,
187 int64_t end,
188 int64_t preload,
189 int64_t max_buffer_forward,
190 int64_t max_buffer_backward) :
191 multibuffer_(multibuffer),
192 pos_(start),
193 end_(end),
194 preload_(preload),
195 max_buffer_forward_(max_buffer_forward),
196 max_buffer_backward_(max_buffer_backward) {
197 multibuffer_->IncrementMaxSize(
198 block_ciel(max_buffer_forward_ + max_buffer_backward_));
199 multibuffer_->PinRange(
200 block(std::max<int64_t>(0, pos_ - max_buffer_backward_)),
201 block_ciel(std::min(pos_ + max_buffer_forward_, end_)),
202 1);
203 IncrementPreloadPos();
204 }
205
206 MultiBufferReader::~MultiBufferReader() {
207 multibuffer_->StopWaitFor(preload_pos_, this);
208 multibuffer_->IncrementMaxSize(
209 -block_ciel(max_buffer_forward_ + max_buffer_backward_));
210 multibuffer_->PinRange(
211 block(std::max<int64_t>(0, pos_ - max_buffer_backward_)),
212 block_ciel(std::min(pos_ + max_buffer_forward_, end_)),
213 -1);
214 }
215
216 int32_t MultiBufferReader::block(int64_t byte_pos) const {
217 return byte_pos << multibuffer_->block_size_shift();
218 }
219
220 int32_t MultiBufferReader::block_ciel(int64_t byte_pos) const {
221 return block(byte_pos + (1 << multibuffer_->block_size_shift()) - 1);
222 }
223
224 void MultiBufferReader::Seek(int64_t pos) {
225 DCHECK(cb_.is_null());
226 RangeMap<int32_t, int32_t> tmp;
227 tmp.IncrementRange(block(std::max<int64_t>(0, pos_ - max_buffer_backward_)),
228 block_ciel(std::min(pos_ + max_buffer_forward_, end_)),
229 -1);
230
231 // If we're seeking to somewhere between pos_ and
232 // preload_pos_, then we don't need to reset preload_pos_.
233 if (pos < pos_ || block(pos) >= preload_pos_) {
234 multibuffer_->StopWaitFor(preload_pos_, this);
235 preload_pos_ = pos_;
236 }
237 pos_ = pos;
238 IncrementPreloadPos();
239
240 tmp.IncrementRange(block(std::max<int64_t>(0, pos_ - max_buffer_backward_)),
241 block_ciel(std::min(pos_ + max_buffer_forward_, end_)),
liberato (no reviews please) 2015/06/09 15:00:53 nit: s/ciel/ceil/
hubbe 2015/06/09 21:23:48 Done.
242 1);
243 RangeMap<int32_t, int32_t>::MapType::const_iterator i, j;
244 for (i = tmp.map().begin(); i != tmp.map().end(); ++i) {
245 if (i->second != 0) {
246 j = i;
247 ++j;
248 multibuffer_->PinRange(i->first, j->first, i->second);
249 }
250 }
251 }
252
253 int64_t MultiBufferReader::Available() const {
254 uint64_t preload_byte_pos =
255 preload_pos_ << multibuffer_->block_size_shift();
256 return preload_byte_pos - pos_;
257 }
258
259 bool MultiBufferReader::TryRead(unsigned char *data, int64_t len) {
260 if (Available() < len) {
261 return false;
262 }
263 const MultiBuffer::DataMap& data_map = multibuffer_->map();
264 MultiBuffer::DataMap::const_iterator i = data_map.find(block(pos_));
265 DCHECK(i != data_map.end());
266 int64_t p = pos_;
267 while (len) {
268 DCHECK_EQ(i->first, block(pos_));
269 size_t offset = p & ((1 << multibuffer_->block_size_shift()) - 1);
270 size_t tocopy = i->second->data_size() - offset;
271 memcpy(data, i->second->data() + offset, tocopy);
272 data += tocopy;
273 len -= tocopy;
274 p += tocopy;
275 ++i;
liberato (no reviews please) 2015/06/09 15:00:53 i'm not sure that this doesn't sometimes walk righ
hubbe 2015/06/09 21:23:48 Hmm, why are you not sure? What can I do to make y
liberato (no reviews please) 2015/06/09 21:50:47 i'm not sure. :) the available() test is suppose
hubbe 2015/06/09 23:14:12 What you're describing should never happen because
276 }
277 Seek(p);
278 return true;
279 }
280
281 void MultiBufferReader::Wait(int64_t len, base::Closure cb) {
282 DCHECK_LE(Available(), len);
283 DCHECK_LE(len, max_buffer_forward_);
284 current_wait_size_ = len;
285 cb_ = cb;
286 IncrementPreloadPos();
287 }
288
289 void MultiBufferReader::Continue() {
290 IncrementPreloadPos();
291 }
292
293 void MultiBufferReader::CheckWait() {
294 if (!cb_.is_null() && Available() >= current_wait_size_) {
295 base::Closure cb;
296 std::swap(cb_, cb);
297 current_wait_size_ = 0;
298 cb.Run();
299 }
300 }
301
302 void MultiBufferReader::IncrementPreloadPos() {
303 int64_t max_preload = block_ciel(
304 std::min(end_, pos_ + std::max(preload_, current_wait_size_)));
305
306 if (preload_pos_ < max_preload) {
307 MultiBuffer::DataMap::const_iterator i =
308 multibuffer_->map().find(preload_pos_);
309 if (i != multibuffer_->map().end()) {
310 multibuffer_->StopWaitFor(preload_pos_, this);
311 preload_pos_++;
312 ++i;
313 while (preload_pos_ < max_preload &&
314 i != multibuffer_->map().end() &&
315 i->first == preload_pos_) {
316 preload_pos_++;
317 ++i;
318 }
319 }
320 }
321 if (preload_pos_ < max_preload) {
322 multibuffer_->WaitFor(preload_pos_, this);
323 } else if (preload_pos_ < end_) {
324 multibuffer_->DeferredWaitFor(preload_pos_, this);
325 }
326 CheckWait();
327 }
328
329 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698