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

Side by Side Diff: Source/modules/fetch/DataConsumerTeeTest.cpp

Issue 1195563002: Introduce DataConsumerHandleTee (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: 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
« no previous file with comments | « Source/modules/fetch/DataConsumerTee.cpp ('k') | Source/modules/modules.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "config.h"
6 #include "modules/fetch/DataConsumerTee.h"
7
8 #include "core/testing/DummyPageHolder.h"
9 #include "core/testing/NullExecutionContext.h"
10 #include "platform/Task.h"
11 #include "platform/ThreadSafeFunctional.h"
12 #include "platform/WebThreadSupportingGC.h"
13 #include "public/platform/Platform.h"
14 #include "public/platform/WebThread.h"
15 #include "public/platform/WebTraceLocation.h"
16 #include "public/platform/WebWaitableEvent.h"
17 #include "wtf/Deque.h"
18 #include "wtf/PassRefPtr.h"
19 #include "wtf/RefPtr.h"
20 #include "wtf/ThreadSafeRefCounted.h"
21 #include "wtf/ThreadingPrimitives.h"
22 #include "wtf/Vector.h"
23
24 #include <gtest/gtest.h>
25 #include <string.h>
26 #include <v8.h>
27
28 namespace blink {
29 namespace {
30
31 using Result = WebDataConsumerHandle::Result;
32 const WebDataConsumerHandle::Flags kNone = WebDataConsumerHandle::FlagNone;
33 const Result kOk = WebDataConsumerHandle::Ok;
34 const Result kShouldWait = WebDataConsumerHandle::ShouldWait;
35 const Result kDone = WebDataConsumerHandle::Done;
36 const Result kUnexpectedError = WebDataConsumerHandle::UnexpectedError;
37
38 class Command final {
39 public:
40 enum Name {
41 Data,
42 Done,
43 Error,
44 Wait,
45 };
46
47 Command(Name name) : m_name(name) { }
48 Command(Name name, const Vector<char>& body) : m_name(name), m_body(body) { }
49 Command(Name name, const char* body, size_t size) : m_name(name)
50 {
51 m_body.append(body, size);
52 }
53 Command(Name name, const char* body) : Command(name, body, strlen(body)) { }
54 Name name() const { return m_name; }
55 const Vector<char>& body() const { return m_body; }
56
57 private:
58 const Name m_name;
59 Vector<char> m_body;
60 };
61
62 class Handle final : public WebDataConsumerHandle {
hiroshige 2015/06/23 09:20:44 Please add a comment that says Handle is a WebData
yhirano 2015/06/23 10:18:19 Done.
63 public:
64 class Context final : public ThreadSafeRefCounted<Context> {
65 public:
66 static PassRefPtr<Context> create() { return adoptRef(new Context); }
67
68 // This function cannot be called after creating a tee.
69 void add(const Command& command)
70 {
71 m_commands.append(command);
72 }
73
74 void notify()
75 {
76 if (!m_client)
77 return;
78 ASSERT(m_readerThread);
79 m_readerThread->postTask(FROM_HERE, new Task(threadSafeBind(&Context ::notifyInternal, this)));
80 }
81
82 void attachReader(WebDataConsumerHandle::Client* client)
83 {
84 ASSERT(!m_readerThread);
85 ASSERT(!m_client);
86 m_readerThread = Platform::current()->currentThread();
87 m_client = client;
88
89 if (m_client && !(isEmpty() && m_result == kShouldWait))
90 notify();
91 }
92 void detachReader()
93 {
94 ASSERT(m_readerThread && m_readerThread->isCurrentThread());
95 m_readerThread = nullptr;
96 m_client = nullptr;
97 if (!m_isHandleAttached)
98 m_detachEvent->signal();
hiroshige 2015/06/23 09:20:44 Can we move |m_detachEvent->signal()| to the dtor
yhirano 2015/06/23 10:18:20 That assumption does not hold in DetachBothDestina
99 }
100
101 void detachHandle()
102 {
103 m_isHandleAttached = false;
104 if (!m_readerThread)
105 m_detachEvent->signal();
hiroshige 2015/06/23 09:20:44 ditto, and can we remove detachHandle()?
yhirano 2015/06/23 10:18:19 Ditto
106 }
107
108 bool isEmpty() const { return m_commands.isEmpty(); }
109 const Command& top()
110 {
111 ASSERT(!isEmpty());
112 return m_commands.first();
113 }
114
115 void consume(size_t size)
116 {
117 ASSERT(!isEmpty());
118 ASSERT(size + m_offset <= top().body().size());
119 bool fullyConsumed = (size + m_offset == top().body().size());
hiroshige 2015/06/23 09:20:45 ">=" instead of "==" is a little more robust and c
yhirano 2015/06/23 10:18:20 Done.
120 if (fullyConsumed) {
121 m_offset = 0;
122 m_commands.removeFirst();
123 } else {
124 m_offset += size;
125 }
126 }
127
128 size_t offset() const { return m_offset; }
129 Result result() const { return m_result; }
130 void setDone() { m_result = Done; }
131 void setError() { m_result = UnexpectedError; }
132
133 Mutex& mutex()
134 {
135 return m_mutex;
136 }
137
138 WebWaitableEvent* detachEvent() { return m_detachEvent.get(); }
hiroshige 2015/06/23 09:20:45 nit optional: detachEvent() seems "to detach an ev
yhirano 2015/06/23 10:18:19 Done.
139
140 private:
141 Context()
142 : m_offset(0)
143 , m_readerThread(nullptr)
144 , m_client(nullptr)
145 , m_result(ShouldWait)
146 , m_isHandleAttached(true)
147 , m_detachEvent(adoptPtr(Platform::current()->createWaitableEvent()) )
148 {
149 }
150
151 void notifyInternal()
152 {
153 {
154 MutexLocker locker(m_mutex);
155 if (!m_client || !m_readerThread->isCurrentThread()) {
156 // There is no client, or a new reader is attached.
157 return;
158 }
159 }
160 // The reading thread is the current thread.
161 m_client->didGetReadable();
162 }
163
164 Deque<Command> m_commands;
165 size_t m_offset;
166 WebThread* m_readerThread;
167 Client* m_client;
168 Result m_result;
169 bool m_isHandleAttached;
170 Mutex m_mutex;
171 OwnPtr<WebWaitableEvent> m_detachEvent;
172 };
173
174 class ReaderImpl final : public Reader {
175 public:
176 ReaderImpl(PassRefPtr<Context> context, Client* client)
177 : m_context(context)
178 {
179 MutexLocker locker(m_context->mutex());
hiroshige 2015/06/23 09:20:44 I prefer taking lock in Context, because it is cle
yhirano 2015/06/23 10:18:19 Done.
180 m_context->attachReader(client);
181 }
182 ~ReaderImpl()
183 {
184 MutexLocker locker(m_context->mutex());
hiroshige 2015/06/23 09:20:44 ditto.
yhirano 2015/06/23 10:18:19 Done.
185 m_context->detachReader();
186 }
187
188 Result read(void* buffer, size_t size, Flags flags, size_t* readSize) ov erride
189 {
190 const void* src = nullptr;
191 Result result = beginRead(&src, flags, readSize);
192 if (result != Ok)
193 return result;
194 memcpy(buffer, src, *readSize);
hiroshige 2015/06/23 09:20:44 We should take min(size, *readSize).
yhirano 2015/06/23 10:18:20 Done.
195 return endRead(*readSize);
196 }
197 Result beginRead(const void** buffer, Flags, size_t* available) override
198 {
199 MutexLocker locker(m_context->mutex());
hiroshige 2015/06/23 09:20:44 ditto, and can we move this implementation to Cont
yhirano 2015/06/23 10:18:19 Done.
200 *buffer = nullptr;
201 *available = 0;
202 if (m_context->isEmpty())
203 return m_context->result();
204
205 const Command& command = m_context->top();
206 Result result = Ok;
207 switch (command.name()) {
208 case Command::Data: {
209 auto& body = command.body();
210 *available = body.size() - m_context->offset();
211 *buffer = body.data() + m_context->offset();
212 result = Ok;
213 break;
214 }
215 case Command::Done:
216 m_context->setDone();
217 m_context->consume(0);
218 result = Done;
219 break;
220 case Command::Wait:
221 m_context->consume(0);
222 result = ShouldWait;
223 m_context->notify();
224 break;
225 case Command::Error:
226 m_context->setError();
227 m_context->consume(0);
228 result = UnexpectedError;
229 break;
230 }
231 return result;
232 }
233 Result endRead(size_t readSize) override
234 {
235 m_context->consume(readSize);
236 return Ok;
237 }
238
239 private:
240 RefPtr<Context> m_context;
241 };
242
243 Handle() : m_context(Context::create()) { }
244 ~Handle()
245 {
246 MutexLocker locker(m_context->mutex());
247 m_context->detachHandle();
248 }
249
250 ReaderImpl* obtainReaderInternal(Client* client) override { return new Reade rImpl(m_context, client); }
251
252 // Add a command to this handle. This function must be called on the
253 // creator thread. This function must be called BEFORE any reader is
254 // obtained.
255 void add(const Command& command)
256 {
257 MutexLocker locker(m_context->mutex());
258 m_context->add(command);
259 }
260
261 Context* context() { return m_context.get(); };
262
263 private:
264 RefPtr<Context> m_context;
265 };
266
267 class TestingThread final {
268 public:
269 explicit TestingThread(const char* name)
270 : m_thread(WebThreadSupportingGC::create(name))
271 , m_waitableEvent(adoptPtr(Platform::current()->createWaitableEvent()))
272 , m_isolate(nullptr)
273 {
274 m_thread->postTask(FROM_HERE, new Task(threadSafeBind(&TestingThread::in itialize, AllowCrossThreadAccess(this))));
275 m_waitableEvent->wait();
276 }
277
278 ~TestingThread()
279 {
280 m_thread->postTask(FROM_HERE, new Task(threadSafeBind(&TestingThread::sh utdown, AllowCrossThreadAccess(this))));
281 m_waitableEvent->wait();
282 }
283
284 WebThreadSupportingGC* thread() { return m_thread.get(); }
285 ExecutionContext* executionContext() { return m_executionContext.get(); }
286
287 private:
288 void initialize()
289 {
290 m_isolate = v8::Isolate::New(v8::Isolate::CreateParams());
291 m_isolate->Enter();
292 m_thread->initialize();
293 m_executionContext = adoptRefWillBeNoop(new NullExecutionContext());
294 m_waitableEvent->signal();
295 }
296
297 void shutdown()
298 {
299 m_executionContext = nullptr;
300 m_thread->shutdown();
301 m_isolate->Exit();
302 m_isolate->Dispose();
303 m_isolate = nullptr;
304 m_waitableEvent->signal();
305 }
306
307 OwnPtr<WebThreadSupportingGC> m_thread;
308 OwnPtr<WebWaitableEvent> m_waitableEvent;
309 RefPtrWillBePersistent<NullExecutionContext> m_executionContext;
310 v8::Isolate* m_isolate;
311 };
312
313 class HandleReader : public WebDataConsumerHandle::Client {
314 public:
315 HandleReader() : m_finalResult(kOk) { }
316
317 // Need to wait for the event signal after this function is called.
318 void start(PassOwnPtr<WebDataConsumerHandle> handle)
319 {
320 m_thread = adoptPtr(new TestingThread("reading thread"));
321 m_thread->thread()->postTask(FROM_HERE, new Task(threadSafeBind(&HandleR eader::obtainReader, AllowCrossThreadAccess(this), handle)));
322 m_waitableEvent = adoptPtr(Platform::current()->createWaitableEvent());
hiroshige 2015/06/23 09:20:44 L322 sould be before L321, because didGetReadable(
yhirano 2015/06/23 10:18:19 Done.
323 }
324
325 void didGetReadable() override
326 {
327 Result r = kOk;
328 char buffer[3];
329 while (true) {
330 size_t size;
331 r = m_reader->read(buffer, sizeof(buffer), kNone, &size);
332 if (r == kShouldWait)
333 return;
334 if (r != kOk)
335 break;
336 m_readString.append(String(buffer, size));
337 }
338 m_finalResult = r;
339 m_reader = nullptr;
340 m_waitableEvent->signal();
341 }
342
343 WebWaitableEvent* waitableEvent() { return m_waitableEvent.get(); }
344
345 // These should be accessed after the thread joines.
hiroshige 2015/06/23 09:20:44 nit: s/joines/joins/?
yhirano 2015/06/23 10:18:20 Done.
346 const String& readString() const { return m_readString; }
347 Result finalResult() const { return m_finalResult; }
348
349 private:
350 void obtainReader(PassOwnPtr<WebDataConsumerHandle> handle)
351 {
352 m_reader = handle->obtainReader(this);
353 }
354
355 OwnPtr<TestingThread> m_thread;
356 OwnPtr<WebDataConsumerHandle::Reader> m_reader;
357 String m_readString;
358 Result m_finalResult;
359 OwnPtr<WebWaitableEvent> m_waitableEvent;
360 };
361
362 class HandleTwoPhaseReader : public WebDataConsumerHandle::Client {
363 public:
364 HandleTwoPhaseReader() : m_finalResult(kOk) { }
365
366 // Need to wait for the event signal after this function is called.
367 void start(PassOwnPtr<WebDataConsumerHandle> handle)
368 {
369 m_thread = adoptPtr(new TestingThread("reading thread"));
370 m_thread->thread()->postTask(FROM_HERE, new Task(threadSafeBind(&HandleT woPhaseReader::obtainReader, AllowCrossThreadAccess(this), handle)));
371 m_waitableEvent = adoptPtr(Platform::current()->createWaitableEvent());
hiroshige 2015/06/23 09:20:44 ditto.
yhirano 2015/06/23 10:18:19 Done.
372 }
373
374 void didGetReadable() override
375 {
376 Result r = kOk;
377 while (true) {
378 const void* buffer = nullptr;
379 size_t size;
380 r = m_reader->beginRead(&buffer, kNone, &size);
381 if (r == kShouldWait)
382 return;
383 if (r != kOk)
384 break;
385 // Read smaller than availabe in order to test |endRead|.
386 size_t readSize = std::max(size * 2 / 3, static_cast<size_t>(1));
hiroshige 2015/06/23 09:20:45 BTW, can we assume beginRead()'s size > 0?
yhirano 2015/06/23 10:18:20 No I think, calling read or beginRead with zero si
387 m_readString.append(String(static_cast<const char*>(buffer), readSiz e));
388 m_reader->endRead(readSize);
389 }
390 m_finalResult = r;
391 m_reader = nullptr;
392 m_waitableEvent->signal();
393 }
394
395 WebWaitableEvent* waitableEvent() { return m_waitableEvent.get(); }
396
397 // These should be accessed after the thread joines.
hiroshige 2015/06/23 09:20:45 nit: joins.
yhirano 2015/06/23 10:18:19 Done.
398 const String& readString() const { return m_readString; }
399 Result finalResult() const { return m_finalResult; }
400
401 private:
402 void obtainReader(PassOwnPtr<WebDataConsumerHandle> handle)
403 {
404 m_reader = handle->obtainReader(this);
405 }
406
407 OwnPtr<TestingThread> m_thread;
408 OwnPtr<WebDataConsumerHandle::Reader> m_reader;
409 String m_readString;
410 Result m_finalResult;
411 OwnPtr<WebWaitableEvent> m_waitableEvent;
412 };
413
414 class TeeCreationThread {
415 public:
416 void run(PassOwnPtr<WebDataConsumerHandle> src, OwnPtr<WebDataConsumerHandle >* dest1, OwnPtr<WebDataConsumerHandle>* dest2)
417 {
418 m_thread = adoptPtr(new TestingThread("src thread"));
419 m_waitableEvent = adoptPtr(Platform::current()->createWaitableEvent());
420 m_thread->thread()->postTask(FROM_HERE, new Task(threadSafeBind(&TeeCrea tionThread::runInternal, AllowCrossThreadAccess(this), src, AllowCrossThreadAcce ss(dest1), AllowCrossThreadAccess(dest2))));
421 m_waitableEvent->wait();
422 }
423
424 TestingThread* thread() { return m_thread.get(); }
425
426 private:
427 void runInternal(PassOwnPtr<WebDataConsumerHandle> src, OwnPtr<WebDataConsum erHandle>* dest1, OwnPtr<WebDataConsumerHandle>* dest2)
428 {
429 DataConsumerTee::create(m_thread->executionContext(), src, dest1, dest2) ;
430 m_waitableEvent->signal();
431 }
432
433 OwnPtr<TestingThread> m_thread;
434 OwnPtr<WebWaitableEvent> m_waitableEvent;
435 };
436
437 TEST(DataConsumerTeeTest, CreateDone)
438 {
439 OwnPtr<Handle> src(adoptPtr(new Handle));
440 OwnPtr<WebDataConsumerHandle> dest1, dest2;
441
442 src->add(Command(Command::Done));
443
444 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
445 t->run(src.release(), &dest1, &dest2);
446
447 ASSERT_TRUE(dest1);
448 ASSERT_TRUE(dest2);
449
450 HandleReader r1, r2;
451 r1.start(dest1.release());
452 r2.start(dest2.release());
453
454 r1.waitableEvent()->wait();
455 r2.waitableEvent()->wait();
456
457 EXPECT_EQ(kDone, r1.finalResult());
458 EXPECT_EQ(String(), r1.readString());
459
460 EXPECT_EQ(kDone, r2.finalResult());
461 EXPECT_EQ(String(), r2.readString());
462 }
463
464 TEST(DataConsumerTeeTest, Read)
465 {
466 OwnPtr<Handle> src(adoptPtr(new Handle));
467 OwnPtr<WebDataConsumerHandle> dest1, dest2;
468
469 src->add(Command(Command::Wait));
470 src->add(Command(Command::Data, "hello, "));
471 src->add(Command(Command::Wait));
472 src->add(Command(Command::Data, "world"));
473 src->add(Command(Command::Wait));
474 src->add(Command(Command::Wait));
475 src->add(Command(Command::Done));
476
477 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
478 t->run(src.release(), &dest1, &dest2);
479
480 ASSERT_TRUE(dest1);
481 ASSERT_TRUE(dest2);
482
483 HandleReader r1, r2;
484 r1.start(dest1.release());
485 r2.start(dest2.release());
486
487 r1.waitableEvent()->wait();
488 r2.waitableEvent()->wait();
489
490 EXPECT_EQ(kDone, r1.finalResult());
491 EXPECT_EQ("hello, world", r1.readString());
492
493 EXPECT_EQ(kDone, r2.finalResult());
494 EXPECT_EQ("hello, world", r2.readString());
495 }
496
497 TEST(DataConsumerTeeTest, TwoPhaseRead)
498 {
499 OwnPtr<Handle> src(adoptPtr(new Handle));
500 OwnPtr<WebDataConsumerHandle> dest1, dest2;
501
502 src->add(Command(Command::Wait));
503 src->add(Command(Command::Data, "hello, "));
504 src->add(Command(Command::Wait));
505 src->add(Command(Command::Wait));
506 src->add(Command(Command::Wait));
507 src->add(Command(Command::Data, "world"));
508 src->add(Command(Command::Wait));
509 src->add(Command(Command::Done));
510
511 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
512 t->run(src.release(), &dest1, &dest2);
513
514 ASSERT_TRUE(dest1);
515 ASSERT_TRUE(dest2);
516
517 HandleTwoPhaseReader r1, r2;
518 r1.start(dest1.release());
519 r2.start(dest2.release());
520
521 r1.waitableEvent()->wait();
522 r2.waitableEvent()->wait();
523
524 EXPECT_EQ(kDone, r1.finalResult());
525 EXPECT_EQ("hello, world", r1.readString());
526
527 EXPECT_EQ(kDone, r2.finalResult());
528 EXPECT_EQ("hello, world", r2.readString());
529 }
530
531 TEST(DataConsumerTeeTest, Error)
532 {
533 OwnPtr<Handle> src(adoptPtr(new Handle));
534 OwnPtr<WebDataConsumerHandle> dest1, dest2;
535
536 src->add(Command(Command::Data, "hello, "));
537 src->add(Command(Command::Data, "world"));
538 src->add(Command(Command::Error));
539
540 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
541 t->run(src.release(), &dest1, &dest2);
542
543 ASSERT_TRUE(dest1);
544 ASSERT_TRUE(dest2);
545
546 HandleReader r1, r2;
547 r1.start(dest1.release());
548 r2.start(dest2.release());
549
550 r1.waitableEvent()->wait();
551 r2.waitableEvent()->wait();
552
553 EXPECT_EQ(kUnexpectedError, r1.finalResult());
554 EXPECT_EQ(kUnexpectedError, r2.finalResult());
555 }
556
557 TEST(DataConsumerTeeTest, DetachSource)
558 {
559 OwnPtr<Handle> src(adoptPtr(new Handle));
560 OwnPtr<WebDataConsumerHandle> dest1, dest2;
561
562 src->add(Command(Command::Data, "hello, "));
563 src->add(Command(Command::Data, "world"));
564
565 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
566 t->run(src.release(), &dest1, &dest2);
567
568 ASSERT_TRUE(dest1);
569 ASSERT_TRUE(dest2);
570
571 HandleReader r1, r2;
572 r1.start(dest1.release());
573 r2.start(dest2.release());
574
575 t = nullptr;
576
577 r1.waitableEvent()->wait();
578 r2.waitableEvent()->wait();
579
580 EXPECT_EQ(kUnexpectedError, r1.finalResult());
581 EXPECT_EQ(kUnexpectedError, r2.finalResult());
582 }
583
584 TEST(DataConsumerTeeTest, DetachSourceAfterReadingDone)
585 {
586 OwnPtr<Handle> src(adoptPtr(new Handle));
587 OwnPtr<WebDataConsumerHandle> dest1, dest2;
588
589 src->add(Command(Command::Data, "hello, "));
590 src->add(Command(Command::Data, "world"));
591 src->add(Command(Command::Done));
592
593 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
594 t->run(src.release(), &dest1, &dest2);
595
596 ASSERT_TRUE(dest1);
597 ASSERT_TRUE(dest2);
598
599 HandleReader r1, r2;
600 r1.start(dest1.release());
601 r1.waitableEvent()->wait();
602
603 EXPECT_EQ(kDone, r1.finalResult());
604 EXPECT_EQ("hello, world", r1.readString());
605
606 t = nullptr;
607
608 r2.start(dest2.release());
609 r2.waitableEvent()->wait();
610
611 EXPECT_EQ(kDone, r2.finalResult());
612 EXPECT_EQ("hello, world", r2.readString());
613 }
614
615 TEST(DataConsumerTeeTest, DetachOneDestination)
616 {
617 OwnPtr<Handle> src(adoptPtr(new Handle));
618 OwnPtr<WebDataConsumerHandle> dest1, dest2;
619
620 src->add(Command(Command::Data, "hello, "));
621 src->add(Command(Command::Data, "world"));
622 src->add(Command(Command::Done));
623
624 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
625 t->run(src.release(), &dest1, &dest2);
626
627 ASSERT_TRUE(dest1);
628 ASSERT_TRUE(dest2);
629
630 dest1 = nullptr;
631
632 HandleReader r2;
633 r2.start(dest2.release());
634 r2.waitableEvent()->wait();
635
636 EXPECT_EQ(kDone, r2.finalResult());
637 EXPECT_EQ("hello, world", r2.readString());
638 }
639
640 TEST(DataConsumerTeeTest, DetachBothDestinationsShouldStopSourceReader)
641 {
642 OwnPtr<Handle> src(adoptPtr(new Handle));
643 RefPtr<Handle::Context> context(src->context());
644 OwnPtr<WebDataConsumerHandle> dest1, dest2;
645
646 src->add(Command(Command::Data, "hello, "));
647 src->add(Command(Command::Data, "world"));
648
649 OwnPtr<TeeCreationThread> t = adoptPtr(new TeeCreationThread());
650 t->run(src.release(), &dest1, &dest2);
651
652 ASSERT_TRUE(dest1);
653 ASSERT_TRUE(dest2);
654
655 dest1 = nullptr;
656 dest2 = nullptr;
657
658 // Collect garbage to finalize the source reader.
659 Heap::collectGarbage(ThreadState::HeapPointersOnStack, ThreadState::GCWithSw eep, Heap::ForcedGC);
660 context->detachEvent()->wait();
661 }
662
663 } // namespace
664 } // namespace blink
OLDNEW
« no previous file with comments | « Source/modules/fetch/DataConsumerTee.cpp ('k') | Source/modules/modules.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698