blob: 5c836bd6a0a87e5c1d580ea295f4c08c7adc37ea [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
[email protected]f8e92b5d2013-03-21 18:35:465#include "content/browser/byte_stream.h"
[email protected]b03507862012-05-23 17:11:506
[email protected]6a14c192013-08-06 20:18:427#include <deque>
8#include <set>
9#include <utility>
10
[email protected]b03507862012-05-23 17:11:5011#include "base/bind.h"
12#include "base/location.h"
[email protected]b03507862012-05-23 17:11:5013#include "base/memory/ref_counted.h"
[email protected]fb441962013-05-08 05:35:2414#include "base/sequenced_task_runner.h"
[email protected]b03507862012-05-23 17:11:5015
[email protected]35869622012-10-26 23:23:5516namespace content {
[email protected]b03507862012-05-23 17:11:5017namespace {
18
19typedef std::deque<std::pair<scoped_refptr<net::IOBuffer>, size_t> >
20ContentVector;
21
[email protected]d7db4f622012-06-04 18:20:5622class ByteStreamReaderImpl;
[email protected]b03507862012-05-23 17:11:5023
24// A poor man's weak pointer; a RefCountedThreadSafe boolean that can be
25// cleared in an object destructor and accessed to check for object
26// existence. We can't use weak pointers because they're tightly tied to
27// threads rather than task runners.
28// TODO(rdsmith): A better solution would be extending weak pointers
29// to support SequencedTaskRunners.
30struct LifetimeFlag : public base::RefCountedThreadSafe<LifetimeFlag> {
31 public:
[email protected]41fed972012-06-21 20:46:4532 LifetimeFlag() : is_alive(true) { }
33 bool is_alive;
[email protected]b03507862012-05-23 17:11:5034
35 protected:
36 friend class base::RefCountedThreadSafe<LifetimeFlag>;
37 virtual ~LifetimeFlag() { }
38
39 private:
40 DISALLOW_COPY_AND_ASSIGN(LifetimeFlag);
41};
42
[email protected]d7db4f622012-06-04 18:20:5643// For both ByteStreamWriterImpl and ByteStreamReaderImpl, Construction and
[email protected]b03507862012-05-23 17:11:5044// SetPeer may happen anywhere; all other operations on each class must
45// happen in the context of their SequencedTaskRunner.
[email protected]35869622012-10-26 23:23:5546class ByteStreamWriterImpl : public ByteStreamWriter {
[email protected]b03507862012-05-23 17:11:5047 public:
[email protected]d7db4f622012-06-04 18:20:5648 ByteStreamWriterImpl(scoped_refptr<base::SequencedTaskRunner> task_runner,
[email protected]41fed972012-06-21 20:46:4549 scoped_refptr<LifetimeFlag> lifetime_flag,
50 size_t buffer_size);
dchengc2282aa2014-10-21 12:07:5851 ~ByteStreamWriterImpl() override;
[email protected]b03507862012-05-23 17:11:5052
53 // Must be called before any operations are performed.
[email protected]d7db4f622012-06-04 18:20:5654 void SetPeer(ByteStreamReaderImpl* peer,
[email protected]b03507862012-05-23 17:11:5055 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
56 scoped_refptr<LifetimeFlag> peer_lifetime_flag);
57
[email protected]d7db4f622012-06-04 18:20:5658 // Overridden from ByteStreamWriter.
dchengc2282aa2014-10-21 12:07:5859 bool Write(scoped_refptr<net::IOBuffer> buffer, size_t byte_count) override;
60 void Flush() override;
61 void Close(int status) override;
62 void RegisterCallback(const base::Closure& source_callback) override;
63 size_t GetTotalBufferedBytes() const override;
[email protected]b03507862012-05-23 17:11:5064
[email protected]d7db4f622012-06-04 18:20:5665 // PostTask target from |ByteStreamReaderImpl::MaybeUpdateInput|.
[email protected]b03507862012-05-23 17:11:5066 static void UpdateWindow(scoped_refptr<LifetimeFlag> lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:5667 ByteStreamWriterImpl* target,
[email protected]b03507862012-05-23 17:11:5068 size_t bytes_consumed);
69
70 private:
71 // Called from UpdateWindow when object existence has been validated.
72 void UpdateWindowInternal(size_t bytes_consumed);
73
[email protected]8d0c23e2013-08-02 11:02:3074 void PostToPeer(bool complete, int status);
[email protected]b03507862012-05-23 17:11:5075
76 const size_t total_buffer_size_;
77
78 // All data objects in this class are only valid to access on
79 // this task runner except as otherwise noted.
80 scoped_refptr<base::SequencedTaskRunner> my_task_runner_;
81
82 // True while this object is alive.
83 scoped_refptr<LifetimeFlag> my_lifetime_flag_;
84
85 base::Closure space_available_callback_;
86 ContentVector input_contents_;
87 size_t input_contents_size_;
88
89 // ** Peer information.
90
91 scoped_refptr<base::SequencedTaskRunner> peer_task_runner_;
92
93 // How much we've sent to the output that for flow control purposes we
94 // must assume hasn't been read yet.
95 size_t output_size_used_;
96
97 // Only valid to access on peer_task_runner_.
98 scoped_refptr<LifetimeFlag> peer_lifetime_flag_;
99
100 // Only valid to access on peer_task_runner_ if
101 // |*peer_lifetime_flag_ == true|
[email protected]d7db4f622012-06-04 18:20:56102 ByteStreamReaderImpl* peer_;
[email protected]b03507862012-05-23 17:11:50103};
104
[email protected]35869622012-10-26 23:23:55105class ByteStreamReaderImpl : public ByteStreamReader {
[email protected]b03507862012-05-23 17:11:50106 public:
[email protected]d7db4f622012-06-04 18:20:56107 ByteStreamReaderImpl(scoped_refptr<base::SequencedTaskRunner> task_runner,
[email protected]b03507862012-05-23 17:11:50108 scoped_refptr<LifetimeFlag> lifetime_flag,
109 size_t buffer_size);
dchengc2282aa2014-10-21 12:07:58110 ~ByteStreamReaderImpl() override;
[email protected]b03507862012-05-23 17:11:50111
112 // Must be called before any operations are performed.
[email protected]d7db4f622012-06-04 18:20:56113 void SetPeer(ByteStreamWriterImpl* peer,
[email protected]b03507862012-05-23 17:11:50114 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
115 scoped_refptr<LifetimeFlag> peer_lifetime_flag);
116
[email protected]d7db4f622012-06-04 18:20:56117 // Overridden from ByteStreamReader.
dchengc2282aa2014-10-21 12:07:58118 StreamState Read(scoped_refptr<net::IOBuffer>* data, size_t* length) override;
119 int GetStatus() const override;
120 void RegisterCallback(const base::Closure& sink_callback) override;
[email protected]b03507862012-05-23 17:11:50121
[email protected]8d0c23e2013-08-02 11:02:30122 // PostTask target from |ByteStreamWriterImpl::Write| and
[email protected]d7db4f622012-06-04 18:20:56123 // |ByteStreamWriterImpl::Close|.
[email protected]b03507862012-05-23 17:11:50124 // Receive data from our peer.
125 // static because it may be called after the object it is targeting
126 // has been destroyed. It may not access |*target|
127 // if |*object_lifetime_flag| is false.
128 static void TransferData(
129 scoped_refptr<LifetimeFlag> object_lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:56130 ByteStreamReaderImpl* target,
[email protected]b03507862012-05-23 17:11:50131 scoped_ptr<ContentVector> transfer_buffer,
132 size_t transfer_buffer_bytes,
133 bool source_complete,
[email protected]8d0c23e2013-08-02 11:02:30134 int status);
[email protected]b03507862012-05-23 17:11:50135
136 private:
137 // Called from TransferData once object existence has been validated.
138 void TransferDataInternal(
139 scoped_ptr<ContentVector> transfer_buffer,
140 size_t transfer_buffer_bytes,
141 bool source_complete,
[email protected]8d0c23e2013-08-02 11:02:30142 int status);
[email protected]b03507862012-05-23 17:11:50143
144 void MaybeUpdateInput();
145
146 const size_t total_buffer_size_;
147
148 scoped_refptr<base::SequencedTaskRunner> my_task_runner_;
149
150 // True while this object is alive.
151 scoped_refptr<LifetimeFlag> my_lifetime_flag_;
152
153 ContentVector available_contents_;
154
155 bool received_status_;
[email protected]8d0c23e2013-08-02 11:02:30156 int status_;
[email protected]b03507862012-05-23 17:11:50157
158 base::Closure data_available_callback_;
159
160 // Time of last point at which data in stream transitioned from full
161 // to non-full. Nulled when a callback is sent.
162 base::Time last_non_full_time_;
163
164 // ** Peer information
165
166 scoped_refptr<base::SequencedTaskRunner> peer_task_runner_;
167
168 // How much has been removed from this class that we haven't told
169 // the input about yet.
170 size_t unreported_consumed_bytes_;
171
172 // Only valid to access on peer_task_runner_.
173 scoped_refptr<LifetimeFlag> peer_lifetime_flag_;
174
175 // Only valid to access on peer_task_runner_ if
176 // |*peer_lifetime_flag_ == true|
[email protected]d7db4f622012-06-04 18:20:56177 ByteStreamWriterImpl* peer_;
[email protected]b03507862012-05-23 17:11:50178};
179
[email protected]d7db4f622012-06-04 18:20:56180ByteStreamWriterImpl::ByteStreamWriterImpl(
[email protected]b03507862012-05-23 17:11:50181 scoped_refptr<base::SequencedTaskRunner> task_runner,
182 scoped_refptr<LifetimeFlag> lifetime_flag,
183 size_t buffer_size)
184 : total_buffer_size_(buffer_size),
185 my_task_runner_(task_runner),
186 my_lifetime_flag_(lifetime_flag),
187 input_contents_size_(0),
188 output_size_used_(0),
189 peer_(NULL) {
190 DCHECK(my_lifetime_flag_.get());
[email protected]41fed972012-06-21 20:46:45191 my_lifetime_flag_->is_alive = true;
[email protected]b03507862012-05-23 17:11:50192}
193
[email protected]d7db4f622012-06-04 18:20:56194ByteStreamWriterImpl::~ByteStreamWriterImpl() {
[email protected]f9feef32014-03-14 19:14:42195 // No RunsTasksOnCurrentThread() check to allow deleting a created writer
196 // before we start using it. Once started, should be deleted on the specified
197 // task runner.
[email protected]41fed972012-06-21 20:46:45198 my_lifetime_flag_->is_alive = false;
[email protected]b03507862012-05-23 17:11:50199}
200
[email protected]d7db4f622012-06-04 18:20:56201void ByteStreamWriterImpl::SetPeer(
202 ByteStreamReaderImpl* peer,
[email protected]b03507862012-05-23 17:11:50203 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
204 scoped_refptr<LifetimeFlag> peer_lifetime_flag) {
205 peer_ = peer;
206 peer_task_runner_ = peer_task_runner;
207 peer_lifetime_flag_ = peer_lifetime_flag;
208}
209
[email protected]d7db4f622012-06-04 18:20:56210bool ByteStreamWriterImpl::Write(
[email protected]b03507862012-05-23 17:11:50211 scoped_refptr<net::IOBuffer> buffer, size_t byte_count) {
212 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
213
[email protected]07516262013-08-22 07:43:24214 // Check overflow.
215 //
216 // TODO(tyoshino): Discuss with content/browser/download developer and if
217 // they're fine with, set smaller limit and make it configurable.
218 size_t space_limit = std::numeric_limits<size_t>::max() -
219 GetTotalBufferedBytes();
220 if (byte_count > space_limit) {
221 // TODO(tyoshino): Tell the user that Write() failed.
222 // Ignore input.
223 return false;
224 }
225
[email protected]b03507862012-05-23 17:11:50226 input_contents_.push_back(std::make_pair(buffer, byte_count));
227 input_contents_size_ += byte_count;
228
229 // Arbitrarily, we buffer to a third of the total size before sending.
230 if (input_contents_size_ > total_buffer_size_ / kFractionBufferBeforeSending)
[email protected]8d0c23e2013-08-02 11:02:30231 PostToPeer(false, 0);
[email protected]b03507862012-05-23 17:11:50232
[email protected]07516262013-08-22 07:43:24233 return GetTotalBufferedBytes() <= total_buffer_size_;
[email protected]b03507862012-05-23 17:11:50234}
235
[email protected]566357e2013-07-31 03:59:36236void ByteStreamWriterImpl::Flush() {
237 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
238 if (input_contents_size_ > 0)
[email protected]8d0c23e2013-08-02 11:02:30239 PostToPeer(false, 0);
[email protected]566357e2013-07-31 03:59:36240}
241
[email protected]8d0c23e2013-08-02 11:02:30242void ByteStreamWriterImpl::Close(int status) {
[email protected]b03507862012-05-23 17:11:50243 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
244 PostToPeer(true, status);
245}
246
[email protected]d7db4f622012-06-04 18:20:56247void ByteStreamWriterImpl::RegisterCallback(
[email protected]b03507862012-05-23 17:11:50248 const base::Closure& source_callback) {
249 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
250 space_available_callback_ = source_callback;
251}
252
[email protected]07516262013-08-22 07:43:24253size_t ByteStreamWriterImpl::GetTotalBufferedBytes() const {
254 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
255 // This sum doesn't overflow since Write() fails if this sum is going to
256 // overflow.
257 return input_contents_size_ + output_size_used_;
258}
259
[email protected]b03507862012-05-23 17:11:50260// static
[email protected]d7db4f622012-06-04 18:20:56261void ByteStreamWriterImpl::UpdateWindow(
262 scoped_refptr<LifetimeFlag> lifetime_flag, ByteStreamWriterImpl* target,
[email protected]b03507862012-05-23 17:11:50263 size_t bytes_consumed) {
264 // If the target object isn't alive anymore, we do nothing.
[email protected]41fed972012-06-21 20:46:45265 if (!lifetime_flag->is_alive) return;
[email protected]b03507862012-05-23 17:11:50266
267 target->UpdateWindowInternal(bytes_consumed);
268}
269
[email protected]d7db4f622012-06-04 18:20:56270void ByteStreamWriterImpl::UpdateWindowInternal(size_t bytes_consumed) {
[email protected]b03507862012-05-23 17:11:50271 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
[email protected]07516262013-08-22 07:43:24272
273 bool was_above_limit = GetTotalBufferedBytes() > total_buffer_size_;
274
[email protected]b03507862012-05-23 17:11:50275 DCHECK_GE(output_size_used_, bytes_consumed);
276 output_size_used_ -= bytes_consumed;
277
278 // Callback if we were above the limit and we're now <= to it.
[email protected]07516262013-08-22 07:43:24279 bool no_longer_above_limit = GetTotalBufferedBytes() <= total_buffer_size_;
[email protected]b03507862012-05-23 17:11:50280
[email protected]07516262013-08-22 07:43:24281 if (no_longer_above_limit && was_above_limit &&
[email protected]b03507862012-05-23 17:11:50282 !space_available_callback_.is_null())
283 space_available_callback_.Run();
284}
285
[email protected]8d0c23e2013-08-02 11:02:30286void ByteStreamWriterImpl::PostToPeer(bool complete, int status) {
[email protected]b03507862012-05-23 17:11:50287 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
288 // Valid contexts in which to call.
289 DCHECK(complete || 0 != input_contents_size_);
290
[email protected]6a14c192013-08-06 20:18:42291 scoped_ptr<ContentVector> transfer_buffer;
[email protected]b03507862012-05-23 17:11:50292 size_t buffer_size = 0;
293 if (0 != input_contents_size_) {
294 transfer_buffer.reset(new ContentVector);
295 transfer_buffer->swap(input_contents_);
296 buffer_size = input_contents_size_;
297 output_size_used_ += input_contents_size_;
298 input_contents_size_ = 0;
299 }
300 peer_task_runner_->PostTask(
301 FROM_HERE, base::Bind(
[email protected]d7db4f622012-06-04 18:20:56302 &ByteStreamReaderImpl::TransferData,
[email protected]b03507862012-05-23 17:11:50303 peer_lifetime_flag_,
304 peer_,
[email protected]c7e946702012-12-18 11:55:09305 base::Passed(&transfer_buffer),
[email protected]b03507862012-05-23 17:11:50306 buffer_size,
307 complete,
308 status));
309}
310
[email protected]d7db4f622012-06-04 18:20:56311ByteStreamReaderImpl::ByteStreamReaderImpl(
[email protected]b03507862012-05-23 17:11:50312 scoped_refptr<base::SequencedTaskRunner> task_runner,
313 scoped_refptr<LifetimeFlag> lifetime_flag,
314 size_t buffer_size)
315 : total_buffer_size_(buffer_size),
316 my_task_runner_(task_runner),
317 my_lifetime_flag_(lifetime_flag),
318 received_status_(false),
[email protected]8d0c23e2013-08-02 11:02:30319 status_(0),
[email protected]b03507862012-05-23 17:11:50320 unreported_consumed_bytes_(0),
321 peer_(NULL) {
322 DCHECK(my_lifetime_flag_.get());
[email protected]41fed972012-06-21 20:46:45323 my_lifetime_flag_->is_alive = true;
[email protected]b03507862012-05-23 17:11:50324}
325
[email protected]d7db4f622012-06-04 18:20:56326ByteStreamReaderImpl::~ByteStreamReaderImpl() {
[email protected]f9feef32014-03-14 19:14:42327 // No RunsTasksOnCurrentThread() check to allow deleting a created writer
328 // before we start using it. Once started, should be deleted on the specified
329 // task runner.
[email protected]41fed972012-06-21 20:46:45330 my_lifetime_flag_->is_alive = false;
[email protected]b03507862012-05-23 17:11:50331}
332
[email protected]d7db4f622012-06-04 18:20:56333void ByteStreamReaderImpl::SetPeer(
334 ByteStreamWriterImpl* peer,
[email protected]b03507862012-05-23 17:11:50335 scoped_refptr<base::SequencedTaskRunner> peer_task_runner,
336 scoped_refptr<LifetimeFlag> peer_lifetime_flag) {
337 peer_ = peer;
338 peer_task_runner_ = peer_task_runner;
339 peer_lifetime_flag_ = peer_lifetime_flag;
340}
341
[email protected]d7db4f622012-06-04 18:20:56342ByteStreamReaderImpl::StreamState
343ByteStreamReaderImpl::Read(scoped_refptr<net::IOBuffer>* data,
[email protected]b03507862012-05-23 17:11:50344 size_t* length) {
345 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
346
347 if (available_contents_.size()) {
348 *data = available_contents_.front().first;
349 *length = available_contents_.front().second;
350 available_contents_.pop_front();
351 unreported_consumed_bytes_ += *length;
352
353 MaybeUpdateInput();
354 return STREAM_HAS_DATA;
355 }
356 if (received_status_) {
357 return STREAM_COMPLETE;
358 }
359 return STREAM_EMPTY;
360}
361
[email protected]8d0c23e2013-08-02 11:02:30362int ByteStreamReaderImpl::GetStatus() const {
[email protected]b03507862012-05-23 17:11:50363 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
364 DCHECK(received_status_);
365 return status_;
366}
367
[email protected]d7db4f622012-06-04 18:20:56368void ByteStreamReaderImpl::RegisterCallback(
[email protected]b03507862012-05-23 17:11:50369 const base::Closure& sink_callback) {
370 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
371
372 data_available_callback_ = sink_callback;
373}
374
375// static
[email protected]d7db4f622012-06-04 18:20:56376void ByteStreamReaderImpl::TransferData(
[email protected]b03507862012-05-23 17:11:50377 scoped_refptr<LifetimeFlag> object_lifetime_flag,
[email protected]d7db4f622012-06-04 18:20:56378 ByteStreamReaderImpl* target,
[email protected]b03507862012-05-23 17:11:50379 scoped_ptr<ContentVector> transfer_buffer,
380 size_t buffer_size,
381 bool source_complete,
[email protected]8d0c23e2013-08-02 11:02:30382 int status) {
[email protected]b03507862012-05-23 17:11:50383 // If our target is no longer alive, do nothing.
[email protected]41fed972012-06-21 20:46:45384 if (!object_lifetime_flag->is_alive) return;
[email protected]b03507862012-05-23 17:11:50385
386 target->TransferDataInternal(
387 transfer_buffer.Pass(), buffer_size, source_complete, status);
388}
389
[email protected]d7db4f622012-06-04 18:20:56390void ByteStreamReaderImpl::TransferDataInternal(
[email protected]b03507862012-05-23 17:11:50391 scoped_ptr<ContentVector> transfer_buffer,
392 size_t buffer_size,
393 bool source_complete,
[email protected]8d0c23e2013-08-02 11:02:30394 int status) {
[email protected]b03507862012-05-23 17:11:50395 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
396
397 bool was_empty = available_contents_.empty();
398
[email protected]59383c782013-04-17 16:43:27399 if (transfer_buffer) {
[email protected]b03507862012-05-23 17:11:50400 available_contents_.insert(available_contents_.end(),
[email protected]6a14c192013-08-06 20:18:42401 transfer_buffer->begin(),
402 transfer_buffer->end());
[email protected]b03507862012-05-23 17:11:50403 }
404
405 if (source_complete) {
406 received_status_ = true;
407 status_ = status;
408 }
409
410 // Callback on transition from empty to non-empty, or
411 // source complete.
412 if (((was_empty && !available_contents_.empty()) ||
413 source_complete) &&
414 !data_available_callback_.is_null())
415 data_available_callback_.Run();
416}
417
418// Decide whether or not to send the input a window update.
419// Currently we do that whenever we've got unreported consumption
420// greater than 1/3 of total size.
[email protected]d7db4f622012-06-04 18:20:56421void ByteStreamReaderImpl::MaybeUpdateInput() {
[email protected]b03507862012-05-23 17:11:50422 DCHECK(my_task_runner_->RunsTasksOnCurrentThread());
423
424 if (unreported_consumed_bytes_ <=
425 total_buffer_size_ / kFractionReadBeforeWindowUpdate)
426 return;
427
428 peer_task_runner_->PostTask(
429 FROM_HERE, base::Bind(
[email protected]d7db4f622012-06-04 18:20:56430 &ByteStreamWriterImpl::UpdateWindow,
[email protected]b03507862012-05-23 17:11:50431 peer_lifetime_flag_,
432 peer_,
433 unreported_consumed_bytes_));
434 unreported_consumed_bytes_ = 0;
435}
436
437} // namespace
438
[email protected]fc179dd12013-03-16 09:35:19439const int ByteStreamWriter::kFractionBufferBeforeSending = 3;
[email protected]479bd642013-01-22 19:20:29440const int ByteStreamReader::kFractionReadBeforeWindowUpdate = 3;
441
[email protected]d7db4f622012-06-04 18:20:56442ByteStreamReader::~ByteStreamReader() { }
[email protected]b03507862012-05-23 17:11:50443
[email protected]d7db4f622012-06-04 18:20:56444ByteStreamWriter::~ByteStreamWriter() { }
[email protected]b03507862012-05-23 17:11:50445
446void CreateByteStream(
447 scoped_refptr<base::SequencedTaskRunner> input_task_runner,
448 scoped_refptr<base::SequencedTaskRunner> output_task_runner,
449 size_t buffer_size,
[email protected]d7db4f622012-06-04 18:20:56450 scoped_ptr<ByteStreamWriter>* input,
451 scoped_ptr<ByteStreamReader>* output) {
[email protected]b03507862012-05-23 17:11:50452 scoped_refptr<LifetimeFlag> input_flag(new LifetimeFlag());
453 scoped_refptr<LifetimeFlag> output_flag(new LifetimeFlag());
454
[email protected]d7db4f622012-06-04 18:20:56455 ByteStreamWriterImpl* in = new ByteStreamWriterImpl(
[email protected]b03507862012-05-23 17:11:50456 input_task_runner, input_flag, buffer_size);
[email protected]d7db4f622012-06-04 18:20:56457 ByteStreamReaderImpl* out = new ByteStreamReaderImpl(
[email protected]b03507862012-05-23 17:11:50458 output_task_runner, output_flag, buffer_size);
459
460 in->SetPeer(out, output_task_runner, output_flag);
461 out->SetPeer(in, input_task_runner, input_flag);
462 input->reset(in);
463 output->reset(out);
464}
465
466} // namespace content