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

Side by Side Diff: net/proxy/single_threaded_proxy_resolver.cc

Issue 2822043: Add the capability to run multiple proxy PAC scripts in parallel.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Re-upload after revert Created 10 years, 5 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 | Annotate | Revision Log
OLDNEW
(Empty)
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/single_threaded_proxy_resolver.h"
6
7 #include "base/message_loop.h"
8 #include "base/thread.h"
9 #include "net/base/capturing_net_log.h"
10 #include "net/base/net_errors.h"
11 #include "net/proxy/proxy_info.h"
12
13 namespace net {
14
15 namespace {
16
17 class PurgeMemoryTask : public base::RefCountedThreadSafe<PurgeMemoryTask> {
18 public:
19 explicit PurgeMemoryTask(ProxyResolver* resolver) : resolver_(resolver) {}
20 void PurgeMemory() { resolver_->PurgeMemory(); }
21 private:
22 friend class base::RefCountedThreadSafe<PurgeMemoryTask>;
23 ~PurgeMemoryTask() {}
24 ProxyResolver* resolver_;
25 };
26
27 } // namespace
28
29 // SingleThreadedProxyResolver::SetPacScriptTask ------------------------------
30
31 // Runs on the worker thread to call ProxyResolver::SetPacScript.
32 class SingleThreadedProxyResolver::SetPacScriptTask
33 : public base::RefCountedThreadSafe<
34 SingleThreadedProxyResolver::SetPacScriptTask> {
35 public:
36 SetPacScriptTask(SingleThreadedProxyResolver* coordinator,
37 const GURL& pac_url,
38 const string16& pac_script,
39 CompletionCallback* callback)
40 : coordinator_(coordinator),
41 callback_(callback),
42 pac_script_(pac_script),
43 pac_url_(pac_url),
44 origin_loop_(MessageLoop::current()) {
45 DCHECK(callback);
46 }
47
48 // Start the SetPacScript request on the worker thread.
49 void Start() {
50 coordinator_->thread()->message_loop()->PostTask(
51 FROM_HERE, NewRunnableMethod(this, &SetPacScriptTask::DoRequest,
52 coordinator_->resolver_.get()));
53 }
54
55 void Cancel() {
56 // Clear these to inform RequestComplete that it should not try to
57 // access them.
58 coordinator_ = NULL;
59 callback_ = NULL;
60 }
61
62 // Returns true if Cancel() has been called.
63 bool was_cancelled() const { return callback_ == NULL; }
64
65 private:
66 friend class base::RefCountedThreadSafe<
67 SingleThreadedProxyResolver::SetPacScriptTask>;
68
69 ~SetPacScriptTask() {}
70
71 // Runs on the worker thread.
72 void DoRequest(ProxyResolver* resolver) {
73 int rv = resolver->expects_pac_bytes() ?
74 resolver->SetPacScriptByData(pac_script_, NULL) :
75 resolver->SetPacScriptByUrl(pac_url_, NULL);
76
77 DCHECK_NE(rv, ERR_IO_PENDING);
78 origin_loop_->PostTask(FROM_HERE,
79 NewRunnableMethod(this, &SetPacScriptTask::RequestComplete, rv));
80 }
81
82 // Runs the completion callback on the origin thread.
83 void RequestComplete(int result_code) {
84 // The task may have been cancelled after it was started.
85 if (!was_cancelled()) {
86 CompletionCallback* callback = callback_;
87 coordinator_->RemoveOutstandingSetPacScriptTask(this);
88 callback->Run(result_code);
89 }
90 }
91
92 // Must only be used on the "origin" thread.
93 SingleThreadedProxyResolver* coordinator_;
94 CompletionCallback* callback_;
95 string16 pac_script_;
96 GURL pac_url_;
97
98 // Usable from within DoQuery on the worker thread.
99 MessageLoop* origin_loop_;
100 };
101
102 // SingleThreadedProxyResolver::Job -------------------------------------------
103
104 class SingleThreadedProxyResolver::Job
105 : public base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job> {
106 public:
107 // |coordinator| -- the SingleThreadedProxyResolver that owns this job.
108 // |url| -- the URL of the query.
109 // |results| -- the structure to fill with proxy resolve results.
110 Job(SingleThreadedProxyResolver* coordinator,
111 const GURL& url,
112 ProxyInfo* results,
113 CompletionCallback* callback,
114 const BoundNetLog& net_log)
115 : coordinator_(coordinator),
116 callback_(callback),
117 results_(results),
118 net_log_(net_log),
119 url_(url),
120 is_started_(false),
121 origin_loop_(MessageLoop::current()) {
122 DCHECK(callback);
123 }
124
125 // Start the resolve proxy request on the worker thread.
126 void Start() {
127 is_started_ = true;
128
129 size_t load_log_bound = 100;
130
131 coordinator_->thread()->message_loop()->PostTask(
132 FROM_HERE, NewRunnableMethod(this, &Job::DoQuery,
133 coordinator_->resolver_.get(),
134 load_log_bound));
135 }
136
137 bool is_started() const { return is_started_; }
138
139 void Cancel() {
140 // Clear these to inform QueryComplete that it should not try to
141 // access them.
142 coordinator_ = NULL;
143 callback_ = NULL;
144 results_ = NULL;
145 }
146
147 // Returns true if Cancel() has been called.
148 bool was_cancelled() const { return callback_ == NULL; }
149
150 BoundNetLog* net_log() { return &net_log_; }
151
152 private:
153 friend class base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job>;
154
155 ~Job() {}
156
157 // Runs on the worker thread.
158 void DoQuery(ProxyResolver* resolver, size_t load_log_bound) {
159 worker_log_.reset(new CapturingNetLog(load_log_bound));
160 BoundNetLog bound_worker_log(NetLog::Source(), worker_log_.get());
161
162 int rv = resolver->GetProxyForURL(url_, &results_buf_, NULL, NULL,
163 bound_worker_log);
164 DCHECK_NE(rv, ERR_IO_PENDING);
165
166 origin_loop_->PostTask(FROM_HERE,
167 NewRunnableMethod(this, &Job::QueryComplete, rv));
168 }
169
170 // Runs the completion callback on the origin thread.
171 void QueryComplete(int result_code) {
172 // Merge the load log that was generated on the worker thread, into the
173 // main log.
174 CapturingBoundNetLog bound_worker_log(NetLog::Source(),
175 worker_log_.release());
176 bound_worker_log.AppendTo(net_log_);
177
178 // The Job may have been cancelled after it was started.
179 if (!was_cancelled()) {
180 if (result_code >= OK) { // Note: unit-tests use values > 0.
181 results_->Use(results_buf_);
182 }
183 callback_->Run(result_code);
184
185 // We check for cancellation once again, in case the callback deleted
186 // the owning ProxyService (whose destructor will in turn cancel us).
187 if (!was_cancelled())
188 coordinator_->RemoveFrontOfJobsQueueAndStartNext(this);
189 }
190 }
191
192 // Must only be used on the "origin" thread.
193 SingleThreadedProxyResolver* coordinator_;
194 CompletionCallback* callback_;
195 ProxyInfo* results_;
196 BoundNetLog net_log_;
197 GURL url_;
198 bool is_started_;
199
200 // Usable from within DoQuery on the worker thread.
201 ProxyInfo results_buf_;
202 MessageLoop* origin_loop_;
203
204 // Used to pass the captured events between DoQuery [worker thread] and
205 // QueryComplete [origin thread].
206 scoped_ptr<CapturingNetLog> worker_log_;
207 };
208
209 // SingleThreadedProxyResolver ------------------------------------------------
210
211 SingleThreadedProxyResolver::SingleThreadedProxyResolver(
212 ProxyResolver* resolver)
213 : ProxyResolver(resolver->expects_pac_bytes()),
214 resolver_(resolver) {
215 }
216
217 SingleThreadedProxyResolver::~SingleThreadedProxyResolver() {
218 // Cancel the inprogress job (if any), and free the rest.
219 for (PendingJobsQueue::iterator it = pending_jobs_.begin();
220 it != pending_jobs_.end();
221 ++it) {
222 (*it)->Cancel();
223 }
224
225 if (outstanding_set_pac_script_task_)
226 outstanding_set_pac_script_task_->Cancel();
227
228 // Note that |thread_| is destroyed before |resolver_|. This is important
229 // since |resolver_| could be running on |thread_|.
230 }
231
232 int SingleThreadedProxyResolver::GetProxyForURL(const GURL& url,
233 ProxyInfo* results,
234 CompletionCallback* callback,
235 RequestHandle* request,
236 const BoundNetLog& net_log) {
237 DCHECK(callback);
238
239 scoped_refptr<Job> job = new Job(this, url, results, callback, net_log);
240 bool is_first_job = pending_jobs_.empty();
241 pending_jobs_.push_back(job); // Jobs can never finish synchronously.
242
243 if (is_first_job) {
244 // If there is nothing already running, start the job now.
245 EnsureThreadStarted();
246 job->Start();
247 } else {
248 // Otherwise the job will get started eventually by ProcessPendingJobs().
249 job->net_log()->BeginEvent(
250 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD, NULL);
251 }
252
253 // Completion will be notified through |callback|, unless the caller cancels
254 // the request using |request|.
255 if (request)
256 *request = reinterpret_cast<RequestHandle>(job.get());
257
258 return ERR_IO_PENDING;
259 }
260
261 // There are three states of the request we need to handle:
262 // (1) Not started (just sitting in the queue).
263 // (2) Executing Job::DoQuery in the worker thread.
264 // (3) Waiting for Job::QueryComplete to be run on the origin thread.
265 void SingleThreadedProxyResolver::CancelRequest(RequestHandle req) {
266 DCHECK(req);
267
268 Job* job = reinterpret_cast<Job*>(req);
269
270 bool is_active_job = job->is_started() && !pending_jobs_.empty() &&
271 pending_jobs_.front().get() == job;
272
273 job->Cancel();
274
275 if (is_active_job) {
276 RemoveFrontOfJobsQueueAndStartNext(job);
277 return;
278 }
279
280 // Otherwise just delete the job from the queue.
281 PendingJobsQueue::iterator it = std::find(
282 pending_jobs_.begin(), pending_jobs_.end(), job);
283 DCHECK(it != pending_jobs_.end());
284 pending_jobs_.erase(it);
285 }
286
287 void SingleThreadedProxyResolver::CancelSetPacScript() {
288 DCHECK(outstanding_set_pac_script_task_);
289 outstanding_set_pac_script_task_->Cancel();
290 outstanding_set_pac_script_task_ = NULL;
291 }
292
293 void SingleThreadedProxyResolver::PurgeMemory() {
294 if (thread_.get()) {
295 scoped_refptr<PurgeMemoryTask> helper(new PurgeMemoryTask(resolver_.get()));
296 thread_->message_loop()->PostTask(FROM_HERE,
297 NewRunnableMethod(helper.get(), &PurgeMemoryTask::PurgeMemory));
298 }
299 }
300
301 int SingleThreadedProxyResolver::SetPacScript(
302 const GURL& pac_url,
303 const string16& pac_script,
304 CompletionCallback* callback) {
305 EnsureThreadStarted();
306 DCHECK(!outstanding_set_pac_script_task_);
307
308 SetPacScriptTask* task = new SetPacScriptTask(
309 this, pac_url, pac_script, callback);
310 outstanding_set_pac_script_task_ = task;
311 task->Start();
312 return ERR_IO_PENDING;
313 }
314
315 void SingleThreadedProxyResolver::EnsureThreadStarted() {
316 if (!thread_.get()) {
317 thread_.reset(new base::Thread("pac-thread"));
318 thread_->Start();
319 }
320 }
321
322 void SingleThreadedProxyResolver::ProcessPendingJobs() {
323 if (pending_jobs_.empty())
324 return;
325
326 // Get the next job to process (FIFO).
327 Job* job = pending_jobs_.front().get();
328 if (job->is_started())
329 return;
330
331 job->net_log()->EndEvent(
332 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD, NULL);
333
334 EnsureThreadStarted();
335 job->Start();
336 }
337
338 void SingleThreadedProxyResolver::RemoveFrontOfJobsQueueAndStartNext(
339 Job* expected_job) {
340 DCHECK_EQ(expected_job, pending_jobs_.front().get());
341 pending_jobs_.pop_front();
342
343 // Start next work item.
344 ProcessPendingJobs();
345 }
346
347 void SingleThreadedProxyResolver::RemoveOutstandingSetPacScriptTask(
348 SetPacScriptTask* task) {
349 DCHECK_EQ(outstanding_set_pac_script_task_.get(), task);
350 outstanding_set_pac_script_task_ = NULL;
351 }
352
353 } // namespace net
OLDNEW
« no previous file with comments | « net/proxy/single_threaded_proxy_resolver.h ('k') | net/proxy/single_threaded_proxy_resolver_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698