Index: net/cert/multi_threaded_cert_verifier.cc |
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc |
index 44eff8e776f5a0df2c0e4677b8b7756ee1df9cc0..273194c85ebe938e9b2d3fe79d8da755cf02517f 100644 |
--- a/net/cert/multi_threaded_cert_verifier.cc |
+++ b/net/cert/multi_threaded_cert_verifier.cc |
@@ -8,13 +8,14 @@ |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
+#include "base/callback_helpers.h" |
#include "base/compiler_specific.h" |
+#include "base/containers/linked_list.h" |
#include "base/message_loop/message_loop.h" |
#include "base/metrics/histogram.h" |
#include "base/profiler/scoped_tracker.h" |
#include "base/sha1.h" |
#include "base/stl_util.h" |
-#include "base/synchronization/lock.h" |
#include "base/threading/worker_pool.h" |
#include "base/time/time.h" |
#include "base/values.h" |
@@ -35,51 +36,15 @@ namespace net { |
//////////////////////////////////////////////////////////////////////////// |
-// Life of a request: |
-// |
-// MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request |
-// | (origin loop) (worker loop) |
-// | |
-// Verify() |
-// |---->-------------------------------------<creates> |
-// | |
-// |---->-------------------<creates> |
-// | |
-// |---->-------------------------------------------------------<creates> |
-// | |
-// |---->---------------------------------------Start |
-// | | |
-// | PostTask |
-// | |
-// | <starts verifying> |
-// |---->-------------------AddRequest | |
-// | |
-// | |
-// | |
-// Finish |
-// | |
-// PostTask |
-// |
-// | |
-// DoReply |
-// |----<-----------------------------------------| |
-// HandleResult |
-// | |
-// |---->------------------HandleResult |
-// | |
-// |------>---------------------------Post |
-// |
-// |
-// |
// On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously |
// without posting a task to a worker thread. |
namespace { |
-// The default value of max_cache_entries_. |
+// The maximum number of cache entries to use for the ExpiringCache. |
const unsigned kMaxCacheEntries = 256; |
-// The number of seconds for which we'll cache a cache entry. |
+// The number of seconds to cache entries. |
const unsigned kTTLSecs = 1800; // 30 minutes. |
base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result, |
@@ -165,103 +130,66 @@ bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()( |
now.verification_time < expiration.expiration_time; |
}; |
- |
-// Represents the output and result callback of a request. |
-class CertVerifierRequest { |
+// Represents the output and result callback of a request. The |
+// CertVerifierRequest is owned by the caller that initiated the call to |
+// CertVerifier::Verify(). |
+class CertVerifierRequest : public CertVerifier::Request, |
+ public base::LinkNode<CertVerifierRequest> { |
public: |
- CertVerifierRequest(const CompletionCallback& callback, |
+ CertVerifierRequest(CertVerifierJob* job, |
+ const CompletionCallback& callback, |
CertVerifyResult* verify_result, |
const BoundNetLog& net_log) |
- : callback_(callback), |
+ : job_(job), |
+ callback_(callback), |
verify_result_(verify_result), |
net_log_(net_log) { |
net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
} |
- ~CertVerifierRequest() { |
- } |
- |
- // Ensures that the result callback will never be made. |
- void Cancel() { |
- callback_.Reset(); |
- verify_result_ = NULL; |
- net_log_.AddEvent(NetLog::TYPE_CANCELLED); |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
- } |
+ // Cancels the request. |
+ ~CertVerifierRequest() override; |
// Copies the contents of |verify_result| to the caller's |
// CertVerifyResult and calls the callback. |
void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) { |
- if (!callback_.is_null()) { |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
- *verify_result_ = verify_result.result; |
- callback_.Run(verify_result.error); |
- } |
- delete this; |
+ DCHECK(job_); |
+ job_ = nullptr; |
+ |
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
+ *verify_result_ = verify_result.result; |
+ |
+ base::ResetAndReturn(&callback_).Run(verify_result.error); |
} |
- bool canceled() const { return callback_.is_null(); } |
+ void OnJobCancelled() { |
+ job_ = nullptr; |
+ callback_.Reset(); |
+ } |
const BoundNetLog& net_log() const { return net_log_; } |
private: |
+ CertVerifierJob* job_; // Not owned. |
CompletionCallback callback_; |
CertVerifyResult* verify_result_; |
const BoundNetLog net_log_; |
}; |
+// DoVerifyOnWorkerThread runs the verification synchronously on a worker |
+// thread. The output parameters (error and result) must remain alive. |
+void DoVerifyOnWorkerThread(scoped_refptr<CertVerifyProc> verify_proc, |
+ scoped_refptr<X509Certificate> cert, |
+ const std::string& hostname, |
+ const std::string& ocsp_response, |
+ int flags, |
+ scoped_refptr<CRLSet> crl_set, |
+ const CertificateList& additional_trust_anchors, |
+ int* error, |
+ CertVerifyResult* result) { |
+ *error = verify_proc->Verify(cert.get(), hostname, ocsp_response, flags, |
+ crl_set.get(), additional_trust_anchors, result); |
-// CertVerifierWorker runs on a worker thread and takes care of the blocking |
-// process of performing the certificate verification. Deletes itself |
-// eventually if Start() succeeds. |
-class CertVerifierWorker { |
- public: |
- CertVerifierWorker(CertVerifyProc* verify_proc, |
- X509Certificate* cert, |
- const std::string& hostname, |
- const std::string& ocsp_response, |
- int flags, |
- CRLSet* crl_set, |
- const CertificateList& additional_trust_anchors, |
- MultiThreadedCertVerifier* cert_verifier) |
- : verify_proc_(verify_proc), |
- cert_(cert), |
- hostname_(hostname), |
- ocsp_response_(ocsp_response), |
- flags_(flags), |
- crl_set_(crl_set), |
- additional_trust_anchors_(additional_trust_anchors), |
- origin_loop_(base::MessageLoop::current()), |
- cert_verifier_(cert_verifier), |
- canceled_(false), |
- error_(ERR_FAILED) {} |
- |
- // Returns the certificate being verified. May only be called /before/ |
- // Start() is called. |
- X509Certificate* certificate() const { return cert_.get(); } |
- |
- bool Start() { |
- DCHECK_EQ(base::MessageLoop::current(), origin_loop_); |
- |
- return base::WorkerPool::PostTask( |
- FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)), |
- true /* task is slow */); |
- } |
- |
- // Cancel is called from the origin loop when the MultiThreadedCertVerifier is |
- // getting deleted. |
- void Cancel() { |
- DCHECK_EQ(base::MessageLoop::current(), origin_loop_); |
- base::AutoLock locked(lock_); |
- canceled_ = true; |
- } |
- |
- private: |
- void Run() { |
- // Runs on a worker thread. |
- error_ = verify_proc_->Verify(cert_.get(), hostname_, ocsp_response_, |
- flags_, crl_set_.get(), |
- additional_trust_anchors_, &verify_result_); |
#if defined(USE_NSS_CERTS) || defined(OS_IOS) |
// Detach the thread from NSPR. |
// Calling NSS functions attaches the thread to NSPR, which stores |
@@ -272,118 +200,93 @@ class CertVerifierWorker { |
// destructors run. |
PR_DetachThread(); |
#endif |
- Finish(); |
- } |
- |
- // DoReply runs on the origin thread. |
- void DoReply() { |
- // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is |
- // fixed. |
- tracked_objects::ScopedTracker tracking_profile( |
- FROM_HERE_WITH_EXPLICIT_FUNCTION("477117 CertVerifierWorker::DoReply")); |
- DCHECK_EQ(base::MessageLoop::current(), origin_loop_); |
- { |
- // We lock here because the worker thread could still be in Finished, |
- // after the PostTask, but before unlocking |lock_|. If we do not lock in |
- // this case, we will end up deleting a locked Lock, which can lead to |
- // memory leaks or worse errors. |
- base::AutoLock locked(lock_); |
- if (!canceled_) { |
- cert_verifier_->HandleResult(cert_.get(), hostname_, ocsp_response_, |
- flags_, additional_trust_anchors_, error_, |
- verify_result_); |
- } |
- } |
- delete this; |
- } |
- |
- void Finish() { |
- // Runs on the worker thread. |
- // We assume that the origin loop outlives the MultiThreadedCertVerifier. If |
- // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If |
- // it does so before the Acquire, we'll delete ourselves and return. If it's |
- // trying to do so concurrently, then it'll block on the lock and we'll call |
- // PostTask while the MultiThreadedCertVerifier (and therefore the |
- // MessageLoop) is still alive. |
- // If it does so after this function, we assume that the MessageLoop will |
- // process pending tasks. In which case we'll notice the |canceled_| flag |
- // in DoReply. |
- |
- bool canceled; |
- { |
- base::AutoLock locked(lock_); |
- canceled = canceled_; |
- if (!canceled) { |
- origin_loop_->PostTask( |
- FROM_HERE, base::Bind( |
- &CertVerifierWorker::DoReply, base::Unretained(this))); |
- } |
- } |
- |
- if (canceled) |
- delete this; |
- } |
- |
- scoped_refptr<CertVerifyProc> verify_proc_; |
- scoped_refptr<X509Certificate> cert_; |
- const std::string hostname_; |
- const std::string ocsp_response_; |
- const int flags_; |
- scoped_refptr<CRLSet> crl_set_; |
- const CertificateList additional_trust_anchors_; |
- base::MessageLoop* const origin_loop_; |
- MultiThreadedCertVerifier* const cert_verifier_; |
- |
- // lock_ protects canceled_. |
- base::Lock lock_; |
- |
- // If canceled_ is true, |
- // * origin_loop_ cannot be accessed by the worker thread, |
- // * cert_verifier_ cannot be accessed by any thread. |
- bool canceled_; |
- |
- int error_; |
- CertVerifyResult verify_result_; |
- |
- DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker); |
-}; |
+} |
-// A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It |
-// lives only on the CertVerifier's origin message loop. |
+// CertVerifierJob lives only on the verifier's origin message loop. |
class CertVerifierJob { |
public: |
- CertVerifierJob(CertVerifierWorker* worker, |
- const BoundNetLog& net_log) |
- : start_time_(base::TimeTicks::Now()), |
- worker_(worker), |
- net_log_(net_log) { |
+ CertVerifierJob(const MultiThreadedCertVerifier::RequestParams& key, |
+ NetLog* net_log, |
+ X509Certificate* cert, |
+ MultiThreadedCertVerifier* cert_verifier) |
+ : key_(key), |
+ start_time_(base::TimeTicks::Now()), |
+ net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_CERT_VERIFIER_JOB)), |
+ cert_verifier_(cert_verifier), |
+ weak_ptr_factory_(this) { |
net_log_.BeginEvent( |
NetLog::TYPE_CERT_VERIFIER_JOB, |
- base::Bind(&NetLogX509CertificateCallback, |
- base::Unretained(worker_->certificate()))); |
+ base::Bind(&NetLogX509CertificateCallback, base::Unretained(cert))); |
+ } |
+ |
+ // Indicates whether this was the first job started by the CertVerifier. This |
+ // is only used for logging certain UMA stats. |
+ void set_is_first_job(bool is_first_job) { is_first_job_ = is_first_job; } |
+ |
+ const MultiThreadedCertVerifier::RequestParams& key() const { return key_; } |
+ |
+ // Posts a task to the worker pool to do the verification. Once the |
+ // verification has completed on the worker thread, it will call |
+ // OnJobCompleted() on the origin thread. |
+ bool Start(const scoped_refptr<CertVerifyProc>& verify_proc, |
+ const scoped_refptr<X509Certificate>& cert, |
+ const std::string& hostname, |
+ const std::string& ocsp_response, |
+ int flags, |
+ const scoped_refptr<CRLSet>& crl_set, |
+ const CertificateList& additional_trust_anchors) { |
+ // Owned by the bound reply callback. |
+ scoped_ptr<MultiThreadedCertVerifier::CachedResult> result( |
+ new MultiThreadedCertVerifier::CachedResult()); |
+ |
+ return base::WorkerPool::PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(DoVerifyOnWorkerThread, verify_proc, cert, hostname, |
+ ocsp_response, flags, crl_set, additional_trust_anchors, |
+ &result->error, &result->result), |
+ base::Bind(&CertVerifierJob::OnJobCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&result)), |
+ true /* task is slow */); |
} |
~CertVerifierJob() { |
- if (worker_) { |
+ // If the job is in progress, cancel it. |
+ if (cert_verifier_) { |
+ cert_verifier_ = nullptr; |
+ |
net_log_.AddEvent(NetLog::TYPE_CANCELLED); |
net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB); |
- worker_->Cancel(); |
- DeleteAllCanceled(); |
+ |
+ // Notify each request of the cancellation. |
+ for (base::LinkNode<CertVerifierRequest>* it = requests_.head(); |
+ it != requests_.end(); it = it->next()) { |
+ it->value()->OnJobCancelled(); |
+ } |
} |
} |
- void AddRequest(CertVerifierRequest* request) { |
+ // Creates and attaches a request to the Job. |
+ scoped_ptr<CertVerifierRequest> CreateRequest( |
+ const CompletionCallback& callback, |
+ CertVerifyResult* verify_result, |
+ const BoundNetLog& net_log) { |
+ scoped_ptr<CertVerifierRequest> request( |
+ new CertVerifierRequest(this, callback, verify_result, net_log)); |
+ |
request->net_log().AddEvent( |
NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB, |
net_log_.source().ToEventParametersCallback()); |
- requests_.push_back(request); |
+ requests_.Append(request.get()); |
+ return request.Pass(); |
} |
- void HandleResult( |
- const MultiThreadedCertVerifier::CachedResult& verify_result, |
- bool is_first_job) { |
- worker_ = NULL; |
+ private: |
+ using RequestList = base::LinkedList<CertVerifierRequest>; |
+ |
+ // Called on completion of the Job to log UMA metrics and NetLog events. |
+ void LogMetrics( |
+ const MultiThreadedCertVerifier::CachedResult& verify_result) { |
net_log_.EndEvent( |
NetLog::TYPE_CERT_VERIFIER_JOB, |
base::Bind(&CertVerifyResultCallback, verify_result.result)); |
@@ -393,49 +296,61 @@ class CertVerifierJob { |
base::TimeDelta::FromMilliseconds(1), |
base::TimeDelta::FromMinutes(10), |
100); |
- if (is_first_job) { |
+ if (is_first_job_) { |
UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency", |
latency, |
base::TimeDelta::FromMilliseconds(1), |
base::TimeDelta::FromMinutes(10), |
100); |
} |
- PostAll(verify_result); |
- } |
- |
- private: |
- void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) { |
- std::vector<CertVerifierRequest*> requests; |
- requests_.swap(requests); |
- |
- for (std::vector<CertVerifierRequest*>::iterator |
- i = requests.begin(); i != requests.end(); i++) { |
- (*i)->Post(verify_result); |
- // Post() causes the CertVerifierRequest to delete itself. |
- } |
} |
- void DeleteAllCanceled() { |
- for (std::vector<CertVerifierRequest*>::iterator |
- i = requests_.begin(); i != requests_.end(); i++) { |
- if ((*i)->canceled()) { |
- delete *i; |
- } else { |
- LOG(DFATAL) << "CertVerifierRequest leaked!"; |
- } |
+ void OnJobCompleted( |
+ scoped_ptr<MultiThreadedCertVerifier::CachedResult> verify_result) { |
+ scoped_ptr<CertVerifierJob> keep_alive = cert_verifier_->RemoveJob(this); |
+ |
+ LogMetrics(*verify_result); |
+ cert_verifier_->SaveResultToCache(key_, *verify_result); |
+ cert_verifier_ = nullptr; |
+ |
+ // TODO(eroman): If the cert_verifier_ is deleted from within one of the |
+ // callbacks, any remaining requests for that job should be cancelled. Right |
+ // now they will be called. |
+ while (!requests_.empty()) { |
+ base::LinkNode<CertVerifierRequest>* request = requests_.head(); |
+ request->RemoveFromList(); |
+ request->value()->Post(*verify_result); |
} |
} |
+ const MultiThreadedCertVerifier::RequestParams key_; |
const base::TimeTicks start_time_; |
- std::vector<CertVerifierRequest*> requests_; |
- CertVerifierWorker* worker_; |
+ |
+ RequestList requests_; // Non-owned. |
+ |
const BoundNetLog net_log_; |
+ MultiThreadedCertVerifier* cert_verifier_; // Non-owned. |
+ |
+ bool is_first_job_ = false; |
+ base::WeakPtrFactory<CertVerifierJob> weak_ptr_factory_; |
}; |
+CertVerifierRequest::~CertVerifierRequest() { |
+ if (job_) { |
+ // Cancel the outstanding request. |
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED); |
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
+ |
+ // Remove the request from the Job. No attempt is made to cancel the job |
+ // even though it may no longer have any requests attached to it. Because it |
+ // is running on a worker thread aborting it isn't feasible. |
+ RemoveFromList(); |
+ } |
+} |
+ |
MultiThreadedCertVerifier::MultiThreadedCertVerifier( |
CertVerifyProc* verify_proc) |
: cache_(kMaxCacheEntries), |
- first_job_(NULL), |
requests_(0), |
cache_hits_(0), |
inflight_joins_(0), |
@@ -445,7 +360,7 @@ MultiThreadedCertVerifier::MultiThreadedCertVerifier( |
} |
MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { |
- STLDeleteValues(&inflight_); |
+ STLDeleteElements(&inflight_); |
CertDatabase::GetInstance()->RemoveObserver(this); |
} |
@@ -462,14 +377,14 @@ int MultiThreadedCertVerifier::Verify(X509Certificate* cert, |
CRLSet* crl_set, |
CertVerifyResult* verify_result, |
const CompletionCallback& callback, |
- RequestHandle* out_req, |
+ scoped_ptr<Request>* out_req, |
const BoundNetLog& net_log) { |
+ out_req->reset(); |
+ |
DCHECK(CalledOnValidThread()); |
- if (callback.is_null() || !verify_result || hostname.empty()) { |
- *out_req = NULL; |
+ if (callback.is_null() || !verify_result || hostname.empty()) |
return ERR_INVALID_ARGUMENT; |
- } |
requests_++; |
@@ -484,56 +399,41 @@ int MultiThreadedCertVerifier::Verify(X509Certificate* cert, |
cache_.Get(key, CacheValidityPeriod(base::Time::Now())); |
if (cached_entry) { |
++cache_hits_; |
- *out_req = NULL; |
*verify_result = cached_entry->result; |
return cached_entry->error; |
} |
// No cache hit. See if an identical request is currently in flight. |
- CertVerifierJob* job; |
- std::map<RequestParams, CertVerifierJob*>::const_iterator j; |
- j = inflight_.find(key); |
- if (j != inflight_.end()) { |
+ CertVerifierJob* job = FindJob(key); |
+ if (job) { |
// An identical request is in flight already. We'll just attach our |
// callback. |
inflight_joins_++; |
- job = j->second; |
} else { |
- // Need to make a new request. |
- CertVerifierWorker* worker = new CertVerifierWorker( |
- verify_proc_.get(), cert, hostname, ocsp_response, flags, crl_set, |
- additional_trust_anchors, this); |
- job = new CertVerifierJob( |
- worker, |
- BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB)); |
- if (!worker->Start()) { |
- delete job; |
- delete worker; |
- *out_req = NULL; |
+ // Need to make a new job. |
+ scoped_ptr<CertVerifierJob> new_job( |
+ new CertVerifierJob(key, net_log.net_log(), cert, this)); |
+ |
+ if (!new_job->Start(verify_proc_, cert, hostname, ocsp_response, flags, |
+ crl_set, additional_trust_anchors)) { |
// TODO(wtc): log to the NetLog. |
LOG(ERROR) << "CertVerifierWorker couldn't be started."; |
return ERR_INSUFFICIENT_RESOURCES; // Just a guess. |
} |
- inflight_.insert(std::make_pair(key, job)); |
- if (requests_ == 1) { |
- // Cleared in HandleResult. |
- first_job_ = job; |
- } |
+ |
+ job = new_job.release(); |
+ inflight_.insert(job); |
+ |
+ if (requests_ == 1) |
+ job->set_is_first_job(true); |
} |
- CertVerifierRequest* request = |
- new CertVerifierRequest(callback, verify_result, net_log); |
- job->AddRequest(request); |
- *out_req = request; |
+ scoped_ptr<CertVerifierRequest> request = |
+ job->CreateRequest(callback, verify_result, net_log); |
+ *out_req = request.Pass(); |
return ERR_IO_PENDING; |
} |
-void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) { |
- DCHECK(CalledOnValidThread()); |
- CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); |
- request->Cancel(); |
-} |
- |
bool MultiThreadedCertVerifier::SupportsOCSPStapling() { |
return verify_proc_->SupportsOCSPStapling(); |
} |
@@ -574,45 +474,22 @@ bool MultiThreadedCertVerifier::RequestParams::operator<( |
other.hash_values.end(), SHA1HashValueLessThan()); |
} |
-// HandleResult is called by CertVerifierWorker on the origin message loop. |
-// It deletes CertVerifierJob. |
-void MultiThreadedCertVerifier::HandleResult( |
- X509Certificate* cert, |
- const std::string& hostname, |
- const std::string& ocsp_response, |
- int flags, |
- const CertificateList& additional_trust_anchors, |
- int error, |
- const CertVerifyResult& verify_result) { |
+void MultiThreadedCertVerifier::SaveResultToCache(const RequestParams& key, |
+ const CachedResult& result) { |
DCHECK(CalledOnValidThread()); |
- const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), hostname, |
- ocsp_response, flags, additional_trust_anchors); |
- |
- CachedResult cached_result; |
- cached_result.error = error; |
- cached_result.result = verify_result; |
base::Time now = base::Time::Now(); |
cache_.Put( |
- key, cached_result, CacheValidityPeriod(now), |
+ key, result, CacheValidityPeriod(now), |
CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs))); |
+} |
- std::map<RequestParams, CertVerifierJob*>::iterator j; |
- j = inflight_.find(key); |
- if (j == inflight_.end()) { |
- NOTREACHED(); |
- return; |
- } |
- CertVerifierJob* job = j->second; |
- inflight_.erase(j); |
- bool is_first_job = false; |
- if (first_job_ == job) { |
- is_first_job = true; |
- first_job_ = NULL; |
- } |
- |
- job->HandleResult(cached_result, is_first_job); |
- delete job; |
+scoped_ptr<CertVerifierJob> MultiThreadedCertVerifier::RemoveJob( |
+ CertVerifierJob* job) { |
+ DCHECK(CalledOnValidThread()); |
+ bool erased_job = inflight_.erase(job) == 1; |
+ DCHECK(erased_job); |
+ return make_scoped_ptr(job); |
} |
void MultiThreadedCertVerifier::OnCACertChanged( |
@@ -622,5 +499,24 @@ void MultiThreadedCertVerifier::OnCACertChanged( |
ClearCache(); |
} |
+struct MultiThreadedCertVerifier::JobToRequestParamsComparator { |
+ bool operator()(const CertVerifierJob* job, |
+ const MultiThreadedCertVerifier::RequestParams& value) const { |
+ return job->key() < value; |
+ } |
+}; |
+ |
+CertVerifierJob* MultiThreadedCertVerifier::FindJob(const RequestParams& key) { |
+ DCHECK(CalledOnValidThread()); |
+ |
+ // The JobSet is kept in sorted order so items can be found using binary |
+ // search. |
+ JobSet::iterator it = std::lower_bound(inflight_.begin(), inflight_.end(), |
+ key, JobToRequestParamsComparator()); |
+ if (it != inflight_.end() && !(key < (*it)->key())) |
+ return *it; |
+ return nullptr; |
+} |
+ |
} // namespace net |