blob: 381ae235babe682b3fca3d30dc502d21b30f941b [file] [log] [blame]
[email protected]b03507862012-05-23 17:11:501// Copyright (c) 2012 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 "content/browser/download/byte_stream.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/memory/weak_ptr.h"
10#include "base/memory/ref_counted.h"
11#include "base/sequenced_task_runner.h"
12
13namespace {
14
15typedef std::deque<std::pair<scoped_refptr<net::IOBuffer>, size_t> >
16ContentVector;
17
18// The fraction of the buffer that must be ready to send on the input
19// before we ship data to the output.
20static const int kFractionBufferBeforeSending = 3;
21
22// The fraction of the buffer that must have been consumed on the output
23// before we update the input window.
24static const int kFractionReadBeforeWindowUpdate = 3;
25
[email protected]d7db4f622012-06-04 18:20:5626class ByteStreamReaderImpl;
[email protected]b03507862012-05-23 17:11:5027
28// A poor man's weak pointer; a RefCountedThreadSafe boolean that can be
29// cleared in an object destructor and accessed to check for object
30// existence. We can't use weak pointers because they're tightly tied to
31// threads rather than task runners.
32// TODO(rdsmith): A better solution would be extending weak pointers
33// to support SequencedTaskRunners.
34struct LifetimeFlag : public base::RefCountedThreadSafe<LifetimeFlag> {
35 public:
36 LifetimeFlag() : is_alive_(true) { }
37 bool is_alive_;
38
39 protected:
40 friend class base::RefCountedThreadSafe<LifetimeFlag>;
41 virtual ~LifetimeFlag() { }
42
43 private:
44 DISALLOW_COPY_AND_ASSIGN(LifetimeFlag);
45};
46
[email protected]d7db4f622012-06-04 18:20:5647// For both ByteStreamWriterImpl and ByteStreamReaderImpl, Construction and
[email protected]b03507862012-05-23 17:11:5048// SetPeer may happen anywhere; all other operations on each class must
49// happen in the context of their SequencedTaskRunner.
[email protected]d7db4f622012-06-04 18:20:5650class ByteStreamWriterImpl : public content::ByteStreamWriter {
[email protected]b03507862012-05-23 17:11:5051 public:
[email protected]d7db4f622012-06-04 18:20:5652 ByteStreamWriterImpl(scoped_refptr<base::SequencedTaskRunner> task_runner,
[email protected]b03507862012-05-23 17:11:5053 scoped_refptr<LifetimeFlag> lifetime_flag,
54 size_t buffer_size);
[email protected]d7db4f622012-06-04 18:20:5655 virtual ~ByteStreamWriterImpl();
[email protected]b03507862012-05-23 17:11:5056
57 // Must be called before any operations are performed.
[email protected]d7db4f622012-06-04 18:20:5658 void SetPeer(ByteStreamReaderImpl* peer,
[email protected]b03507862012-05-23 17:11:5059 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
60 scoped_refptr<LifetimeFlag> peer_lifetime_flag);
61
[email protected]d7db4f622012-06-04 18:20:5662 // Overridden from ByteStreamWriter.
[email protected]b03507862012-05-23 17:11:5063 virtual bool Write(scoped_refptr<net::IOBuffer> buffer,
64 size_t byte_count) OVERRIDE;
65 virtual void Close(content::DownloadInterruptReason status) OVERRIDE;
66 virtual void RegisterCallback(const base::Closure& source_callback) OVERRIDE;
67
[email protected]d7db4f622012-06-04 18:20:5668 // PostTask target from |ByteStreamReaderImpl::MaybeUpdateInput|.
[email protected]b03507862012-05-23 17:11:5069 static void UpdateWindow(scoped_refptr<LifetimeFlag> lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:5670 ByteStreamWriterImpl* target,
[email protected]b03507862012-05-23 17:11:5071 size_t bytes_consumed);
72
73 private:
74 // Called from UpdateWindow when object existence has been validated.
75 void UpdateWindowInternal(size_t bytes_consumed);
76
77 void PostToPeer(bool complete, content::DownloadInterruptReason status);
78
79 const size_t total_buffer_size_;
80
81 // All data objects in this class are only valid to access on
82 // this task runner except as otherwise noted.
83 scoped_refptr<base::SequencedTaskRunner> my_task_runner_;
84
85 // True while this object is alive.
86 scoped_refptr<LifetimeFlag> my_lifetime_flag_;
87
88 base::Closure space_available_callback_;
89 ContentVector input_contents_;
90 size_t input_contents_size_;
91
92 // ** Peer information.
93
94 scoped_refptr<base::SequencedTaskRunner> peer_task_runner_;
95
96 // How much we've sent to the output that for flow control purposes we
97 // must assume hasn't been read yet.
98 size_t output_size_used_;
99
100 // Only valid to access on peer_task_runner_.
101 scoped_refptr<LifetimeFlag> peer_lifetime_flag_;
102
103 // Only valid to access on peer_task_runner_ if
104 // |*peer_lifetime_flag_ == true|
[email protected]d7db4f622012-06-04 18:20:56105 ByteStreamReaderImpl* peer_;
[email protected]b03507862012-05-23 17:11:50106};
107
[email protected]d7db4f622012-06-04 18:20:56108class ByteStreamReaderImpl : public content::ByteStreamReader {
[email protected]b03507862012-05-23 17:11:50109 public:
[email protected]d7db4f622012-06-04 18:20:56110 ByteStreamReaderImpl(scoped_refptr<base::SequencedTaskRunner> task_runner,
[email protected]b03507862012-05-23 17:11:50111 scoped_refptr<LifetimeFlag> lifetime_flag,
112 size_t buffer_size);
[email protected]d7db4f622012-06-04 18:20:56113 virtual ~ByteStreamReaderImpl();
[email protected]b03507862012-05-23 17:11:50114
115 // Must be called before any operations are performed.
[email protected]d7db4f622012-06-04 18:20:56116 void SetPeer(ByteStreamWriterImpl* peer,
[email protected]b03507862012-05-23 17:11:50117 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
118 scoped_refptr<LifetimeFlag> peer_lifetime_flag);
119
[email protected]d7db4f622012-06-04 18:20:56120 // Overridden from ByteStreamReader.
[email protected]b03507862012-05-23 17:11:50121 virtual StreamState Read(scoped_refptr<net::IOBuffer>* data,
122 size_t* length) OVERRIDE;
123 virtual content::DownloadInterruptReason GetStatus() const OVERRIDE;
124 virtual void RegisterCallback(const base::Closure& sink_callback) OVERRIDE;
125
[email protected]d7db4f622012-06-04 18:20:56126 // PostTask target from |ByteStreamWriterImpl::MaybePostToPeer| and
127 // |ByteStreamWriterImpl::Close|.
[email protected]b03507862012-05-23 17:11:50128 // Receive data from our peer.
129 // static because it may be called after the object it is targeting
130 // has been destroyed. It may not access |*target|
131 // if |*object_lifetime_flag| is false.
132 static void TransferData(
133 scoped_refptr<LifetimeFlag> object_lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:56134 ByteStreamReaderImpl* target,
[email protected]b03507862012-05-23 17:11:50135 scoped_ptr<ContentVector> transfer_buffer,
136 size_t transfer_buffer_bytes,
137 bool source_complete,
138 content::DownloadInterruptReason status);
139
140 private:
141 // Called from TransferData once object existence has been validated.
142 void TransferDataInternal(
143 scoped_ptr<ContentVector> transfer_buffer,
144 size_t transfer_buffer_bytes,
145 bool source_complete,
146 content::DownloadInterruptReason status);
147
148 void MaybeUpdateInput();
149
150 const size_t total_buffer_size_;
151
152 scoped_refptr<base::SequencedTaskRunner> my_task_runner_;
153
154 // True while this object is alive.
155 scoped_refptr<LifetimeFlag> my_lifetime_flag_;
156
157 ContentVector available_contents_;
158
159 bool received_status_;
160 content::DownloadInterruptReason status_;
161
162 base::Closure data_available_callback_;
163
164 // Time of last point at which data in stream transitioned from full
165 // to non-full. Nulled when a callback is sent.
166 base::Time last_non_full_time_;
167
168 // ** Peer information
169
170 scoped_refptr<base::SequencedTaskRunner> peer_task_runner_;
171
172 // How much has been removed from this class that we haven't told
173 // the input about yet.
174 size_t unreported_consumed_bytes_;
175
176 // Only valid to access on peer_task_runner_.
177 scoped_refptr<LifetimeFlag> peer_lifetime_flag_;
178
179 // Only valid to access on peer_task_runner_ if
180 // |*peer_lifetime_flag_ == true|
[email protected]d7db4f622012-06-04 18:20:56181 ByteStreamWriterImpl* peer_;
[email protected]b03507862012-05-23 17:11:50182};
183
[email protected]d7db4f622012-06-04 18:20:56184ByteStreamWriterImpl::ByteStreamWriterImpl(
[email protected]b03507862012-05-23 17:11:50185 scoped_refptr<base::SequencedTaskRunner> task_runner,
186 scoped_refptr<LifetimeFlag> lifetime_flag,
187 size_t buffer_size)
188 : total_buffer_size_(buffer_size),
189 my_task_runner_(task_runner),
190 my_lifetime_flag_(lifetime_flag),
191 input_contents_size_(0),
192 output_size_used_(0),
193 peer_(NULL) {
194 DCHECK(my_lifetime_flag_.get());
195 my_lifetime_flag_->is_alive_ = true;
196}
197
[email protected]d7db4f622012-06-04 18:20:56198ByteStreamWriterImpl::~ByteStreamWriterImpl() {
[email protected]b03507862012-05-23 17:11:50199 my_lifetime_flag_->is_alive_ = false;
200}
201
[email protected]d7db4f622012-06-04 18:20:56202void ByteStreamWriterImpl::SetPeer(
203 ByteStreamReaderImpl* peer,
[email protected]b03507862012-05-23 17:11:50204 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
205 scoped_refptr<LifetimeFlag> peer_lifetime_flag) {
206 peer_ = peer;
207 peer_task_runner_ = peer_task_runner;
208 peer_lifetime_flag_ = peer_lifetime_flag;
209}
210
[email protected]d7db4f622012-06-04 18:20:56211bool ByteStreamWriterImpl::Write(
[email protected]b03507862012-05-23 17:11:50212 scoped_refptr<net::IOBuffer> buffer, size_t byte_count) {
213 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
214
215 input_contents_.push_back(std::make_pair(buffer, byte_count));
216 input_contents_size_ += byte_count;
217
218 // Arbitrarily, we buffer to a third of the total size before sending.
219 if (input_contents_size_ > total_buffer_size_ / kFractionBufferBeforeSending)
220 PostToPeer(false, content::DOWNLOAD_INTERRUPT_REASON_NONE);
221
222 return (input_contents_size_ + output_size_used_ <= total_buffer_size_);
223}
224
[email protected]d7db4f622012-06-04 18:20:56225void ByteStreamWriterImpl::Close(
[email protected]b03507862012-05-23 17:11:50226 content::DownloadInterruptReason status) {
227 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
228 PostToPeer(true, status);
229}
230
[email protected]d7db4f622012-06-04 18:20:56231void ByteStreamWriterImpl::RegisterCallback(
[email protected]b03507862012-05-23 17:11:50232 const base::Closure& source_callback) {
233 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
234 space_available_callback_ = source_callback;
235}
236
237// static
[email protected]d7db4f622012-06-04 18:20:56238void ByteStreamWriterImpl::UpdateWindow(
239 scoped_refptr<LifetimeFlag> lifetime_flag, ByteStreamWriterImpl* target,
[email protected]b03507862012-05-23 17:11:50240 size_t bytes_consumed) {
241 // If the target object isn't alive anymore, we do nothing.
242 if (!lifetime_flag->is_alive_) return;
243
244 target->UpdateWindowInternal(bytes_consumed);
245}
246
[email protected]d7db4f622012-06-04 18:20:56247void ByteStreamWriterImpl::UpdateWindowInternal(size_t bytes_consumed) {
[email protected]b03507862012-05-23 17:11:50248 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
249 DCHECK_GE(output_size_used_, bytes_consumed);
250 output_size_used_ -= bytes_consumed;
251
252 // Callback if we were above the limit and we're now <= to it.
253 size_t total_known_size_used =
254 input_contents_size_ + output_size_used_;
255
256 if (total_known_size_used <= total_buffer_size_ &&
257 (total_known_size_used + bytes_consumed > total_buffer_size_) &&
258 !space_available_callback_.is_null())
259 space_available_callback_.Run();
260}
261
[email protected]d7db4f622012-06-04 18:20:56262void ByteStreamWriterImpl::PostToPeer(
[email protected]b03507862012-05-23 17:11:50263 bool complete, content::DownloadInterruptReason status) {
264 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
265 // Valid contexts in which to call.
266 DCHECK(complete || 0 != input_contents_size_);
267
268 scoped_ptr<ContentVector> transfer_buffer(new ContentVector);
269 size_t buffer_size = 0;
270 if (0 != input_contents_size_) {
271 transfer_buffer.reset(new ContentVector);
272 transfer_buffer->swap(input_contents_);
273 buffer_size = input_contents_size_;
274 output_size_used_ += input_contents_size_;
275 input_contents_size_ = 0;
276 }
277 peer_task_runner_->PostTask(
278 FROM_HERE, base::Bind(
[email protected]d7db4f622012-06-04 18:20:56279 &ByteStreamReaderImpl::TransferData,
[email protected]b03507862012-05-23 17:11:50280 peer_lifetime_flag_,
281 peer_,
282 base::Passed(transfer_buffer.Pass()),
283 buffer_size,
284 complete,
285 status));
286}
287
[email protected]d7db4f622012-06-04 18:20:56288ByteStreamReaderImpl::ByteStreamReaderImpl(
[email protected]b03507862012-05-23 17:11:50289 scoped_refptr<base::SequencedTaskRunner> task_runner,
290 scoped_refptr<LifetimeFlag> lifetime_flag,
291 size_t buffer_size)
292 : total_buffer_size_(buffer_size),
293 my_task_runner_(task_runner),
294 my_lifetime_flag_(lifetime_flag),
295 received_status_(false),
296 status_(content::DOWNLOAD_INTERRUPT_REASON_NONE),
297 unreported_consumed_bytes_(0),
298 peer_(NULL) {
299 DCHECK(my_lifetime_flag_.get());
300 my_lifetime_flag_->is_alive_ = true;
301}
302
[email protected]d7db4f622012-06-04 18:20:56303ByteStreamReaderImpl::~ByteStreamReaderImpl() {
[email protected]b03507862012-05-23 17:11:50304 my_lifetime_flag_->is_alive_ = false;
305}
306
[email protected]d7db4f622012-06-04 18:20:56307void ByteStreamReaderImpl::SetPeer(
308 ByteStreamWriterImpl* peer,
[email protected]b03507862012-05-23 17:11:50309 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
310 scoped_refptr<LifetimeFlag> peer_lifetime_flag) {
311 peer_ = peer;
312 peer_task_runner_ = peer_task_runner;
313 peer_lifetime_flag_ = peer_lifetime_flag;
314}
315
[email protected]d7db4f622012-06-04 18:20:56316ByteStreamReaderImpl::StreamState
317ByteStreamReaderImpl::Read(scoped_refptr<net::IOBuffer>* data,
[email protected]b03507862012-05-23 17:11:50318 size_t* length) {
319 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
320
321 if (available_contents_.size()) {
322 *data = available_contents_.front().first;
323 *length = available_contents_.front().second;
324 available_contents_.pop_front();
325 unreported_consumed_bytes_ += *length;
326
327 MaybeUpdateInput();
328 return STREAM_HAS_DATA;
329 }
330 if (received_status_) {
331 return STREAM_COMPLETE;
332 }
333 return STREAM_EMPTY;
334}
335
336content::DownloadInterruptReason
[email protected]d7db4f622012-06-04 18:20:56337ByteStreamReaderImpl::GetStatus() const {
[email protected]b03507862012-05-23 17:11:50338 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
339 DCHECK(received_status_);
340 return status_;
341}
342
[email protected]d7db4f622012-06-04 18:20:56343void ByteStreamReaderImpl::RegisterCallback(
[email protected]b03507862012-05-23 17:11:50344 const base::Closure& sink_callback) {
345 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
346
347 data_available_callback_ = sink_callback;
348}
349
350// static
[email protected]d7db4f622012-06-04 18:20:56351void ByteStreamReaderImpl::TransferData(
[email protected]b03507862012-05-23 17:11:50352 scoped_refptr<LifetimeFlag> object_lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:56353 ByteStreamReaderImpl* target,
[email protected]b03507862012-05-23 17:11:50354 scoped_ptr<ContentVector> transfer_buffer,
355 size_t buffer_size,
356 bool source_complete,
357 content::DownloadInterruptReason status) {
358 // If our target is no longer alive, do nothing.
359 if (!object_lifetime_flag->is_alive_) return;
360
361 target->TransferDataInternal(
362 transfer_buffer.Pass(), buffer_size, source_complete, status);
363}
364
[email protected]d7db4f622012-06-04 18:20:56365void ByteStreamReaderImpl::TransferDataInternal(
[email protected]b03507862012-05-23 17:11:50366 scoped_ptr<ContentVector> transfer_buffer,
367 size_t buffer_size,
368 bool source_complete,
369 content::DownloadInterruptReason status) {
370 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
371
372 bool was_empty = available_contents_.empty();
373
374 if (transfer_buffer.get()) {
375 available_contents_.insert(available_contents_.end(),
376 transfer_buffer->begin(),
377 transfer_buffer->end());
378 }
379
380 if (source_complete) {
381 received_status_ = true;
382 status_ = status;
383 }
384
385 // Callback on transition from empty to non-empty, or
386 // source complete.
387 if (((was_empty && !available_contents_.empty()) ||
388 source_complete) &&
389 !data_available_callback_.is_null())
390 data_available_callback_.Run();
391}
392
393// Decide whether or not to send the input a window update.
394// Currently we do that whenever we've got unreported consumption
395// greater than 1/3 of total size.
[email protected]d7db4f622012-06-04 18:20:56396void ByteStreamReaderImpl::MaybeUpdateInput() {
[email protected]b03507862012-05-23 17:11:50397 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
398
399 if (unreported_consumed_bytes_ <=
400 total_buffer_size_ / kFractionReadBeforeWindowUpdate)
401 return;
402
403 peer_task_runner_->PostTask(
404 FROM_HERE, base::Bind(
[email protected]d7db4f622012-06-04 18:20:56405 &ByteStreamWriterImpl::UpdateWindow,
[email protected]b03507862012-05-23 17:11:50406 peer_lifetime_flag_,
407 peer_,
408 unreported_consumed_bytes_));
409 unreported_consumed_bytes_ = 0;
410}
411
412} // namespace
413
414namespace content {
415
[email protected]d7db4f622012-06-04 18:20:56416ByteStreamReader::~ByteStreamReader() { }
[email protected]b03507862012-05-23 17:11:50417
[email protected]d7db4f622012-06-04 18:20:56418ByteStreamWriter::~ByteStreamWriter() { }
[email protected]b03507862012-05-23 17:11:50419
420void CreateByteStream(
421 scoped_refptr<base::SequencedTaskRunner> input_task_runner,
422 scoped_refptr<base::SequencedTaskRunner> output_task_runner,
423 size_t buffer_size,
[email protected]d7db4f622012-06-04 18:20:56424 scoped_ptr<ByteStreamWriter>* input,
425 scoped_ptr<ByteStreamReader>* output) {
[email protected]b03507862012-05-23 17:11:50426 scoped_refptr<LifetimeFlag> input_flag(new LifetimeFlag());
427 scoped_refptr<LifetimeFlag> output_flag(new LifetimeFlag());
428
[email protected]d7db4f622012-06-04 18:20:56429 ByteStreamWriterImpl* in = new ByteStreamWriterImpl(
[email protected]b03507862012-05-23 17:11:50430 input_task_runner, input_flag, buffer_size);
[email protected]d7db4f622012-06-04 18:20:56431 ByteStreamReaderImpl* out = new ByteStreamReaderImpl(
[email protected]b03507862012-05-23 17:11:50432 output_task_runner, output_flag, buffer_size);
433
434 in->SetPeer(out, output_task_runner, output_flag);
435 out->SetPeer(in, input_task_runner, input_flag);
436 input->reset(in);
437 output->reset(out);
438}
439
440} // namespace content