1// Copyright (c) 2010 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 "net/proxy/multi_threaded_proxy_resolver.h" 6 7#include "base/message_loop.h" 8#include "base/stl_util-inl.h" 9#include "base/string_util.h" 10#include "base/stringprintf.h" 11#include "base/threading/platform_thread.h" 12#include "base/utf_string_conversions.h" 13#include "base/synchronization/waitable_event.h" 14#include "googleurl/src/gurl.h" 15#include "net/base/net_errors.h" 16#include "net/base/net_log.h" 17#include "net/base/net_log_unittest.h" 18#include "net/base/test_completion_callback.h" 19#include "net/proxy/proxy_info.h" 20#include "testing/gtest/include/gtest/gtest.h" 21 22namespace net { 23 24namespace { 25 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. 29class MockProxyResolver : public ProxyResolver { 30 public: 31 MockProxyResolver() 32 : ProxyResolver(true /*expects_pac_bytes*/), 33 wrong_loop_(MessageLoop::current()), 34 request_count_(0), 35 purge_count_(0), 36 resolve_latency_ms_(0) {} 37 38 // ProxyResolver implementation: 39 virtual int GetProxyForURL(const GURL& query_url, 40 ProxyInfo* results, 41 CompletionCallback* callback, 42 RequestHandle* request, 43 const BoundNetLog& net_log) { 44 if (resolve_latency_ms_) 45 base::PlatformThread::Sleep(resolve_latency_ms_); 46 47 CheckIsOnWorkerThread(); 48 49 EXPECT_TRUE(callback == NULL); 50 EXPECT_TRUE(request == NULL); 51 52 // Write something into |net_log| (doesn't really have any meaning.) 53 net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE, NULL); 54 55 results->UseNamedProxy(query_url.host()); 56 57 // Return a success code which represents the request's order. 58 return request_count_++; 59 } 60 61 virtual void CancelRequest(RequestHandle request) { 62 NOTREACHED(); 63 } 64 65 virtual void CancelSetPacScript() { 66 NOTREACHED(); 67 } 68 69 virtual int SetPacScript( 70 const scoped_refptr<ProxyResolverScriptData>& script_data, 71 CompletionCallback* callback) { 72 CheckIsOnWorkerThread(); 73 last_script_data_ = script_data; 74 return OK; 75 } 76 77 virtual void PurgeMemory() { 78 CheckIsOnWorkerThread(); 79 ++purge_count_; 80 } 81 82 int purge_count() const { return purge_count_; } 83 int request_count() const { return request_count_; } 84 85 const ProxyResolverScriptData* last_script_data() const { 86 return last_script_data_; 87 } 88 89 void SetResolveLatency(int latency_ms) { 90 resolve_latency_ms_ = latency_ms; 91 } 92 93 private: 94 void CheckIsOnWorkerThread() { 95 // We should be running on the worker thread -- while we don't know the 96 // message loop of MultiThreadedProxyResolver's worker thread, we do 97 // know that it is going to be distinct from the loop running the 98 // test, so at least make sure it isn't the main loop. 99 EXPECT_NE(MessageLoop::current(), wrong_loop_); 100 } 101 102 MessageLoop* wrong_loop_; 103 int request_count_; 104 int purge_count_; 105 scoped_refptr<ProxyResolverScriptData> last_script_data_; 106 int resolve_latency_ms_; 107}; 108 109 110// A mock synchronous ProxyResolver which can be set to block upon reaching 111// GetProxyForURL(). 112// TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(), 113// otherwise there will be a race on |should_block_| since it is 114// read without any synchronization. 115class BlockableProxyResolver : public MockProxyResolver { 116 public: 117 BlockableProxyResolver() 118 : should_block_(false), 119 unblocked_(true, true), 120 blocked_(true, false) { 121 } 122 123 void Block() { 124 should_block_ = true; 125 unblocked_.Reset(); 126 } 127 128 void Unblock() { 129 should_block_ = false; 130 blocked_.Reset(); 131 unblocked_.Signal(); 132 } 133 134 void WaitUntilBlocked() { 135 blocked_.Wait(); 136 } 137 138 virtual int GetProxyForURL(const GURL& query_url, 139 ProxyInfo* results, 140 CompletionCallback* callback, 141 RequestHandle* request, 142 const BoundNetLog& net_log) { 143 if (should_block_) { 144 blocked_.Signal(); 145 unblocked_.Wait(); 146 } 147 148 return MockProxyResolver::GetProxyForURL( 149 query_url, results, callback, request, net_log); 150 } 151 152 private: 153 bool should_block_; 154 base::WaitableEvent unblocked_; 155 base::WaitableEvent blocked_; 156}; 157 158// ForwardingProxyResolver forwards all requests to |impl|. 159class ForwardingProxyResolver : public ProxyResolver { 160 public: 161 explicit ForwardingProxyResolver(ProxyResolver* impl) 162 : ProxyResolver(impl->expects_pac_bytes()), 163 impl_(impl) {} 164 165 virtual int GetProxyForURL(const GURL& query_url, 166 ProxyInfo* results, 167 CompletionCallback* callback, 168 RequestHandle* request, 169 const BoundNetLog& net_log) { 170 return impl_->GetProxyForURL( 171 query_url, results, callback, request, net_log); 172 } 173 174 virtual void CancelRequest(RequestHandle request) { 175 impl_->CancelRequest(request); 176 } 177 178 virtual void CancelSetPacScript() { 179 impl_->CancelSetPacScript(); 180 } 181 182 virtual int SetPacScript( 183 const scoped_refptr<ProxyResolverScriptData>& script_data, 184 CompletionCallback* callback) { 185 return impl_->SetPacScript(script_data, callback); 186 } 187 188 virtual void PurgeMemory() { 189 impl_->PurgeMemory(); 190 } 191 192 private: 193 ProxyResolver* impl_; 194}; 195 196// This factory returns ProxyResolvers that forward all requests to 197// |resolver|. 198class ForwardingProxyResolverFactory : public ProxyResolverFactory { 199 public: 200 explicit ForwardingProxyResolverFactory(ProxyResolver* resolver) 201 : ProxyResolverFactory(resolver->expects_pac_bytes()), 202 resolver_(resolver) {} 203 204 virtual ProxyResolver* CreateProxyResolver() { 205 return new ForwardingProxyResolver(resolver_); 206 } 207 208 private: 209 ProxyResolver* resolver_; 210}; 211 212// This factory returns new instances of BlockableProxyResolver. 213class BlockableProxyResolverFactory : public ProxyResolverFactory { 214 public: 215 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {} 216 217 ~BlockableProxyResolverFactory() { 218 STLDeleteElements(&resolvers_); 219 } 220 221 virtual ProxyResolver* CreateProxyResolver() { 222 BlockableProxyResolver* resolver = new BlockableProxyResolver; 223 resolvers_.push_back(resolver); 224 return new ForwardingProxyResolver(resolver); 225 } 226 227 std::vector<BlockableProxyResolver*> resolvers() { 228 return resolvers_; 229 } 230 231 private: 232 std::vector<BlockableProxyResolver*> resolvers_; 233}; 234 235TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) { 236 const size_t kNumThreads = 1u; 237 scoped_ptr<MockProxyResolver> mock(new MockProxyResolver); 238 MultiThreadedProxyResolver resolver( 239 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 240 241 int rv; 242 243 EXPECT_TRUE(resolver.expects_pac_bytes()); 244 245 // Call SetPacScriptByData() -- verify that it reaches the synchronous 246 // resolver. 247 TestCompletionCallback set_script_callback; 248 rv = resolver.SetPacScript( 249 ProxyResolverScriptData::FromUTF8("pac script bytes"), 250 &set_script_callback); 251 EXPECT_EQ(ERR_IO_PENDING, rv); 252 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 253 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 254 mock->last_script_data()->utf16()); 255 256 // Start request 0. 257 TestCompletionCallback callback0; 258 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); 259 ProxyInfo results0; 260 rv = resolver.GetProxyForURL( 261 GURL("http://request0"), &results0, &callback0, NULL, log0.bound()); 262 EXPECT_EQ(ERR_IO_PENDING, rv); 263 264 // Wait for request 0 to finish. 265 rv = callback0.WaitForResult(); 266 EXPECT_EQ(0, rv); 267 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); 268 269 // The mock proxy resolver should have written 1 log entry. And 270 // on completion, this should have been copied into |log0|. 271 // We also have 1 log entry that was emitted by the 272 // MultiThreadedProxyResolver. 273 net::CapturingNetLog::EntryList entries0; 274 log0.GetEntries(&entries0); 275 276 ASSERT_EQ(2u, entries0.size()); 277 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type); 278 279 // Start 3 more requests (request1 to request3). 280 281 TestCompletionCallback callback1; 282 ProxyInfo results1; 283 rv = resolver.GetProxyForURL( 284 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); 285 EXPECT_EQ(ERR_IO_PENDING, rv); 286 287 TestCompletionCallback callback2; 288 ProxyInfo results2; 289 rv = resolver.GetProxyForURL( 290 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); 291 EXPECT_EQ(ERR_IO_PENDING, rv); 292 293 TestCompletionCallback callback3; 294 ProxyInfo results3; 295 rv = resolver.GetProxyForURL( 296 GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog()); 297 EXPECT_EQ(ERR_IO_PENDING, rv); 298 299 // Wait for the requests to finish (they must finish in the order they were 300 // started, which is what we check for from their magic return value) 301 302 rv = callback1.WaitForResult(); 303 EXPECT_EQ(1, rv); 304 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 305 306 rv = callback2.WaitForResult(); 307 EXPECT_EQ(2, rv); 308 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); 309 310 rv = callback3.WaitForResult(); 311 EXPECT_EQ(3, rv); 312 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); 313 314 // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the 315 // right thread. 316 EXPECT_EQ(0, mock->purge_count()); 317 resolver.PurgeMemory(); 318 // There is no way to get a callback directly when PurgeMemory() completes, so 319 // we queue up a dummy request after the PurgeMemory() call and wait until it 320 // finishes to ensure PurgeMemory() has had a chance to run. 321 TestCompletionCallback dummy_callback; 322 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"), 323 &dummy_callback); 324 EXPECT_EQ(OK, dummy_callback.WaitForResult()); 325 EXPECT_EQ(1, mock->purge_count()); 326} 327 328// Tests that the NetLog is updated to include the time the request was waiting 329// to be scheduled to a thread. 330TEST(MultiThreadedProxyResolverTest, 331 SingleThread_UpdatesNetLogWithThreadWait) { 332 const size_t kNumThreads = 1u; 333 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 334 MultiThreadedProxyResolver resolver( 335 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 336 337 int rv; 338 339 // Initialize the resolver. 340 TestCompletionCallback init_callback; 341 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 342 &init_callback); 343 EXPECT_EQ(OK, init_callback.WaitForResult()); 344 345 // Block the proxy resolver, so no request can complete. 346 mock->Block(); 347 348 // Start request 0. 349 ProxyResolver::RequestHandle request0; 350 TestCompletionCallback callback0; 351 ProxyInfo results0; 352 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); 353 rv = resolver.GetProxyForURL( 354 GURL("http://request0"), &results0, &callback0, &request0, log0.bound()); 355 EXPECT_EQ(ERR_IO_PENDING, rv); 356 357 // Start 2 more requests (request1 and request2). 358 359 TestCompletionCallback callback1; 360 ProxyInfo results1; 361 CapturingBoundNetLog log1(CapturingNetLog::kUnbounded); 362 rv = resolver.GetProxyForURL( 363 GURL("http://request1"), &results1, &callback1, NULL, log1.bound()); 364 EXPECT_EQ(ERR_IO_PENDING, rv); 365 366 ProxyResolver::RequestHandle request2; 367 TestCompletionCallback callback2; 368 ProxyInfo results2; 369 CapturingBoundNetLog log2(CapturingNetLog::kUnbounded); 370 rv = resolver.GetProxyForURL( 371 GURL("http://request2"), &results2, &callback2, &request2, log2.bound()); 372 EXPECT_EQ(ERR_IO_PENDING, rv); 373 374 // Unblock the worker thread so the requests can continue running. 375 mock->WaitUntilBlocked(); 376 mock->Unblock(); 377 378 // Check that request 0 completed as expected. 379 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and 380 // 1 entry from the mock proxy resolver. 381 EXPECT_EQ(0, callback0.WaitForResult()); 382 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); 383 384 net::CapturingNetLog::EntryList entries0; 385 log0.GetEntries(&entries0); 386 387 ASSERT_EQ(2u, entries0.size()); 388 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, 389 entries0[0].type); 390 391 // Check that request 1 completed as expected. 392 EXPECT_EQ(1, callback1.WaitForResult()); 393 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 394 395 net::CapturingNetLog::EntryList entries1; 396 log1.GetEntries(&entries1); 397 398 ASSERT_EQ(4u, entries1.size()); 399 EXPECT_TRUE(LogContainsBeginEvent( 400 entries1, 0, 401 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 402 EXPECT_TRUE(LogContainsEndEvent( 403 entries1, 1, 404 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 405 406 // Check that request 2 completed as expected. 407 EXPECT_EQ(2, callback2.WaitForResult()); 408 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); 409 410 net::CapturingNetLog::EntryList entries2; 411 log2.GetEntries(&entries2); 412 413 ASSERT_EQ(4u, entries2.size()); 414 EXPECT_TRUE(LogContainsBeginEvent( 415 entries2, 0, 416 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 417 EXPECT_TRUE(LogContainsEndEvent( 418 entries2, 1, 419 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 420} 421 422// Cancel a request which is in progress, and then cancel a request which 423// is pending. 424TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) { 425 const size_t kNumThreads = 1u; 426 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 427 MultiThreadedProxyResolver resolver( 428 new ForwardingProxyResolverFactory(mock.get()), 429 kNumThreads); 430 431 int rv; 432 433 // Initialize the resolver. 434 TestCompletionCallback init_callback; 435 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 436 &init_callback); 437 EXPECT_EQ(OK, init_callback.WaitForResult()); 438 439 // Block the proxy resolver, so no request can complete. 440 mock->Block(); 441 442 // Start request 0. 443 ProxyResolver::RequestHandle request0; 444 TestCompletionCallback callback0; 445 ProxyInfo results0; 446 rv = resolver.GetProxyForURL( 447 GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog()); 448 EXPECT_EQ(ERR_IO_PENDING, rv); 449 450 // Wait until requests 0 reaches the worker thread. 451 mock->WaitUntilBlocked(); 452 453 // Start 3 more requests (request1 : request3). 454 455 TestCompletionCallback callback1; 456 ProxyInfo results1; 457 rv = resolver.GetProxyForURL( 458 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); 459 EXPECT_EQ(ERR_IO_PENDING, rv); 460 461 ProxyResolver::RequestHandle request2; 462 TestCompletionCallback callback2; 463 ProxyInfo results2; 464 rv = resolver.GetProxyForURL( 465 GURL("http://request2"), &results2, &callback2, &request2, BoundNetLog()); 466 EXPECT_EQ(ERR_IO_PENDING, rv); 467 468 TestCompletionCallback callback3; 469 ProxyInfo results3; 470 rv = resolver.GetProxyForURL( 471 GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog()); 472 EXPECT_EQ(ERR_IO_PENDING, rv); 473 474 // Cancel request0 (inprogress) and request2 (pending). 475 resolver.CancelRequest(request0); 476 resolver.CancelRequest(request2); 477 478 // Unblock the worker thread so the requests can continue running. 479 mock->Unblock(); 480 481 // Wait for requests 1 and 3 to finish. 482 483 rv = callback1.WaitForResult(); 484 EXPECT_EQ(1, rv); 485 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 486 487 rv = callback3.WaitForResult(); 488 // Note that since request2 was cancelled before reaching the resolver, 489 // the request count is 2 and not 3 here. 490 EXPECT_EQ(2, rv); 491 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); 492 493 // Requests 0 and 2 which were cancelled, hence their completion callbacks 494 // were never summoned. 495 EXPECT_FALSE(callback0.have_result()); 496 EXPECT_FALSE(callback2.have_result()); 497} 498 499// Test that deleting MultiThreadedProxyResolver while requests are 500// outstanding cancels them (and doesn't leak anything). 501TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) { 502 const size_t kNumThreads = 1u; 503 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 504 scoped_ptr<MultiThreadedProxyResolver> resolver( 505 new MultiThreadedProxyResolver( 506 new ForwardingProxyResolverFactory(mock.get()), kNumThreads)); 507 508 int rv; 509 510 // Initialize the resolver. 511 TestCompletionCallback init_callback; 512 rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 513 &init_callback); 514 EXPECT_EQ(OK, init_callback.WaitForResult()); 515 516 // Block the proxy resolver, so no request can complete. 517 mock->Block(); 518 519 // Start 3 requests. 520 521 TestCompletionCallback callback0; 522 ProxyInfo results0; 523 rv = resolver->GetProxyForURL( 524 GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog()); 525 EXPECT_EQ(ERR_IO_PENDING, rv); 526 527 TestCompletionCallback callback1; 528 ProxyInfo results1; 529 rv = resolver->GetProxyForURL( 530 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); 531 EXPECT_EQ(ERR_IO_PENDING, rv); 532 533 TestCompletionCallback callback2; 534 ProxyInfo results2; 535 rv = resolver->GetProxyForURL( 536 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); 537 EXPECT_EQ(ERR_IO_PENDING, rv); 538 539 // Wait until request 0 reaches the worker thread. 540 mock->WaitUntilBlocked(); 541 542 // Add some latency, to improve the chance that when 543 // MultiThreadedProxyResolver is deleted below we are still running inside 544 // of the worker thread. The test will pass regardless, so this race doesn't 545 // cause flakiness. However the destruction during execution is a more 546 // interesting case to test. 547 mock->SetResolveLatency(100); 548 549 // Unblock the worker thread and delete the underlying 550 // MultiThreadedProxyResolver immediately. 551 mock->Unblock(); 552 resolver.reset(); 553 554 // Give any posted tasks a chance to run (in case there is badness). 555 MessageLoop::current()->RunAllPending(); 556 557 // Check that none of the outstanding requests were completed. 558 EXPECT_FALSE(callback0.have_result()); 559 EXPECT_FALSE(callback1.have_result()); 560 EXPECT_FALSE(callback2.have_result()); 561} 562 563// Cancel an outstanding call to SetPacScriptByData(). 564TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) { 565 const size_t kNumThreads = 1u; 566 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 567 MultiThreadedProxyResolver resolver( 568 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 569 570 int rv; 571 572 TestCompletionCallback set_pac_script_callback; 573 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"), 574 &set_pac_script_callback); 575 EXPECT_EQ(ERR_IO_PENDING, rv); 576 577 // Cancel the SetPacScriptByData request. 578 resolver.CancelSetPacScript(); 579 580 // Start another SetPacScript request 581 TestCompletionCallback set_pac_script_callback2; 582 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"), 583 &set_pac_script_callback2); 584 EXPECT_EQ(ERR_IO_PENDING, rv); 585 586 // Wait for the initialization to complete. 587 588 rv = set_pac_script_callback2.WaitForResult(); 589 EXPECT_EQ(0, rv); 590 EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16()); 591 592 // The first SetPacScript callback should never have been completed. 593 EXPECT_FALSE(set_pac_script_callback.have_result()); 594} 595 596// Tests setting the PAC script once, lazily creating new threads, and 597// cancelling requests. 598TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) { 599 const size_t kNumThreads = 3u; 600 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory; 601 MultiThreadedProxyResolver resolver(factory, kNumThreads); 602 603 int rv; 604 605 EXPECT_TRUE(resolver.expects_pac_bytes()); 606 607 // Call SetPacScriptByData() -- verify that it reaches the synchronous 608 // resolver. 609 TestCompletionCallback set_script_callback; 610 rv = resolver.SetPacScript( 611 ProxyResolverScriptData::FromUTF8("pac script bytes"), 612 &set_script_callback); 613 EXPECT_EQ(ERR_IO_PENDING, rv); 614 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 615 // One thread has been provisioned (i.e. one ProxyResolver was created). 616 ASSERT_EQ(1u, factory->resolvers().size()); 617 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 618 factory->resolvers()[0]->last_script_data()->utf16()); 619 620 const int kNumRequests = 9; 621 TestCompletionCallback callback[kNumRequests]; 622 ProxyInfo results[kNumRequests]; 623 ProxyResolver::RequestHandle request[kNumRequests]; 624 625 // Start request 0 -- this should run on thread 0 as there is nothing else 626 // going on right now. 627 rv = resolver.GetProxyForURL( 628 GURL("http://request0"), &results[0], &callback[0], &request[0], 629 BoundNetLog()); 630 EXPECT_EQ(ERR_IO_PENDING, rv); 631 632 // Wait for request 0 to finish. 633 rv = callback[0].WaitForResult(); 634 EXPECT_EQ(0, rv); 635 EXPECT_EQ("PROXY request0:80", results[0].ToPacString()); 636 ASSERT_EQ(1u, factory->resolvers().size()); 637 EXPECT_EQ(1, factory->resolvers()[0]->request_count()); 638 639 MessageLoop::current()->RunAllPending(); 640 641 // We now start 8 requests in parallel -- this will cause the maximum of 642 // three threads to be provisioned (an additional two from what we already 643 // have). 644 645 for (int i = 1; i < kNumRequests; ++i) { 646 rv = resolver.GetProxyForURL( 647 GURL(base::StringPrintf("http://request%d", i)), &results[i], 648 &callback[i], &request[i], BoundNetLog()); 649 EXPECT_EQ(ERR_IO_PENDING, rv); 650 } 651 652 // We should now have a total of 3 threads, each with its own ProxyResolver 653 // that will get initialized with the same data. (We check this later since 654 // the assignment happens on the worker threads and may not have occurred 655 // yet.) 656 ASSERT_EQ(3u, factory->resolvers().size()); 657 658 // Cancel 3 of the 8 oustanding requests. 659 resolver.CancelRequest(request[1]); 660 resolver.CancelRequest(request[3]); 661 resolver.CancelRequest(request[6]); 662 663 // Wait for the remaining requests to complete. 664 int kNonCancelledRequests[] = {2, 4, 5, 7, 8}; 665 for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) { 666 int request_index = kNonCancelledRequests[i]; 667 EXPECT_GE(callback[request_index].WaitForResult(), 0); 668 } 669 670 // Check that the cancelled requests never invoked their callback. 671 EXPECT_FALSE(callback[1].have_result()); 672 EXPECT_FALSE(callback[3].have_result()); 673 EXPECT_FALSE(callback[6].have_result()); 674 675 // We call SetPacScript again, solely to stop the current worker threads. 676 // (That way we can test to see the values observed by the synchronous 677 // resolvers in a non-racy manner). 678 TestCompletionCallback set_script_callback2; 679 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"), 680 &set_script_callback2); 681 EXPECT_EQ(ERR_IO_PENDING, rv); 682 EXPECT_EQ(OK, set_script_callback2.WaitForResult()); 683 ASSERT_EQ(4u, factory->resolvers().size()); 684 685 for (int i = 0; i < 3; ++i) { 686 EXPECT_EQ( 687 ASCIIToUTF16("pac script bytes"), 688 factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i; 689 } 690 691 EXPECT_EQ(ASCIIToUTF16("xyz"), 692 factory->resolvers()[3]->last_script_data()->utf16()); 693 694 // We don't know the exact ordering that requests ran on threads with, 695 // but we do know the total count that should have reached the threads. 696 // 8 total were submitted, and three were cancelled. Of the three that 697 // were cancelled, one of them (request 1) was cancelled after it had 698 // already been posted to the worker thread. So the resolvers will 699 // have seen 6 total (and 1 from the run prior). 700 ASSERT_EQ(4u, factory->resolvers().size()); 701 int total_count = 0; 702 for (int i = 0; i < 3; ++i) { 703 total_count += factory->resolvers()[i]->request_count(); 704 } 705 EXPECT_EQ(7, total_count); 706} 707 708// Tests using two threads. The first request hangs the first thread. Checks 709// that other requests are able to complete while this first request remains 710// stalled. 711TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) { 712 const size_t kNumThreads = 2u; 713 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory; 714 MultiThreadedProxyResolver resolver(factory, kNumThreads); 715 716 int rv; 717 718 EXPECT_TRUE(resolver.expects_pac_bytes()); 719 720 // Initialize the resolver. 721 TestCompletionCallback set_script_callback; 722 rv = resolver.SetPacScript( 723 ProxyResolverScriptData::FromUTF8("pac script bytes"), 724 &set_script_callback); 725 EXPECT_EQ(ERR_IO_PENDING, rv); 726 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 727 // One thread has been provisioned (i.e. one ProxyResolver was created). 728 ASSERT_EQ(1u, factory->resolvers().size()); 729 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 730 factory->resolvers()[0]->last_script_data()->utf16()); 731 732 const int kNumRequests = 4; 733 TestCompletionCallback callback[kNumRequests]; 734 ProxyInfo results[kNumRequests]; 735 ProxyResolver::RequestHandle request[kNumRequests]; 736 737 // Start a request that will block the first thread. 738 739 factory->resolvers()[0]->Block(); 740 741 rv = resolver.GetProxyForURL( 742 GURL("http://request0"), &results[0], &callback[0], &request[0], 743 BoundNetLog()); 744 745 EXPECT_EQ(ERR_IO_PENDING, rv); 746 factory->resolvers()[0]->WaitUntilBlocked(); 747 748 // Start 3 more requests -- they should all be serviced by thread #2 749 // since thread #1 is blocked. 750 751 for (int i = 1; i < kNumRequests; ++i) { 752 rv = resolver.GetProxyForURL( 753 GURL(base::StringPrintf("http://request%d", i)), 754 &results[i], &callback[i], &request[i], BoundNetLog()); 755 EXPECT_EQ(ERR_IO_PENDING, rv); 756 } 757 758 // Wait for the three requests to complete (they should complete in FIFO 759 // order). 760 for (int i = 1; i < kNumRequests; ++i) { 761 EXPECT_EQ(i - 1, callback[i].WaitForResult()); 762 } 763 764 // Unblock the first thread. 765 factory->resolvers()[0]->Unblock(); 766 EXPECT_EQ(0, callback[0].WaitForResult()); 767 768 // All in all, the first thread should have seen just 1 request. And the 769 // second thread 3 requests. 770 ASSERT_EQ(2u, factory->resolvers().size()); 771 EXPECT_EQ(1, factory->resolvers()[0]->request_count()); 772 EXPECT_EQ(3, factory->resolvers()[1]->request_count()); 773} 774 775} // namespace 776 777} // namespace net 778