blob: 665a6e388b3569a4996daccbc6f23c4c99d4f9f5 [file] [log] [blame]
// Copyright 2016 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 "content/common/service_manager/embedded_service_runner.h"
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "services/service_manager/public/cpp/service_context.h"
namespace content {
class EmbeddedServiceRunner::InstanceManager
: public base::RefCountedThreadSafe<InstanceManager> {
public:
InstanceManager(const base::StringPiece& name,
const ServiceInfo& info,
const base::Closure& quit_closure)
: name_(name.as_string()),
factory_callback_(info.factory),
use_own_thread_(!info.task_runner && info.use_own_thread),
quit_closure_(quit_closure),
quit_task_runner_(base::ThreadTaskRunnerHandle::Get()),
service_task_runner_(info.task_runner) {
if (!use_own_thread_ && !service_task_runner_)
service_task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
void BindServiceRequest(service_manager::mojom::ServiceRequest request) {
DCHECK(runner_thread_checker_.CalledOnValidThread());
if (use_own_thread_ && !thread_) {
// Start a new thread if necessary.
thread_.reset(new base::Thread(name_));
thread_->Start();
service_task_runner_ = thread_->task_runner();
}
DCHECK(service_task_runner_);
service_task_runner_->PostTask(
FROM_HERE,
base::Bind(&InstanceManager::BindServiceRequestOnServiceThread,
this, base::Passed(&request)));
}
void ShutDown() {
DCHECK(runner_thread_checker_.CalledOnValidThread());
if (!service_task_runner_)
return;
// Any extant ServiceContexts must be destroyed on the application thread.
if (service_task_runner_->BelongsToCurrentThread()) {
QuitOnServiceThread();
} else {
service_task_runner_->PostTask(
FROM_HERE, base::Bind(&InstanceManager::QuitOnServiceThread, this));
}
}
private:
friend class base::RefCountedThreadSafe<InstanceManager>;
~InstanceManager() {
// If this instance had its own thread, it MUST be explicitly destroyed by
// QuitOnRunnerThread() by the time this destructor is run.
DCHECK(!thread_);
}
void BindServiceRequestOnServiceThread(
service_manager::mojom::ServiceRequest request) {
DCHECK(service_task_runner_->BelongsToCurrentThread());
int instance_id = next_instance_id_++;
std::unique_ptr<service_manager::ServiceContext> context =
base::MakeUnique<service_manager::ServiceContext>(
factory_callback_.Run(), std::move(request));
service_manager::ServiceContext* raw_context = context.get();
context->SetQuitClosure(
base::Bind(&InstanceManager::OnInstanceLost, this, instance_id));
contexts_.insert(std::make_pair(raw_context, std::move(context)));
id_to_context_map_.insert(std::make_pair(instance_id, raw_context));
}
void OnInstanceLost(int instance_id) {
DCHECK(service_task_runner_->BelongsToCurrentThread());
auto id_iter = id_to_context_map_.find(instance_id);
CHECK(id_iter != id_to_context_map_.end());
auto context_iter = contexts_.find(id_iter->second);
CHECK(context_iter != contexts_.end());
contexts_.erase(context_iter);
id_to_context_map_.erase(id_iter);
// If we've lost the last instance, run the quit closure.
if (contexts_.empty())
QuitOnServiceThread();
}
void QuitOnServiceThread() {
DCHECK(service_task_runner_->BelongsToCurrentThread());
contexts_.clear();
if (quit_task_runner_->BelongsToCurrentThread()) {
QuitOnRunnerThread();
} else {
quit_task_runner_->PostTask(
FROM_HERE, base::Bind(&InstanceManager::QuitOnRunnerThread, this));
}
}
void QuitOnRunnerThread() {
DCHECK(runner_thread_checker_.CalledOnValidThread());
if (thread_) {
thread_.reset();
service_task_runner_ = nullptr;
}
quit_closure_.Run();
}
const std::string name_;
const ServiceInfo::ServiceFactory factory_callback_;
const bool use_own_thread_;
const base::Closure quit_closure_;
const scoped_refptr<base::SingleThreadTaskRunner> quit_task_runner_;
// Thread checker used to ensure certain operations happen only on the
// runner's (i.e. our owner's) thread.
base::ThreadChecker runner_thread_checker_;
// These fields must only be accessed from the runner's thread.
std::unique_ptr<base::Thread> thread_;
scoped_refptr<base::SingleThreadTaskRunner> service_task_runner_;
// These fields must only be accessed from the service thread, except in
// the destructor which may run on either the runner thread or the service
// thread.
// A map which owns all existing Service instances for this service.
using ServiceContextMap =
std::map<service_manager::ServiceContext*,
std::unique_ptr<service_manager::ServiceContext>>;
ServiceContextMap contexts_;
int next_instance_id_ = 0;
// A mapping from instance ID to (not owned) ServiceContext.
//
// TODO(rockot): Remove this once we get rid of the quit closure argument to
// service factory functions.
std::map<int, service_manager::ServiceContext*> id_to_context_map_;
DISALLOW_COPY_AND_ASSIGN(InstanceManager);
};
EmbeddedServiceRunner::EmbeddedServiceRunner(const base::StringPiece& name,
const ServiceInfo& info)
: weak_factory_(this) {
instance_manager_ = new InstanceManager(
name, info, base::Bind(&EmbeddedServiceRunner::OnQuit,
weak_factory_.GetWeakPtr()));
}
EmbeddedServiceRunner::~EmbeddedServiceRunner() {
instance_manager_->ShutDown();
}
void EmbeddedServiceRunner::BindServiceRequest(
service_manager::mojom::ServiceRequest request) {
instance_manager_->BindServiceRequest(std::move(request));
}
void EmbeddedServiceRunner::SetQuitClosure(
const base::Closure& quit_closure) {
quit_closure_ = quit_closure;
}
void EmbeddedServiceRunner::OnQuit() {
if (!quit_closure_.is_null())
quit_closure_.Run();
}
} // namespace content