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"
22 using base::ASCIIToUTF16
;
28 // A synchronous mock ProxyResolver implementation, which can be used in
29 // conjunction with MultiThreadedProxyResolver.
30 // - returns a single-item proxy list with the query's host.
31 class MockProxyResolver
: public ProxyResolver
{
34 : ProxyResolver(true /*expects_pac_bytes*/),
35 wrong_loop_(base::MessageLoop::current()),
38 // ProxyResolver implementation.
39 int GetProxyForURL(const GURL
& query_url
,
41 const CompletionCallback
& callback
,
42 RequestHandle
* request
,
43 const BoundNetLog
& net_log
) override
{
44 if (resolve_latency_
!= base::TimeDelta())
45 base::PlatformThread::Sleep(resolve_latency_
);
47 CheckIsOnWorkerThread();
49 EXPECT_TRUE(callback
.is_null());
50 EXPECT_TRUE(request
== NULL
);
52 // Write something into |net_log| (doesn't really have any meaning.)
53 net_log
.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT
);
55 results
->UseNamedProxy(query_url
.host());
57 // Return a success code which represents the request's order.
58 return request_count_
++;
61 void CancelRequest(RequestHandle request
) override
{ NOTREACHED(); }
63 LoadState
GetLoadState(RequestHandle request
) const override
{
65 return LOAD_STATE_IDLE
;
68 void CancelSetPacScript() override
{ NOTREACHED(); }
70 int SetPacScript(const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
71 const CompletionCallback
& callback
) override
{
72 CheckIsOnWorkerThread();
73 last_script_data_
= script_data
;
77 int request_count() const { return request_count_
; }
79 const ProxyResolverScriptData
* last_script_data() const {
80 return last_script_data_
.get();
83 void SetResolveLatency(base::TimeDelta latency
) {
84 resolve_latency_
= latency
;
88 void CheckIsOnWorkerThread() {
89 // We should be running on the worker thread -- while we don't know the
90 // message loop of MultiThreadedProxyResolver's worker thread, we do
91 // know that it is going to be distinct from the loop running the
92 // test, so at least make sure it isn't the main loop.
93 EXPECT_NE(base::MessageLoop::current(), wrong_loop_
);
96 base::MessageLoop
* wrong_loop_
;
98 scoped_refptr
<ProxyResolverScriptData
> last_script_data_
;
99 base::TimeDelta resolve_latency_
;
103 // A mock synchronous ProxyResolver which can be set to block upon reaching
105 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
106 // otherwise there will be a race on |should_block_| since it is
107 // read without any synchronization.
108 class BlockableProxyResolver
: public MockProxyResolver
{
110 BlockableProxyResolver()
111 : should_block_(false),
112 unblocked_(true, true),
113 blocked_(true, false) {
117 should_block_
= true;
122 should_block_
= false;
127 void WaitUntilBlocked() {
131 int GetProxyForURL(const GURL
& query_url
,
133 const CompletionCallback
& callback
,
134 RequestHandle
* request
,
135 const BoundNetLog
& net_log
) override
{
141 return MockProxyResolver::GetProxyForURL(
142 query_url
, results
, callback
, request
, net_log
);
147 base::WaitableEvent unblocked_
;
148 base::WaitableEvent blocked_
;
151 // ForwardingProxyResolver forwards all requests to |impl|.
152 class ForwardingProxyResolver
: public ProxyResolver
{
154 explicit ForwardingProxyResolver(ProxyResolver
* impl
)
155 : ProxyResolver(impl
->expects_pac_bytes()),
158 int GetProxyForURL(const GURL
& query_url
,
160 const CompletionCallback
& callback
,
161 RequestHandle
* request
,
162 const BoundNetLog
& net_log
) override
{
163 return impl_
->GetProxyForURL(
164 query_url
, results
, callback
, request
, net_log
);
167 void CancelRequest(RequestHandle request
) override
{
168 impl_
->CancelRequest(request
);
171 LoadState
GetLoadState(RequestHandle request
) const override
{
173 return LOAD_STATE_IDLE
;
176 void CancelSetPacScript() override
{ impl_
->CancelSetPacScript(); }
178 int SetPacScript(const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
179 const CompletionCallback
& callback
) override
{
180 return impl_
->SetPacScript(script_data
, callback
);
184 ProxyResolver
* impl_
;
187 // This factory returns ProxyResolvers that forward all requests to
189 class ForwardingProxyResolverFactory
: public ProxyResolverFactory
{
191 explicit ForwardingProxyResolverFactory(ProxyResolver
* resolver
)
192 : ProxyResolverFactory(resolver
->expects_pac_bytes()),
193 resolver_(resolver
) {}
195 ProxyResolver
* CreateProxyResolver() override
{
196 return new ForwardingProxyResolver(resolver_
);
200 ProxyResolver
* resolver_
;
203 // This factory returns new instances of BlockableProxyResolver.
204 class BlockableProxyResolverFactory
: public ProxyResolverFactory
{
206 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
208 ~BlockableProxyResolverFactory() override
{ STLDeleteElements(&resolvers_
); }
210 ProxyResolver
* CreateProxyResolver() override
{
211 BlockableProxyResolver
* resolver
= new BlockableProxyResolver
;
212 resolvers_
.push_back(resolver
);
213 return new ForwardingProxyResolver(resolver
);
216 std::vector
<BlockableProxyResolver
*> resolvers() {
221 std::vector
<BlockableProxyResolver
*> resolvers_
;
224 TEST(MultiThreadedProxyResolverTest
, SingleThread_Basic
) {
225 const size_t kNumThreads
= 1u;
226 scoped_ptr
<MockProxyResolver
> mock(new MockProxyResolver
);
227 MultiThreadedProxyResolver
resolver(
228 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
232 EXPECT_TRUE(resolver
.expects_pac_bytes());
234 // Call SetPacScriptByData() -- verify that it reaches the synchronous
236 TestCompletionCallback set_script_callback
;
237 rv
= resolver
.SetPacScript(
238 ProxyResolverScriptData::FromUTF8("pac script bytes"),
239 set_script_callback
.callback());
240 EXPECT_EQ(ERR_IO_PENDING
, rv
);
241 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
242 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
243 mock
->last_script_data()->utf16());
246 TestCompletionCallback callback0
;
247 CapturingBoundNetLog log0
;
249 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
250 callback0
.callback(), NULL
, log0
.bound());
251 EXPECT_EQ(ERR_IO_PENDING
, rv
);
253 // Wait for request 0 to finish.
254 rv
= callback0
.WaitForResult();
256 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
258 // The mock proxy resolver should have written 1 log entry. And
259 // on completion, this should have been copied into |log0|.
260 // We also have 1 log entry that was emitted by the
261 // MultiThreadedProxyResolver.
262 CapturingNetLog::CapturedEntryList entries0
;
263 log0
.GetEntries(&entries0
);
265 ASSERT_EQ(2u, entries0
.size());
266 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
, entries0
[0].type
);
268 // Start 3 more requests (request1 to request3).
270 TestCompletionCallback callback1
;
272 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
273 callback1
.callback(), NULL
, BoundNetLog());
274 EXPECT_EQ(ERR_IO_PENDING
, rv
);
276 TestCompletionCallback callback2
;
278 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
279 callback2
.callback(), NULL
, BoundNetLog());
280 EXPECT_EQ(ERR_IO_PENDING
, rv
);
282 TestCompletionCallback callback3
;
284 rv
= resolver
.GetProxyForURL(GURL("http://request3"), &results3
,
285 callback3
.callback(), NULL
, BoundNetLog());
286 EXPECT_EQ(ERR_IO_PENDING
, rv
);
288 // Wait for the requests to finish (they must finish in the order they were
289 // started, which is what we check for from their magic return value)
291 rv
= callback1
.WaitForResult();
293 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
295 rv
= callback2
.WaitForResult();
297 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
299 rv
= callback3
.WaitForResult();
301 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
304 // Tests that the NetLog is updated to include the time the request was waiting
305 // to be scheduled to a thread.
306 TEST(MultiThreadedProxyResolverTest
,
307 SingleThread_UpdatesNetLogWithThreadWait
) {
308 const size_t kNumThreads
= 1u;
309 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
310 MultiThreadedProxyResolver
resolver(
311 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
315 // Initialize the resolver.
316 TestCompletionCallback init_callback
;
317 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
318 init_callback
.callback());
319 EXPECT_EQ(OK
, init_callback
.WaitForResult());
321 // Block the proxy resolver, so no request can complete.
325 ProxyResolver::RequestHandle request0
;
326 TestCompletionCallback callback0
;
328 CapturingBoundNetLog log0
;
329 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
330 callback0
.callback(), &request0
, log0
.bound());
331 EXPECT_EQ(ERR_IO_PENDING
, rv
);
333 // Start 2 more requests (request1 and request2).
335 TestCompletionCallback callback1
;
337 CapturingBoundNetLog log1
;
338 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
339 callback1
.callback(), NULL
, log1
.bound());
340 EXPECT_EQ(ERR_IO_PENDING
, rv
);
342 ProxyResolver::RequestHandle request2
;
343 TestCompletionCallback callback2
;
345 CapturingBoundNetLog log2
;
346 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
347 callback2
.callback(), &request2
, log2
.bound());
348 EXPECT_EQ(ERR_IO_PENDING
, rv
);
350 // Unblock the worker thread so the requests can continue running.
351 mock
->WaitUntilBlocked();
354 // Check that request 0 completed as expected.
355 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
356 // 1 entry from the mock proxy resolver.
357 EXPECT_EQ(0, callback0
.WaitForResult());
358 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
360 CapturingNetLog::CapturedEntryList entries0
;
361 log0
.GetEntries(&entries0
);
363 ASSERT_EQ(2u, entries0
.size());
364 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
,
367 // Check that request 1 completed as expected.
368 EXPECT_EQ(1, callback1
.WaitForResult());
369 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
371 CapturingNetLog::CapturedEntryList entries1
;
372 log1
.GetEntries(&entries1
);
374 ASSERT_EQ(4u, entries1
.size());
375 EXPECT_TRUE(LogContainsBeginEvent(
377 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
378 EXPECT_TRUE(LogContainsEndEvent(
380 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
382 // Check that request 2 completed as expected.
383 EXPECT_EQ(2, callback2
.WaitForResult());
384 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
386 CapturingNetLog::CapturedEntryList entries2
;
387 log2
.GetEntries(&entries2
);
389 ASSERT_EQ(4u, entries2
.size());
390 EXPECT_TRUE(LogContainsBeginEvent(
392 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
393 EXPECT_TRUE(LogContainsEndEvent(
395 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
398 // Cancel a request which is in progress, and then cancel a request which
400 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelRequest
) {
401 const size_t kNumThreads
= 1u;
402 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
403 MultiThreadedProxyResolver
resolver(
404 new ForwardingProxyResolverFactory(mock
.get()),
409 // Initialize the resolver.
410 TestCompletionCallback init_callback
;
411 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
412 init_callback
.callback());
413 EXPECT_EQ(OK
, init_callback
.WaitForResult());
415 // Block the proxy resolver, so no request can complete.
419 ProxyResolver::RequestHandle request0
;
420 TestCompletionCallback callback0
;
422 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
423 callback0
.callback(), &request0
, BoundNetLog());
424 EXPECT_EQ(ERR_IO_PENDING
, rv
);
426 // Wait until requests 0 reaches the worker thread.
427 mock
->WaitUntilBlocked();
429 // Start 3 more requests (request1 : request3).
431 TestCompletionCallback callback1
;
433 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
434 callback1
.callback(), NULL
, BoundNetLog());
435 EXPECT_EQ(ERR_IO_PENDING
, rv
);
437 ProxyResolver::RequestHandle request2
;
438 TestCompletionCallback callback2
;
440 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
441 callback2
.callback(), &request2
, BoundNetLog());
442 EXPECT_EQ(ERR_IO_PENDING
, rv
);
444 TestCompletionCallback callback3
;
446 rv
= resolver
.GetProxyForURL(GURL("http://request3"), &results3
,
447 callback3
.callback(), NULL
, BoundNetLog());
448 EXPECT_EQ(ERR_IO_PENDING
, rv
);
450 // Cancel request0 (inprogress) and request2 (pending).
451 resolver
.CancelRequest(request0
);
452 resolver
.CancelRequest(request2
);
454 // Unblock the worker thread so the requests can continue running.
457 // Wait for requests 1 and 3 to finish.
459 rv
= callback1
.WaitForResult();
461 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
463 rv
= callback3
.WaitForResult();
464 // Note that since request2 was cancelled before reaching the resolver,
465 // the request count is 2 and not 3 here.
467 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
469 // Requests 0 and 2 which were cancelled, hence their completion callbacks
470 // were never summoned.
471 EXPECT_FALSE(callback0
.have_result());
472 EXPECT_FALSE(callback2
.have_result());
475 // Test that deleting MultiThreadedProxyResolver while requests are
476 // outstanding cancels them (and doesn't leak anything).
477 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelRequestByDeleting
) {
478 const size_t kNumThreads
= 1u;
479 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
480 scoped_ptr
<MultiThreadedProxyResolver
> resolver(
481 new MultiThreadedProxyResolver(
482 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
));
486 // Initialize the resolver.
487 TestCompletionCallback init_callback
;
488 rv
= resolver
->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
489 init_callback
.callback());
490 EXPECT_EQ(OK
, init_callback
.WaitForResult());
492 // Block the proxy resolver, so no request can complete.
497 TestCompletionCallback callback0
;
499 rv
= resolver
->GetProxyForURL(GURL("http://request0"), &results0
,
500 callback0
.callback(), NULL
, BoundNetLog());
501 EXPECT_EQ(ERR_IO_PENDING
, rv
);
503 TestCompletionCallback callback1
;
505 rv
= resolver
->GetProxyForURL(GURL("http://request1"), &results1
,
506 callback1
.callback(), NULL
, BoundNetLog());
507 EXPECT_EQ(ERR_IO_PENDING
, rv
);
509 TestCompletionCallback callback2
;
511 rv
= resolver
->GetProxyForURL(GURL("http://request2"), &results2
,
512 callback2
.callback(), NULL
, BoundNetLog());
513 EXPECT_EQ(ERR_IO_PENDING
, rv
);
515 // Wait until request 0 reaches the worker thread.
516 mock
->WaitUntilBlocked();
518 // Add some latency, to improve the chance that when
519 // MultiThreadedProxyResolver is deleted below we are still running inside
520 // of the worker thread. The test will pass regardless, so this race doesn't
521 // cause flakiness. However the destruction during execution is a more
522 // interesting case to test.
523 mock
->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
525 // Unblock the worker thread and delete the underlying
526 // MultiThreadedProxyResolver immediately.
530 // Give any posted tasks a chance to run (in case there is badness).
531 base::MessageLoop::current()->RunUntilIdle();
533 // Check that none of the outstanding requests were completed.
534 EXPECT_FALSE(callback0
.have_result());
535 EXPECT_FALSE(callback1
.have_result());
536 EXPECT_FALSE(callback2
.have_result());
539 // Cancel an outstanding call to SetPacScriptByData().
540 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelSetPacScript
) {
541 const size_t kNumThreads
= 1u;
542 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
543 MultiThreadedProxyResolver
resolver(
544 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
548 TestCompletionCallback set_pac_script_callback
;
549 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
550 set_pac_script_callback
.callback());
551 EXPECT_EQ(ERR_IO_PENDING
, rv
);
553 // Cancel the SetPacScriptByData request.
554 resolver
.CancelSetPacScript();
556 // Start another SetPacScript request
557 TestCompletionCallback set_pac_script_callback2
;
558 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
559 set_pac_script_callback2
.callback());
560 EXPECT_EQ(ERR_IO_PENDING
, rv
);
562 // Wait for the initialization to complete.
564 rv
= set_pac_script_callback2
.WaitForResult();
566 EXPECT_EQ(ASCIIToUTF16("data2"), mock
->last_script_data()->utf16());
568 // The first SetPacScript callback should never have been completed.
569 EXPECT_FALSE(set_pac_script_callback
.have_result());
572 // Tests setting the PAC script once, lazily creating new threads, and
573 // cancelling requests.
574 TEST(MultiThreadedProxyResolverTest
, ThreeThreads_Basic
) {
575 const size_t kNumThreads
= 3u;
576 BlockableProxyResolverFactory
* factory
= new BlockableProxyResolverFactory
;
577 MultiThreadedProxyResolver
resolver(factory
, kNumThreads
);
581 EXPECT_TRUE(resolver
.expects_pac_bytes());
583 // Call SetPacScriptByData() -- verify that it reaches the synchronous
585 TestCompletionCallback set_script_callback
;
586 rv
= resolver
.SetPacScript(
587 ProxyResolverScriptData::FromUTF8("pac script bytes"),
588 set_script_callback
.callback());
589 EXPECT_EQ(ERR_IO_PENDING
, rv
);
590 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
591 // One thread has been provisioned (i.e. one ProxyResolver was created).
592 ASSERT_EQ(1u, factory
->resolvers().size());
593 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
594 factory
->resolvers()[0]->last_script_data()->utf16());
596 const int kNumRequests
= 9;
597 TestCompletionCallback callback
[kNumRequests
];
598 ProxyInfo results
[kNumRequests
];
599 ProxyResolver::RequestHandle request
[kNumRequests
];
601 // Start request 0 -- this should run on thread 0 as there is nothing else
602 // going on right now.
603 rv
= resolver
.GetProxyForURL(
604 GURL("http://request0"), &results
[0], callback
[0].callback(), &request
[0],
606 EXPECT_EQ(ERR_IO_PENDING
, rv
);
608 // Wait for request 0 to finish.
609 rv
= callback
[0].WaitForResult();
611 EXPECT_EQ("PROXY request0:80", results
[0].ToPacString());
612 ASSERT_EQ(1u, factory
->resolvers().size());
613 EXPECT_EQ(1, factory
->resolvers()[0]->request_count());
615 base::MessageLoop::current()->RunUntilIdle();
617 // We now start 8 requests in parallel -- this will cause the maximum of
618 // three threads to be provisioned (an additional two from what we already
621 for (int i
= 1; i
< kNumRequests
; ++i
) {
622 rv
= resolver
.GetProxyForURL(
623 GURL(base::StringPrintf("http://request%d", i
)), &results
[i
],
624 callback
[i
].callback(), &request
[i
], BoundNetLog());
625 EXPECT_EQ(ERR_IO_PENDING
, rv
);
628 // We should now have a total of 3 threads, each with its own ProxyResolver
629 // that will get initialized with the same data. (We check this later since
630 // the assignment happens on the worker threads and may not have occurred
632 ASSERT_EQ(3u, factory
->resolvers().size());
634 // Cancel 3 of the 8 oustanding requests.
635 resolver
.CancelRequest(request
[1]);
636 resolver
.CancelRequest(request
[3]);
637 resolver
.CancelRequest(request
[6]);
639 // Wait for the remaining requests to complete.
640 int kNonCancelledRequests
[] = {2, 4, 5, 7, 8};
641 for (size_t i
= 0; i
< arraysize(kNonCancelledRequests
); ++i
) {
642 int request_index
= kNonCancelledRequests
[i
];
643 EXPECT_GE(callback
[request_index
].WaitForResult(), 0);
646 // Check that the cancelled requests never invoked their callback.
647 EXPECT_FALSE(callback
[1].have_result());
648 EXPECT_FALSE(callback
[3].have_result());
649 EXPECT_FALSE(callback
[6].have_result());
651 // We call SetPacScript again, solely to stop the current worker threads.
652 // (That way we can test to see the values observed by the synchronous
653 // resolvers in a non-racy manner).
654 TestCompletionCallback set_script_callback2
;
655 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
656 set_script_callback2
.callback());
657 EXPECT_EQ(ERR_IO_PENDING
, rv
);
658 EXPECT_EQ(OK
, set_script_callback2
.WaitForResult());
659 ASSERT_EQ(4u, factory
->resolvers().size());
661 for (int i
= 0; i
< 3; ++i
) {
663 ASCIIToUTF16("pac script bytes"),
664 factory
->resolvers()[i
]->last_script_data()->utf16()) << "i=" << i
;
667 EXPECT_EQ(ASCIIToUTF16("xyz"),
668 factory
->resolvers()[3]->last_script_data()->utf16());
670 // We don't know the exact ordering that requests ran on threads with,
671 // but we do know the total count that should have reached the threads.
672 // 8 total were submitted, and three were cancelled. Of the three that
673 // were cancelled, one of them (request 1) was cancelled after it had
674 // already been posted to the worker thread. So the resolvers will
675 // have seen 6 total (and 1 from the run prior).
676 ASSERT_EQ(4u, factory
->resolvers().size());
678 for (int i
= 0; i
< 3; ++i
) {
679 total_count
+= factory
->resolvers()[i
]->request_count();
681 EXPECT_EQ(7, total_count
);
684 // Tests using two threads. The first request hangs the first thread. Checks
685 // that other requests are able to complete while this first request remains
687 TEST(MultiThreadedProxyResolverTest
, OneThreadBlocked
) {
688 const size_t kNumThreads
= 2u;
689 BlockableProxyResolverFactory
* factory
= new BlockableProxyResolverFactory
;
690 MultiThreadedProxyResolver
resolver(factory
, kNumThreads
);
694 EXPECT_TRUE(resolver
.expects_pac_bytes());
696 // Initialize the resolver.
697 TestCompletionCallback set_script_callback
;
698 rv
= resolver
.SetPacScript(
699 ProxyResolverScriptData::FromUTF8("pac script bytes"),
700 set_script_callback
.callback());
701 EXPECT_EQ(ERR_IO_PENDING
, rv
);
702 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
703 // One thread has been provisioned (i.e. one ProxyResolver was created).
704 ASSERT_EQ(1u, factory
->resolvers().size());
705 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
706 factory
->resolvers()[0]->last_script_data()->utf16());
708 const int kNumRequests
= 4;
709 TestCompletionCallback callback
[kNumRequests
];
710 ProxyInfo results
[kNumRequests
];
711 ProxyResolver::RequestHandle request
[kNumRequests
];
713 // Start a request that will block the first thread.
715 factory
->resolvers()[0]->Block();
717 rv
= resolver
.GetProxyForURL(
718 GURL("http://request0"), &results
[0], callback
[0].callback(), &request
[0],
721 EXPECT_EQ(ERR_IO_PENDING
, rv
);
722 factory
->resolvers()[0]->WaitUntilBlocked();
724 // Start 3 more requests -- they should all be serviced by thread #2
725 // since thread #1 is blocked.
727 for (int i
= 1; i
< kNumRequests
; ++i
) {
728 rv
= resolver
.GetProxyForURL(
729 GURL(base::StringPrintf("http://request%d", i
)),
730 &results
[i
], callback
[i
].callback(), &request
[i
], BoundNetLog());
731 EXPECT_EQ(ERR_IO_PENDING
, rv
);
734 // Wait for the three requests to complete (they should complete in FIFO
736 for (int i
= 1; i
< kNumRequests
; ++i
) {
737 EXPECT_EQ(i
- 1, callback
[i
].WaitForResult());
740 // Unblock the first thread.
741 factory
->resolvers()[0]->Unblock();
742 EXPECT_EQ(0, callback
[0].WaitForResult());
744 // All in all, the first thread should have seen just 1 request. And the
745 // second thread 3 requests.
746 ASSERT_EQ(2u, factory
->resolvers().size());
747 EXPECT_EQ(1, factory
->resolvers()[0]->request_count());
748 EXPECT_EQ(3, factory
->resolvers()[1]->request_count());