1// Copyright (c) 2011 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#ifndef NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
6#define NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
7#pragma once
8
9#include <deque>
10#include <vector>
11
12#include "base/basictypes.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/threading/non_thread_safe.h"
16#include "net/proxy/proxy_resolver.h"
17
18namespace base {
19class Thread;
20}  // namespace base
21
22namespace net {
23
24// ProxyResolverFactory is an interface for creating ProxyResolver instances.
25class ProxyResolverFactory {
26 public:
27  explicit ProxyResolverFactory(bool resolvers_expect_pac_bytes)
28      : resolvers_expect_pac_bytes_(resolvers_expect_pac_bytes) {}
29
30  virtual ~ProxyResolverFactory() {}
31
32  // Creates a new ProxyResolver. The caller is responsible for freeing this
33  // object.
34  virtual ProxyResolver* CreateProxyResolver() = 0;
35
36  bool resolvers_expect_pac_bytes() const {
37    return resolvers_expect_pac_bytes_;
38  }
39
40 private:
41  bool resolvers_expect_pac_bytes_;
42  DISALLOW_COPY_AND_ASSIGN(ProxyResolverFactory);
43};
44
45// MultiThreadedProxyResolver is a ProxyResolver implementation that runs
46// synchronous ProxyResolver implementations on worker threads.
47//
48// Threads are created lazily on demand, up to a maximum total. The advantage
49// of having a pool of threads, is faster performance. In particular, being
50// able to keep servicing PAC requests even if one blocks its execution.
51//
52// During initialization (SetPacScript), a single thread is spun up to test
53// the script. If this succeeds, we cache the input script, and will re-use
54// this to lazily provision any new threads as needed.
55//
56// For each new thread that we spawn, a corresponding new ProxyResolver is
57// created using ProxyResolverFactory.
58//
59// Because we are creating multiple ProxyResolver instances, this means we
60// are duplicating script contexts for what is ordinarily seen as being a
61// single script. This can affect compatibility on some classes of PAC
62// script:
63//
64// (a) Scripts whose initialization has external dependencies on network or
65//     time may end up successfully initializing on some threads, but not
66//     others. So depending on what thread services the request, the result
67//     may jump between several possibilities.
68//
69// (b) Scripts whose FindProxyForURL() depends on side-effects may now
70//     work differently. For example, a PAC script which was incrementing
71//     a global counter and using that to make a decision. In the
72//     multi-threaded model, each thread may have a different value for this
73//     counter, so it won't globally be seen as monotonically increasing!
74class MultiThreadedProxyResolver : public ProxyResolver,
75                                   public base::NonThreadSafe {
76 public:
77  // Creates an asynchronous ProxyResolver that runs requests on up to
78  // |max_num_threads|.
79  //
80  // For each thread that is created, an accompanying synchronous ProxyResolver
81  // will be provisioned using |resolver_factory|. All methods on these
82  // ProxyResolvers will be called on the one thread, with the exception of
83  // ProxyResolver::Shutdown() which will be called from the origin thread
84  // prior to destruction.
85  //
86  // The constructor takes ownership of |resolver_factory|.
87  MultiThreadedProxyResolver(ProxyResolverFactory* resolver_factory,
88                             size_t max_num_threads);
89
90  virtual ~MultiThreadedProxyResolver();
91
92  // ProxyResolver implementation:
93  virtual int GetProxyForURL(const GURL& url,
94                             ProxyInfo* results,
95                             CompletionCallback* callback,
96                             RequestHandle* request,
97                             const BoundNetLog& net_log);
98  virtual void CancelRequest(RequestHandle request);
99  virtual void CancelSetPacScript();
100  virtual void PurgeMemory();
101  virtual int SetPacScript(
102      const scoped_refptr<ProxyResolverScriptData>& script_data,
103      CompletionCallback* callback);
104
105 private:
106  class Executor;
107  class Job;
108  class SetPacScriptJob;
109  class GetProxyForURLJob;
110  // FIFO queue of pending jobs waiting to be started.
111  // TODO(eroman): Make this priority queue.
112  typedef std::deque<scoped_refptr<Job> > PendingJobsQueue;
113  typedef std::vector<scoped_refptr<Executor> > ExecutorList;
114
115  // Asserts that there are no outstanding user-initiated jobs on any of the
116  // worker threads.
117  void CheckNoOutstandingUserRequests() const;
118
119  // Stops and deletes all of the worker threads.
120  void ReleaseAllExecutors();
121
122  // Returns an idle worker thread which is ready to receive GetProxyForURL()
123  // requests. If all threads are occupied, returns NULL.
124  Executor* FindIdleExecutor();
125
126  // Creates a new worker thread, and appends it to |executors_|.
127  Executor* AddNewExecutor();
128
129  // Starts the next job from |pending_jobs_| if possible.
130  void OnExecutorReady(Executor* executor);
131
132  const scoped_ptr<ProxyResolverFactory> resolver_factory_;
133  const size_t max_num_threads_;
134  PendingJobsQueue pending_jobs_;
135  ExecutorList executors_;
136  scoped_refptr<ProxyResolverScriptData> current_script_data_;
137};
138
139}  // namespace net
140
141#endif  // NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
142