blob: 3615379a7584f081735fa26a7b51884a2d98bae4 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/webdata/common/web_data_request_manager.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/profiler/scoped_tracker.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
////////////////////////////////////////////////////////////////////////////////
//
// WebDataRequest implementation.
//
////////////////////////////////////////////////////////////////////////////////
WebDataRequest::~WebDataRequest() {
if (IsActive()) {
manager_->CancelRequest(handle_);
}
}
WebDataServiceBase::Handle WebDataRequest::GetHandle() const {
return handle_;
}
bool WebDataRequest::IsActive() const {
return manager_ != nullptr;
}
WebDataRequest::WebDataRequest(WebDataRequestManager* manager,
WebDataServiceConsumer* consumer)
: task_runner_(base::ThreadTaskRunnerHandle::Get()),
manager_(manager),
consumer_(consumer),
handle_(0) {
DCHECK(IsActive());
}
void WebDataRequest::AssertThreadSafe() const {
// Manipulations of the request state should only happen when the request is
// active and the manager's lock is held (i.e., during request cancellation
// or completion).
DCHECK(IsActive());
manager_->AssertLockedByCurrentThread();
}
WebDataServiceConsumer* WebDataRequest::GetConsumer() const {
AssertThreadSafe();
return consumer_;
}
scoped_refptr<base::SingleThreadTaskRunner> WebDataRequest::GetTaskRunner()
const {
return task_runner_;
}
void WebDataRequest::MarkAsInactive() {
AssertThreadSafe();
consumer_ = nullptr;
manager_ = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
//
// WebDataRequestManager implementation.
//
////////////////////////////////////////////////////////////////////////////////
WebDataRequestManager::WebDataRequestManager()
: next_request_handle_(1) {
}
std::unique_ptr<WebDataRequest> WebDataRequestManager::NewRequest(
WebDataServiceConsumer* consumer) {
std::unique_ptr<WebDataRequest> request(new WebDataRequest(this, consumer));
// Grab the lock to get the next request handle and register the request.
base::AutoLock l(pending_lock_);
request->handle_ = next_request_handle_++;
pending_requests_[request->handle_] = request.get();
return request;
}
void WebDataRequestManager::CancelRequest(WebDataServiceBase::Handle h) {
base::AutoLock l(pending_lock_);
// If the request was already cancelled, or has already completed, it won't
// be in the pending_requests_ collection any more.
auto i = pending_requests_.find(h);
if (i == pending_requests_.end())
return;
i->second->MarkAsInactive();
pending_requests_.erase(i);
}
void WebDataRequestManager::RequestCompleted(
std::unique_ptr<WebDataRequest> request,
std::unique_ptr<WDTypedResult> result) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
request->GetTaskRunner();
task_runner->PostTask(
FROM_HERE,
base::Bind(&WebDataRequestManager::RequestCompletedOnThread, this,
base::Passed(&request), base::Passed(&result)));
}
void WebDataRequestManager::AssertLockedByCurrentThread() const {
pending_lock_.AssertAcquired();
}
WebDataRequestManager::~WebDataRequestManager() {
base::AutoLock l(pending_lock_);
for (auto i = pending_requests_.begin(); i != pending_requests_.end(); ++i)
i->second->MarkAsInactive();
pending_requests_.clear();
}
void WebDataRequestManager::RequestCompletedOnThread(
std::unique_ptr<WebDataRequest> request,
std::unique_ptr<WDTypedResult> result) {
// If the request is already inactive, early exit to avoid contending for the
// lock (aka, the double checked locking pattern).
if (!request->IsActive())
return;
// Used to export the consumer from the locked region below so that the
// consumer can be notified outside of the lock and after the request has
// been marked as inactive.
WebDataServiceConsumer* consumer;
// Manipulate the pending_requests_ collection while holding the lock.
{
base::AutoLock l(pending_lock_);
// Re-check whether the request is active. It might have been cancelled in
// another thread before the lock was acquired.
if (!request->IsActive())
return;
// Remember the consumer for notification below.
consumer = request->GetConsumer();
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
// is fixed.
tracked_objects::ScopedTracker tracking_profile(
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422460 "
"WebDataRequestManager::RequestCompletedOnThread::UpdateMap"));
auto i = pending_requests_.find(request->GetHandle());
DCHECK(i != pending_requests_.end());
// Take ownership of the request object and remove it from the map.
pending_requests_.erase(i);
// The request is no longer active.
request->MarkAsInactive();
}
// Notify the consumer if needed.
//
// NOTE: The pending_lock_ is no longer held here. It's up to the consumer to
// be appropriately thread safe.
if (consumer) {
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
// is fixed.
tracked_objects::ScopedTracker tracking_profile(
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422460 "
"WebDataRequestManager::RequestCompletedOnThread::NotifyConsumer"));
consumer->OnWebDataServiceRequestDone(request->GetHandle(),
std::move(result));
}
}