1 // 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.
5 #include "net/proxy/multi_threaded_proxy_resolver.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/threading/platform_thread.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/net_log.h"
16 #include "net/base/net_log_unittest.h"
17 #include "net/base/test_completion_callback.h"
18 #include "net/proxy/proxy_info.h"
19 #include "testing/gtest/include/gtest/gtest.h"
26 // A synchronous mock ProxyResolver implementation, which can be used in
27 // conjunction with MultiThreadedProxyResolver.
28 // - returns a single-item proxy list with the query's host.
29 class MockProxyResolver : public ProxyResolver {
32 : ProxyResolver(true /*expects_pac_bytes*/),
33 wrong_loop_(base::MessageLoop::current()),
37 // ProxyResolver implementation.
38 virtual int GetProxyForURL(const GURL& query_url,
40 const CompletionCallback& callback,
41 RequestHandle* request,
42 const BoundNetLog& net_log) OVERRIDE {
43 if (resolve_latency_ != base::TimeDelta())
44 base::PlatformThread::Sleep(resolve_latency_);
46 CheckIsOnWorkerThread();
48 EXPECT_TRUE(callback.is_null());
49 EXPECT_TRUE(request == NULL);
51 // Write something into |net_log| (doesn't really have any meaning.)
52 net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT);
54 results->UseNamedProxy(query_url.host());
56 // Return a success code which represents the request's order.
57 return request_count_++;
60 virtual void CancelRequest(RequestHandle request) OVERRIDE {
64 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
66 return LOAD_STATE_IDLE;
69 virtual void CancelSetPacScript() OVERRIDE {
73 virtual int SetPacScript(
74 const scoped_refptr<ProxyResolverScriptData>& script_data,
75 const CompletionCallback& callback) OVERRIDE {
76 CheckIsOnWorkerThread();
77 last_script_data_ = script_data;
81 virtual void PurgeMemory() OVERRIDE {
82 CheckIsOnWorkerThread();
86 int purge_count() const { return purge_count_; }
87 int request_count() const { return request_count_; }
89 const ProxyResolverScriptData* last_script_data() const {
90 return last_script_data_.get();
93 void SetResolveLatency(base::TimeDelta latency) {
94 resolve_latency_ = latency;
98 void CheckIsOnWorkerThread() {
99 // We should be running on the worker thread -- while we don't know the
100 // message loop of MultiThreadedProxyResolver's worker thread, we do
101 // know that it is going to be distinct from the loop running the
102 // test, so at least make sure it isn't the main loop.
103 EXPECT_NE(base::MessageLoop::current(), wrong_loop_);
106 base::MessageLoop* wrong_loop_;
109 scoped_refptr<ProxyResolverScriptData> last_script_data_;
110 base::TimeDelta resolve_latency_;
114 // A mock synchronous ProxyResolver which can be set to block upon reaching
116 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
117 // otherwise there will be a race on |should_block_| since it is
118 // read without any synchronization.
119 class BlockableProxyResolver : public MockProxyResolver {
121 BlockableProxyResolver()
122 : should_block_(false),
123 unblocked_(true, true),
124 blocked_(true, false) {
128 should_block_ = true;
133 should_block_ = false;
138 void WaitUntilBlocked() {
142 virtual int GetProxyForURL(const GURL& query_url,
144 const CompletionCallback& callback,
145 RequestHandle* request,
146 const BoundNetLog& net_log) OVERRIDE {
152 return MockProxyResolver::GetProxyForURL(
153 query_url, results, callback, request, net_log);
158 base::WaitableEvent unblocked_;
159 base::WaitableEvent blocked_;
162 // ForwardingProxyResolver forwards all requests to |impl|.
163 class ForwardingProxyResolver : public ProxyResolver {
165 explicit ForwardingProxyResolver(ProxyResolver* impl)
166 : ProxyResolver(impl->expects_pac_bytes()),
169 virtual int GetProxyForURL(const GURL& query_url,
171 const CompletionCallback& callback,
172 RequestHandle* request,
173 const BoundNetLog& net_log) OVERRIDE {
174 return impl_->GetProxyForURL(
175 query_url, results, callback, request, net_log);
178 virtual void CancelRequest(RequestHandle request) OVERRIDE {
179 impl_->CancelRequest(request);
182 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
184 return LOAD_STATE_IDLE;
187 virtual void CancelSetPacScript() OVERRIDE {
188 impl_->CancelSetPacScript();
191 virtual int SetPacScript(
192 const scoped_refptr<ProxyResolverScriptData>& script_data,
193 const CompletionCallback& callback) OVERRIDE {
194 return impl_->SetPacScript(script_data, callback);
197 virtual void PurgeMemory() OVERRIDE {
198 impl_->PurgeMemory();
202 ProxyResolver* impl_;
205 // This factory returns ProxyResolvers that forward all requests to
207 class ForwardingProxyResolverFactory : public ProxyResolverFactory {
209 explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
210 : ProxyResolverFactory(resolver->expects_pac_bytes()),
211 resolver_(resolver) {}
213 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
214 return new ForwardingProxyResolver(resolver_);
218 ProxyResolver* resolver_;
221 // This factory returns new instances of BlockableProxyResolver.
222 class BlockableProxyResolverFactory : public ProxyResolverFactory {
224 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
226 virtual ~BlockableProxyResolverFactory() {
227 STLDeleteElements(&resolvers_);
230 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
231 BlockableProxyResolver* resolver = new BlockableProxyResolver;
232 resolvers_.push_back(resolver);
233 return new ForwardingProxyResolver(resolver);
236 std::vector<BlockableProxyResolver*> resolvers() {
241 std::vector<BlockableProxyResolver*> resolvers_;
244 TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
245 const size_t kNumThreads = 1u;
246 scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
247 MultiThreadedProxyResolver resolver(
248 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
252 EXPECT_TRUE(resolver.expects_pac_bytes());
254 // Call SetPacScriptByData() -- verify that it reaches the synchronous
256 TestCompletionCallback set_script_callback;
257 rv = resolver.SetPacScript(
258 ProxyResolverScriptData::FromUTF8("pac script bytes"),
259 set_script_callback.callback());
260 EXPECT_EQ(ERR_IO_PENDING, rv);
261 EXPECT_EQ(OK, set_script_callback.WaitForResult());
262 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
263 mock->last_script_data()->utf16());
266 TestCompletionCallback callback0;
267 CapturingBoundNetLog log0;
269 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
270 callback0.callback(), NULL, log0.bound());
271 EXPECT_EQ(ERR_IO_PENDING, rv);
273 // Wait for request 0 to finish.
274 rv = callback0.WaitForResult();
276 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
278 // The mock proxy resolver should have written 1 log entry. And
279 // on completion, this should have been copied into |log0|.
280 // We also have 1 log entry that was emitted by the
281 // MultiThreadedProxyResolver.
282 CapturingNetLog::CapturedEntryList entries0;
283 log0.GetEntries(&entries0);
285 ASSERT_EQ(2u, entries0.size());
286 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
288 // Start 3 more requests (request1 to request3).
290 TestCompletionCallback callback1;
292 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
293 callback1.callback(), NULL, BoundNetLog());
294 EXPECT_EQ(ERR_IO_PENDING, rv);
296 TestCompletionCallback callback2;
298 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
299 callback2.callback(), NULL, BoundNetLog());
300 EXPECT_EQ(ERR_IO_PENDING, rv);
302 TestCompletionCallback callback3;
304 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
305 callback3.callback(), NULL, BoundNetLog());
306 EXPECT_EQ(ERR_IO_PENDING, rv);
308 // Wait for the requests to finish (they must finish in the order they were
309 // started, which is what we check for from their magic return value)
311 rv = callback1.WaitForResult();
313 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
315 rv = callback2.WaitForResult();
317 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
319 rv = callback3.WaitForResult();
321 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
323 // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
325 EXPECT_EQ(0, mock->purge_count());
326 resolver.PurgeMemory();
327 // There is no way to get a callback directly when PurgeMemory() completes, so
328 // we queue up a dummy request after the PurgeMemory() call and wait until it
329 // finishes to ensure PurgeMemory() has had a chance to run.
330 TestCompletionCallback dummy_callback;
331 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
332 dummy_callback.callback());
333 EXPECT_EQ(OK, dummy_callback.WaitForResult());
334 EXPECT_EQ(1, mock->purge_count());
337 // Tests that the NetLog is updated to include the time the request was waiting
338 // to be scheduled to a thread.
339 TEST(MultiThreadedProxyResolverTest,
340 SingleThread_UpdatesNetLogWithThreadWait) {
341 const size_t kNumThreads = 1u;
342 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
343 MultiThreadedProxyResolver resolver(
344 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
348 // Initialize the resolver.
349 TestCompletionCallback init_callback;
350 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
351 init_callback.callback());
352 EXPECT_EQ(OK, init_callback.WaitForResult());
354 // Block the proxy resolver, so no request can complete.
358 ProxyResolver::RequestHandle request0;
359 TestCompletionCallback callback0;
361 CapturingBoundNetLog log0;
362 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
363 callback0.callback(), &request0, log0.bound());
364 EXPECT_EQ(ERR_IO_PENDING, rv);
366 // Start 2 more requests (request1 and request2).
368 TestCompletionCallback callback1;
370 CapturingBoundNetLog log1;
371 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
372 callback1.callback(), NULL, log1.bound());
373 EXPECT_EQ(ERR_IO_PENDING, rv);
375 ProxyResolver::RequestHandle request2;
376 TestCompletionCallback callback2;
378 CapturingBoundNetLog log2;
379 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
380 callback2.callback(), &request2, log2.bound());
381 EXPECT_EQ(ERR_IO_PENDING, rv);
383 // Unblock the worker thread so the requests can continue running.
384 mock->WaitUntilBlocked();
387 // Check that request 0 completed as expected.
388 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
389 // 1 entry from the mock proxy resolver.
390 EXPECT_EQ(0, callback0.WaitForResult());
391 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
393 CapturingNetLog::CapturedEntryList entries0;
394 log0.GetEntries(&entries0);
396 ASSERT_EQ(2u, entries0.size());
397 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
400 // Check that request 1 completed as expected.
401 EXPECT_EQ(1, callback1.WaitForResult());
402 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
404 CapturingNetLog::CapturedEntryList entries1;
405 log1.GetEntries(&entries1);
407 ASSERT_EQ(4u, entries1.size());
408 EXPECT_TRUE(LogContainsBeginEvent(
410 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
411 EXPECT_TRUE(LogContainsEndEvent(
413 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
415 // Check that request 2 completed as expected.
416 EXPECT_EQ(2, callback2.WaitForResult());
417 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
419 CapturingNetLog::CapturedEntryList entries2;
420 log2.GetEntries(&entries2);
422 ASSERT_EQ(4u, entries2.size());
423 EXPECT_TRUE(LogContainsBeginEvent(
425 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
426 EXPECT_TRUE(LogContainsEndEvent(
428 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
431 // Cancel a request which is in progress, and then cancel a request which
433 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
434 const size_t kNumThreads = 1u;
435 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
436 MultiThreadedProxyResolver resolver(
437 new ForwardingProxyResolverFactory(mock.get()),
442 // Initialize the resolver.
443 TestCompletionCallback init_callback;
444 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
445 init_callback.callback());
446 EXPECT_EQ(OK, init_callback.WaitForResult());
448 // Block the proxy resolver, so no request can complete.
452 ProxyResolver::RequestHandle request0;
453 TestCompletionCallback callback0;
455 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
456 callback0.callback(), &request0, BoundNetLog());
457 EXPECT_EQ(ERR_IO_PENDING, rv);
459 // Wait until requests 0 reaches the worker thread.
460 mock->WaitUntilBlocked();
462 // Start 3 more requests (request1 : request3).
464 TestCompletionCallback callback1;
466 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
467 callback1.callback(), NULL, BoundNetLog());
468 EXPECT_EQ(ERR_IO_PENDING, rv);
470 ProxyResolver::RequestHandle request2;
471 TestCompletionCallback callback2;
473 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
474 callback2.callback(), &request2, BoundNetLog());
475 EXPECT_EQ(ERR_IO_PENDING, rv);
477 TestCompletionCallback callback3;
479 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
480 callback3.callback(), NULL, BoundNetLog());
481 EXPECT_EQ(ERR_IO_PENDING, rv);
483 // Cancel request0 (inprogress) and request2 (pending).
484 resolver.CancelRequest(request0);
485 resolver.CancelRequest(request2);
487 // Unblock the worker thread so the requests can continue running.
490 // Wait for requests 1 and 3 to finish.
492 rv = callback1.WaitForResult();
494 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
496 rv = callback3.WaitForResult();
497 // Note that since request2 was cancelled before reaching the resolver,
498 // the request count is 2 and not 3 here.
500 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
502 // Requests 0 and 2 which were cancelled, hence their completion callbacks
503 // were never summoned.
504 EXPECT_FALSE(callback0.have_result());
505 EXPECT_FALSE(callback2.have_result());
508 // Test that deleting MultiThreadedProxyResolver while requests are
509 // outstanding cancels them (and doesn't leak anything).
510 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
511 const size_t kNumThreads = 1u;
512 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
513 scoped_ptr<MultiThreadedProxyResolver> resolver(
514 new MultiThreadedProxyResolver(
515 new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
519 // Initialize the resolver.
520 TestCompletionCallback init_callback;
521 rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
522 init_callback.callback());
523 EXPECT_EQ(OK, init_callback.WaitForResult());
525 // Block the proxy resolver, so no request can complete.
530 TestCompletionCallback callback0;
532 rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
533 callback0.callback(), NULL, BoundNetLog());
534 EXPECT_EQ(ERR_IO_PENDING, rv);
536 TestCompletionCallback callback1;
538 rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
539 callback1.callback(), NULL, BoundNetLog());
540 EXPECT_EQ(ERR_IO_PENDING, rv);
542 TestCompletionCallback callback2;
544 rv = resolver->GetProxyForURL(GURL("http://request2"), &results2,
545 callback2.callback(), NULL, BoundNetLog());
546 EXPECT_EQ(ERR_IO_PENDING, rv);
548 // Wait until request 0 reaches the worker thread.
549 mock->WaitUntilBlocked();
551 // Add some latency, to improve the chance that when
552 // MultiThreadedProxyResolver is deleted below we are still running inside
553 // of the worker thread. The test will pass regardless, so this race doesn't
554 // cause flakiness. However the destruction during execution is a more
555 // interesting case to test.
556 mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
558 // Unblock the worker thread and delete the underlying
559 // MultiThreadedProxyResolver immediately.
563 // Give any posted tasks a chance to run (in case there is badness).
564 base::MessageLoop::current()->RunUntilIdle();
566 // Check that none of the outstanding requests were completed.
567 EXPECT_FALSE(callback0.have_result());
568 EXPECT_FALSE(callback1.have_result());
569 EXPECT_FALSE(callback2.have_result());
572 // Cancel an outstanding call to SetPacScriptByData().
573 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
574 const size_t kNumThreads = 1u;
575 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
576 MultiThreadedProxyResolver resolver(
577 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
581 TestCompletionCallback set_pac_script_callback;
582 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
583 set_pac_script_callback.callback());
584 EXPECT_EQ(ERR_IO_PENDING, rv);
586 // Cancel the SetPacScriptByData request.
587 resolver.CancelSetPacScript();
589 // Start another SetPacScript request
590 TestCompletionCallback set_pac_script_callback2;
591 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
592 set_pac_script_callback2.callback());
593 EXPECT_EQ(ERR_IO_PENDING, rv);
595 // Wait for the initialization to complete.
597 rv = set_pac_script_callback2.WaitForResult();
599 EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
601 // The first SetPacScript callback should never have been completed.
602 EXPECT_FALSE(set_pac_script_callback.have_result());
605 // Tests setting the PAC script once, lazily creating new threads, and
606 // cancelling requests.
607 TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
608 const size_t kNumThreads = 3u;
609 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
610 MultiThreadedProxyResolver resolver(factory, kNumThreads);
614 EXPECT_TRUE(resolver.expects_pac_bytes());
616 // Call SetPacScriptByData() -- verify that it reaches the synchronous
618 TestCompletionCallback set_script_callback;
619 rv = resolver.SetPacScript(
620 ProxyResolverScriptData::FromUTF8("pac script bytes"),
621 set_script_callback.callback());
622 EXPECT_EQ(ERR_IO_PENDING, rv);
623 EXPECT_EQ(OK, set_script_callback.WaitForResult());
624 // One thread has been provisioned (i.e. one ProxyResolver was created).
625 ASSERT_EQ(1u, factory->resolvers().size());
626 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
627 factory->resolvers()[0]->last_script_data()->utf16());
629 const int kNumRequests = 9;
630 TestCompletionCallback callback[kNumRequests];
631 ProxyInfo results[kNumRequests];
632 ProxyResolver::RequestHandle request[kNumRequests];
634 // Start request 0 -- this should run on thread 0 as there is nothing else
635 // going on right now.
636 rv = resolver.GetProxyForURL(
637 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
639 EXPECT_EQ(ERR_IO_PENDING, rv);
641 // Wait for request 0 to finish.
642 rv = callback[0].WaitForResult();
644 EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
645 ASSERT_EQ(1u, factory->resolvers().size());
646 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
648 base::MessageLoop::current()->RunUntilIdle();
650 // We now start 8 requests in parallel -- this will cause the maximum of
651 // three threads to be provisioned (an additional two from what we already
654 for (int i = 1; i < kNumRequests; ++i) {
655 rv = resolver.GetProxyForURL(
656 GURL(base::StringPrintf("http://request%d", i)), &results[i],
657 callback[i].callback(), &request[i], BoundNetLog());
658 EXPECT_EQ(ERR_IO_PENDING, rv);
661 // We should now have a total of 3 threads, each with its own ProxyResolver
662 // that will get initialized with the same data. (We check this later since
663 // the assignment happens on the worker threads and may not have occurred
665 ASSERT_EQ(3u, factory->resolvers().size());
667 // Cancel 3 of the 8 oustanding requests.
668 resolver.CancelRequest(request[1]);
669 resolver.CancelRequest(request[3]);
670 resolver.CancelRequest(request[6]);
672 // Wait for the remaining requests to complete.
673 int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
674 for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
675 int request_index = kNonCancelledRequests[i];
676 EXPECT_GE(callback[request_index].WaitForResult(), 0);
679 // Check that the cancelled requests never invoked their callback.
680 EXPECT_FALSE(callback[1].have_result());
681 EXPECT_FALSE(callback[3].have_result());
682 EXPECT_FALSE(callback[6].have_result());
684 // We call SetPacScript again, solely to stop the current worker threads.
685 // (That way we can test to see the values observed by the synchronous
686 // resolvers in a non-racy manner).
687 TestCompletionCallback set_script_callback2;
688 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
689 set_script_callback2.callback());
690 EXPECT_EQ(ERR_IO_PENDING, rv);
691 EXPECT_EQ(OK, set_script_callback2.WaitForResult());
692 ASSERT_EQ(4u, factory->resolvers().size());
694 for (int i = 0; i < 3; ++i) {
696 ASCIIToUTF16("pac script bytes"),
697 factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
700 EXPECT_EQ(ASCIIToUTF16("xyz"),
701 factory->resolvers()[3]->last_script_data()->utf16());
703 // We don't know the exact ordering that requests ran on threads with,
704 // but we do know the total count that should have reached the threads.
705 // 8 total were submitted, and three were cancelled. Of the three that
706 // were cancelled, one of them (request 1) was cancelled after it had
707 // already been posted to the worker thread. So the resolvers will
708 // have seen 6 total (and 1 from the run prior).
709 ASSERT_EQ(4u, factory->resolvers().size());
711 for (int i = 0; i < 3; ++i) {
712 total_count += factory->resolvers()[i]->request_count();
714 EXPECT_EQ(7, total_count);
717 // Tests using two threads. The first request hangs the first thread. Checks
718 // that other requests are able to complete while this first request remains
720 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
721 const size_t kNumThreads = 2u;
722 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
723 MultiThreadedProxyResolver resolver(factory, kNumThreads);
727 EXPECT_TRUE(resolver.expects_pac_bytes());
729 // Initialize the resolver.
730 TestCompletionCallback set_script_callback;
731 rv = resolver.SetPacScript(
732 ProxyResolverScriptData::FromUTF8("pac script bytes"),
733 set_script_callback.callback());
734 EXPECT_EQ(ERR_IO_PENDING, rv);
735 EXPECT_EQ(OK, set_script_callback.WaitForResult());
736 // One thread has been provisioned (i.e. one ProxyResolver was created).
737 ASSERT_EQ(1u, factory->resolvers().size());
738 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
739 factory->resolvers()[0]->last_script_data()->utf16());
741 const int kNumRequests = 4;
742 TestCompletionCallback callback[kNumRequests];
743 ProxyInfo results[kNumRequests];
744 ProxyResolver::RequestHandle request[kNumRequests];
746 // Start a request that will block the first thread.
748 factory->resolvers()[0]->Block();
750 rv = resolver.GetProxyForURL(
751 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
754 EXPECT_EQ(ERR_IO_PENDING, rv);
755 factory->resolvers()[0]->WaitUntilBlocked();
757 // Start 3 more requests -- they should all be serviced by thread #2
758 // since thread #1 is blocked.
760 for (int i = 1; i < kNumRequests; ++i) {
761 rv = resolver.GetProxyForURL(
762 GURL(base::StringPrintf("http://request%d", i)),
763 &results[i], callback[i].callback(), &request[i], BoundNetLog());
764 EXPECT_EQ(ERR_IO_PENDING, rv);
767 // Wait for the three requests to complete (they should complete in FIFO
769 for (int i = 1; i < kNumRequests; ++i) {
770 EXPECT_EQ(i - 1, callback[i].WaitForResult());
773 // Unblock the first thread.
774 factory->resolvers()[0]->Unblock();
775 EXPECT_EQ(0, callback[0].WaitForResult());
777 // All in all, the first thread should have seen just 1 request. And the
778 // second thread 3 requests.
779 ASSERT_EQ(2u, factory->resolvers().size());
780 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
781 EXPECT_EQ(3, factory->resolvers()[1]->request_count());