Chromium Profile设计思想

设计解析

Chromium的很多功能依赖于Profile对象。Profile是一个上下文对象,类间关系为:

 

例如,浏览器的历史记录、书签、cookie记录等功能都是深深依赖Profile对象。在最初版本的实现中,Profile class承载了所有这些功能。于是随着浏览器的迭代,Profile越来越庞大,它直接依赖的子模块也越来越多。Profile类的可维护性变差,子模块的启动和结束顺序靠手工维护变得越来越不现实。

设计目标

  • 把 Profile控制在可维护的大小内
  • Service的启动和结束要健壮可控
  • 支持功能的可选编译(为了跨平台,不是本方案的重点)

引入KeyedService & KeyedServiceFactory

  • 所有功能Service类都继承自KeyedService;
  • 每一个Service要有自己的ServiceFactory, ServiceFactory的职责是创建和销毁Service实例。

比如我们要实现一个Superman功能,并且我们需要存储一些状态在Profile里。通常我们会实现一个SupermanService

// 功能类
class SupermanService: public KeyedService{
...
}

// 工厂类
class SupermanServiceFactory: public KeyedServiceFactory {
	// 单例方法
  static SupermanServiceFactory* GetInstance() {...}
  // 销毁Service, 调用每一个Service实现的自己的Shutdown()方法
  void ContextShutdown() {...}
}

功能类的基类(KeyedService)

KeyedService的特点

  • 虚析构函数
  • 生命周期由 BrowserContextKeyedServiceFactory 单例管理
  • 有一个可覆写的接口Shutdown(), 在析构函数之前被调用,实现two-phase shutdown

工厂类

至少提供以下4个接口:

  • A static GetInstance() method that refers to the factory as a singleton.
  • A constructor that associates the factory with the BrowserContextDependencyManager singleton, and makes DependsOn() declarations.
  • GetForProfile() method that wraps BCKSF, casting the result back to whatever type we need to return.
  • BuildServiceInstanceFor() method which is called once by the framework for each profile. It must initialize and return a proper instance of our service.

使用Service

用GetForProfile()接口

ServiceFactory::GetForProfile(profile)

Two-phase Shutdown

在调用析构函数之前,先调用Service的Shutdown()接口。每一个Service都会实现自己的Shutdown()接口,其职责是清理资源。

Shutdown()接口是虚函数,在基类被调用,因此利用C++的多态可以实现Shutdown的自动调用。

正是因为在Shutdown()中做了清理工作,所以解决了环形依赖问题

环形依赖问题:

对象以shared_ptr使用并相互依赖,shared_ptr的ref_count为0才会调用析构函数。如果使用Single-phase shutdown,也即仅仅依赖析构函数,如果shared_ptr之间存在环形依赖,是无法正确析构的。

Service实现的Shutdown()接口在ServiceFactory工厂类中被调用,关键代码:

// keyed_service_factory.cc
void KeyedServiceFactory::ContextShutdown(void* context) {
  auto iterator = mapping_.find(context);
  if (iterator != mapping_.end() && iterator->second)
    iterator->second->Shutdown();
}

为什么需要KeyedServiceFactory mapping_字段

KeyedServiceFactory是单例,关键代码:(以SyncServiceFactory为例)

// static
SyncServiceFactory* SyncServiceFactory::GetInstance() {
  // 单例调用
  return base::Singleton<SyncServiceFactory>::get();
}

但是具体的Service不是单例,service实例跟context是1对1的关系,所以用mapping_记录。关键代码:

// components/keyed_service/core/keyed_service_factory.cc

KeyedService* KeyedServiceFactory::GetServiceForContext(void* context,
                                                        bool create) {
...
auto iterator = mapping_.find(context);
  if (iterator != mapping_.end())
    return iterator->second.get(); // mapping_命中,直接返回

  // Object not found.
  if (!create)
    return nullptr;  // And we're forbidden from creating one.

  // Create new object.
  // Check to see if we have a per-context testing factory that we should use
  // instead of default behavior.
  // 新建service实例
  std::unique_ptr<KeyedService> service;
  auto factory_iterator = testing_factories_.find(context);
  if (factory_iterator != testing_factories_.end()) {
    if (factory_iterator->second) {
      service = factory_iterator->second.Run(context);
    }
  } else {
    service = BuildServiceInstanceFor(context);
  }
  // 建立context<->service的一对一关系,更新mapping_
  return Associate(context, std::move(service));
}

Service的设计思想

工厂+单例。

工厂类实例化时用DependsOn声明依赖关系,在析构阶段利用拓扑排序实现按序析构。

所有的KeyedServiceBaseFactory之间的依赖关系由 DependencyManager管理。内部有一个DependencyGraph结构,被依赖(depended)和依赖方(dependee)的构造和析构顺序:

  • 所有depended在dependee之后构造,在dependee之前析构

正确顺序的保证由DependencyGraph 基于拓扑排序保证。

Service初始化的全部流程:

 

软件设计启示

  • 如果一个实体需要承载多种不同的功能,可以把功能分类,引入Service将其拆分。
  • Service承载具体的功能实现,用ServiceFactory工厂类管理Service的构造和清除(Two-phase Shutdown)。
  • 基于有向无环图(DAG)的拓扑排序保证Service的构造和析构顺序。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光机121906

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值