| OLD | NEW |
| (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 | |
| OLD | NEW |