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

Unified Diff: net/cert_net/cert_net_fetcher_impl.cc

Issue 2595723002: Allow CertNetFetcher to be shutdown from the network thread (Closed)
Patch Set: update comments Created 3 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/cert_net/cert_net_fetcher_impl.h ('k') | net/cert_net/cert_net_fetcher_impl_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/cert_net/cert_net_fetcher_impl.cc
diff --git a/net/cert_net/cert_net_fetcher_impl.cc b/net/cert_net/cert_net_fetcher_impl.cc
index bedd2af413bee6299b8c48e469830103ca155c47..50cc66c45923d1e85ee6c3075a5b67aa2e46f06e 100644
--- a/net/cert_net/cert_net_fetcher_impl.cc
+++ b/net/cert_net/cert_net_fetcher_impl.cc
@@ -13,34 +13,24 @@
// caller can use to cancel the fetch, or wait for it to complete
// (blocking).
//
+// The CertNetFetcherImpl is shared between a network thread and a
+// caller thread that waits for fetches to happen on the network thread.
+//
// The classes are mainly organized based on their thread affinity:
//
// ---------------
-// Lives on caller thread
+// Straddles caller thread and network thread
// ---------------
//
// CertNetFetcherImpl (implements CertNetFetcher)
-// * Main entry point
-// * Provides a service to start/cancel/wait for URL fetches
+// * Main entry point. Must be created and shutdown from the network thread.
+// * Provides a service to start/cancel/wait for URL fetches, to be
+// used on the caller thread.
// * Returns callers a CertNetFetcher::Request as a handle
// * Requests can run in parallel, however will block the current thread when
// reading results.
// * Posts tasks to network thread to coordinate actual work
//
-// CertNetFetcherRequestImpl (implements CertNetFetcher::Request)
-// * Wrapper for cancelling events, or waiting for a request to complete
-// * Waits on a WaitableEvent to complete requests.
-//
-// ---------------
-// Straddles caller thread and network thread
-// ---------------
-//
-// CertNetFetcherCore
-// * Reference-counted bridge between CertNetFetcherImpl and the dependencies
-// on network thread.
-// * Small wrapper to holds the state that is conceptually owned by
-// CertNetFetcherImpl, but belongs on the network thread.
-//
// RequestCore
// * Reference-counted bridge between CertNetFetcherRequestImpl and the
// dependencies on the network thread
@@ -48,6 +38,14 @@
// completion, and pointers for canceling work on network thread.
//
// ---------------
+// Lives on caller thread
+// ---------------
+//
+// CertNetFetcherRequestImpl (implements CertNetFetcher::Request)
+// * Wrapper for cancelling events, or waiting for a request to complete
+// * Waits on a WaitableEvent to complete requests.
+//
+// ---------------
// Lives on network thread
// ---------------
//
@@ -69,12 +67,12 @@
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_math.h"
#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/timer/timer.h"
#include "net/base/load_flags.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
// TODO(eroman): Add support for POST parameters.
// TODO(eroman): Add controls for bypassing the cache.
@@ -121,21 +119,22 @@ using JobSet = std::map<Job*, std::unique_ptr<Job>, JobComparator>;
class AsyncCertNetFetcherImpl {
public:
// Initializes AsyncCertNetFetcherImpl using the specified URLRequestContext
- // for issuing requests. |context| must remain valid for the entire
- // lifetime of the AsyncCertNetFetcherImpl.
+ // for issuing requests. |context| must remain valid until Shutdown() is
+ // called or the AsyncCertNetFetcherImpl is destroyed.
explicit AsyncCertNetFetcherImpl(URLRequestContext* context);
- // Deletion implicitly cancels any outstanding requests.
+ // The AsyncCertNetFetcherImpl is expected to be kept alive until all
+ // requests have completed or Shutdown() is called.
~AsyncCertNetFetcherImpl();
// Starts an asynchronous request to fetch the given URL. On completion
- // |callback| will be invoked.
- //
- // Completion of the request will never occur synchronously. In other words it
- // is guaranteed that |callback| will only be invoked once the Fetch*() method
- // has returned.
+ // request->OnJobCompleted() will be invoked.
void Fetch(std::unique_ptr<RequestParams> request_params,
- RequestCore* request);
+ scoped_refptr<RequestCore> request);
+
+ // Cancels outstanding jobs, which stops network requests and signals the
+ // corresponding RequestCores that the requests have completed.
+ void Shutdown();
private:
friend class Job;
@@ -206,6 +205,10 @@ class RequestCore : public base::RefCountedThreadSafe<RequestCore> {
void AttachedToJob(Job* job) {
DCHECK(task_runner_->RunsTasksOnCurrentThread());
DCHECK(!job_);
+ // Requests should not be attached to jobs after they have been signalled
+ // with a cancellation error (which happens via either Cancel() or
+ // SignalImmediateError()).
+ DCHECK_NE(error_, ERR_ABORTED);
job_ = job;
}
@@ -222,8 +225,13 @@ class RequestCore : public base::RefCountedThreadSafe<RequestCore> {
completion_event_.Signal();
}
- // Can be called from any thread.
- void Cancel();
+ // Detaches this request from its job (if it is attached to any) and
+ // signals completion with ERR_ABORTED. Can be called from any thread.
+ void CancelJob();
+
+ // Can be used to signal that an error was encountered before the request was
+ // attached to a job. Can be called from any thread.
+ void SignalImmediateError();
// Should only be called once.
void WaitForResult(Error* error, std::vector<uint8_t>* bytes) {
@@ -247,7 +255,10 @@ class RequestCore : public base::RefCountedThreadSafe<RequestCore> {
// A non-owned pointer to the job that is executing the request.
Job* job_ = nullptr;
- // May be written to from network thread.
+ // May be written to from network thread, or from the caller thread only when
+ // there is no work that will be done on the network thread (e.g. when the
+ // network thread has been shutdown before the request begins). See comment in
+ // SignalImmediateError.
Error error_;
std::vector<uint8_t> bytes_;
@@ -296,10 +307,9 @@ class Job : public URLRequest::Delegate {
const RequestParams& request_params() const { return *request_params_; }
- // Create a request and attaches it to the job. When the job completes it will
- // notify the request of completion through OnJobCompleted. Note that the Job
- // does NOT own the request.
- void AttachRequest(RequestCore* request);
+ // Creates a request and attaches it to the job. When the job completes it
+ // will notify the request of completion through OnJobCompleted.
+ void AttachRequest(scoped_refptr<RequestCore> request);
// Removes |request| from the job.
void DetachRequest(RequestCore* request);
@@ -309,6 +319,11 @@ class Job : public URLRequest::Delegate {
// notified of completion.
void StartURLRequest(URLRequestContext* context);
+ // Cancels the request with an ERR_ABORTED error and invokes
+ // RequestCore::OnJobCompleted() to notify the registered requests of the
+ // cancellation. The job is *not* removed from the AsyncCertNetFetcherImpl.
+ void Cancel();
+
private:
// Implementation of URLRequest::Delegate
void OnReceivedRedirect(URLRequest* request,
@@ -336,12 +351,16 @@ class Job : public URLRequest::Delegate {
// method is called, the |response_body_| variable have been assigned.
void OnJobCompleted(Error error);
+ // Calls r->OnJobCompleted() for each RequestCore |r| currently attached
+ // to this job, and then clears |requests_|.
+ void CompleteAndClearRequests(Error error);
+
// Cancels a request with a specified error code and calls
// OnUrlRequestCompleted().
void FailRequest(Error error);
- // The requests attached to this job (non-owned).
- std::vector<RequestCore*> requests_;
+ // The requests attached to this job.
+ std::vector<scoped_refptr<RequestCore>> requests_;
// The input parameters for starting a URLRequest.
std::unique_ptr<RequestParams> request_params_;
@@ -362,9 +381,10 @@ class Job : public URLRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(Job);
};
-void RequestCore::Cancel() {
+void RequestCore::CancelJob() {
if (!task_runner_->RunsTasksOnCurrentThread()) {
- task_runner_->PostTask(FROM_HERE, base::Bind(&RequestCore::Cancel, this));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&RequestCore::CancelJob, this));
return;
}
@@ -374,8 +394,23 @@ void RequestCore::Cancel() {
job->DetachRequest(this);
}
+ SignalImmediateError();
+}
+
+void RequestCore::SignalImmediateError() {
+ // These data members are normally only written on the network thread, but it
+ // is safe to write here from either thread. This is because
+ // SignalImmediateError is only to be called before this request is attached
+ // to a job. In particular, if called from the caller thread, no work will be
+ // done on the network thread for this request, so these variables will only
+ // be written and read on the caller thread. If called from the network
+ // thread, they will only be written to on the network thread and will not be
+ // read on the caller thread until |completion_event_| is signalled (after
+ // which it will be not be written on the network thread again).
+ DCHECK(!job_);
+ error_ = ERR_ABORTED;
bytes_.clear();
- error_ = ERR_UNEXPECTED;
+ completion_event_.Signal();
}
Job::Job(std::unique_ptr<RequestParams> request_params,
@@ -387,9 +422,9 @@ Job::~Job() {
Stop();
}
-void Job::AttachRequest(RequestCore* request) {
- requests_.push_back(request);
+void Job::AttachRequest(scoped_refptr<RequestCore> request) {
request->AttachedToJob(this);
+ requests_.push_back(std::move(request));
}
void Job::DetachRequest(RequestCore* request) {
@@ -408,10 +443,7 @@ void Job::DetachRequest(RequestCore* request) {
void Job::StartURLRequest(URLRequestContext* context) {
Error error = CanFetchUrl(request_params_->url);
if (error != OK) {
- // TODO(eroman): Don't post a task for this case.
- timer_.Start(
- FROM_HERE, base::TimeDelta(),
- base::Bind(&Job::OnJobCompleted, base::Unretained(this), error));
+ OnJobCompleted(error);
return;
}
@@ -432,6 +464,13 @@ void Job::StartURLRequest(URLRequestContext* context) {
base::Bind(&Job::FailRequest, base::Unretained(this), ERR_TIMED_OUT));
}
+void Job::Cancel() {
+ // Stop the timer and clear the URLRequest.
+ Stop();
+ // Signal attached requests that they've been completed.
+ CompleteAndClearRequests(static_cast<Error>(ERR_ABORTED));
+}
+
void Job::OnReceivedRedirect(URLRequest* request,
const RedirectInfo& redirect_info,
bool* defer_redirect) {
@@ -524,8 +563,11 @@ void Job::OnJobCompleted(Error error) {
Stop();
std::unique_ptr<Job> delete_this = parent_->RemoveJob(this);
+ CompleteAndClearRequests(error);
+}
- for (auto* request : requests_) {
+void Job::CompleteAndClearRequests(Error error) {
+ for (const auto& request : requests_) {
request->OnJobCompleted(this, error, response_body_);
}
@@ -555,20 +597,32 @@ bool JobComparator::operator()(const Job* job1, const Job* job2) const {
void AsyncCertNetFetcherImpl::Fetch(
std::unique_ptr<RequestParams> request_params,
- RequestCore* request) {
+ scoped_refptr<RequestCore> request) {
DCHECK(thread_checker_.CalledOnValidThread());
// If there is an in-progress job that matches the request parameters use it.
// Otherwise start a new job.
Job* job = FindJob(*request_params);
-
- if (!job) {
- job = new Job(std::move(request_params), this);
- jobs_[job] = base::WrapUnique(job);
- job->StartURLRequest(context_);
+ if (job) {
+ job->AttachRequest(std::move(request));
+ return;
}
- return job->AttachRequest(request);
+ job = new Job(std::move(request_params), this);
+ jobs_[job] = base::WrapUnique(job);
+ // Attach the request before calling StartURLRequest; this ensures that the
+ // request will get signalled if StartURLRequest completes the job
+ // synchronously.
+ job->AttachRequest(std::move(request));
+ job->StartURLRequest(context_);
+}
+
+void AsyncCertNetFetcherImpl::Shutdown() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (const auto& job : jobs_) {
+ job.first->Cancel();
+ }
+ jobs_.clear();
}
struct JobToRequestParamsComparator {
@@ -615,67 +669,27 @@ class CertNetFetcherRequestImpl : public CertNetFetcher::Request {
~CertNetFetcherRequestImpl() override {
if (core_)
- core_->Cancel();
+ core_->CancelJob();
}
private:
scoped_refptr<RequestCore> core_;
};
-class CertNetFetcherCore
- : public base::RefCountedThreadSafe<CertNetFetcherCore> {
+class CertNetFetcherImpl : public CertNetFetcher {
public:
- explicit CertNetFetcherCore(URLRequestContextGetter* context_getter)
- : context_getter_(context_getter) {}
+ explicit CertNetFetcherImpl(URLRequestContext* context)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), context_(context) {}
- void Abandon() {
- GetNetworkTaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&CertNetFetcherCore::DoAbandonOnNetworkThread, this));
- }
-
- scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() {
- return context_getter_->GetNetworkTaskRunner();
- }
-
- void DoFetchOnNetworkThread(std::unique_ptr<RequestParams> request_params,
- scoped_refptr<RequestCore> request) {
- DCHECK(GetNetworkTaskRunner()->RunsTasksOnCurrentThread());
-
- if (!impl_) {
- impl_.reset(
- new AsyncCertNetFetcherImpl(context_getter_->GetURLRequestContext()));
+ void Shutdown() override {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ if (impl_) {
+ impl_->Shutdown();
+ impl_.reset();
}
-
- // Don't need to retain a reference to |request| because consume is
- // expected to keep it alive.
- impl_->Fetch(std::move(request_params), request.get());
+ context_ = nullptr;
}
- private:
- friend class base::RefCountedThreadSafe<CertNetFetcherCore>;
-
- void DoAbandonOnNetworkThread() {
- DCHECK(GetNetworkTaskRunner()->RunsTasksOnCurrentThread());
- impl_.reset();
- }
-
- ~CertNetFetcherCore() { DCHECK(!impl_); }
-
- scoped_refptr<URLRequestContextGetter> context_getter_;
-
- std::unique_ptr<AsyncCertNetFetcherImpl> impl_;
-
- DISALLOW_COPY_AND_ASSIGN(CertNetFetcherCore);
-};
-
-class CertNetFetcherImpl : public CertNetFetcher {
- public:
- explicit CertNetFetcherImpl(URLRequestContextGetter* context_getter)
- : core_(new CertNetFetcherCore(context_getter)) {}
-
- ~CertNetFetcherImpl() override { core_->Abandon(); }
-
std::unique_ptr<Request> FetchCaIssuers(const GURL& url,
int timeout_milliseconds,
int max_response_bytes) override {
@@ -720,28 +734,60 @@ class CertNetFetcherImpl : public CertNetFetcher {
}
private:
+ ~CertNetFetcherImpl() override {
+ // The fetcher must be shutdown (at which point |context_| will be set to
+ // null) before destruction.
+ DCHECK(!context_);
+ }
+
+ void DoFetchOnNetworkThread(std::unique_ptr<RequestParams> request_params,
+ scoped_refptr<RequestCore> request) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+
+ if (!context_) {
+ // The fetcher might have been shutdown between when this task was posted
+ // and when it is running. In this case, signal the request and do not
+ // start a network request.
+ request->SignalImmediateError();
+ return;
+ }
+
+ if (!impl_) {
+ impl_.reset(new AsyncCertNetFetcherImpl(context_));
+ }
+
+ impl_->Fetch(std::move(request_params), request);
+ }
+
std::unique_ptr<Request> DoFetch(
std::unique_ptr<RequestParams> request_params) {
- auto task_runner = core_->GetNetworkTaskRunner();
- scoped_refptr<RequestCore> request_core = new RequestCore(task_runner);
-
- task_runner->PostTask(
- FROM_HERE,
- base::Bind(&CertNetFetcherCore::DoFetchOnNetworkThread, core_,
- base::Passed(&request_params), request_core));
+ scoped_refptr<RequestCore> request_core = new RequestCore(task_runner_);
+
+ // If the fetcher has already been shutdown, DoFetchOnNetworkThread will
+ // signal the request with an error. However, if the fetcher shuts down
+ // before DoFetchOnNetworkThread runs and PostTask still returns true, then
+ // the request will hang (that is, WaitForResult will not return).
+ if (!task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CertNetFetcherImpl::DoFetchOnNetworkThread, this,
+ base::Passed(&request_params), request_core))) {
+ request_core->SignalImmediateError();
+ }
return base::MakeUnique<CertNetFetcherRequestImpl>(std::move(request_core));
}
private:
- scoped_refptr<CertNetFetcherCore> core_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ // Not owned. |context_| must stay valid until Shutdown() is called.
+ URLRequestContext* context_ = nullptr;
+ std::unique_ptr<AsyncCertNetFetcherImpl> impl_;
};
} // namespace
-std::unique_ptr<CertNetFetcher> CreateCertNetFetcher(
- URLRequestContextGetter* context_getter) {
- return base::MakeUnique<CertNetFetcherImpl>(context_getter);
+scoped_refptr<CertNetFetcher> CreateCertNetFetcher(URLRequestContext* context) {
+ return make_scoped_refptr(new CertNetFetcherImpl(context));
}
} // namespace net
« no previous file with comments | « net/cert_net/cert_net_fetcher_impl.h ('k') | net/cert_net/cert_net_fetcher_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698