Profile Architecture

 

Profile Architecture

This page details an ongoing design refactoring, started in January 2012.

Chromium has lots of features that hook into a Profile, a bundle of data about the current user and the current chrome session that can span multiple browser windows. When Chromium first started, the profile had only a few moving parts: the cookie jar, the history database, the bookmark database, and things to do with user preferences. In the three years of the Chromium Project, Profile became the join point for every feature, leading to things like Profile::GetInstantPromoCounter() or Proifle::GetHostContentSettingsMap(). As of this writing there are 58 pure virtual methods that start with "Get" in Profile.

Profile should be a minimal reference, a sort of handle object that doesn't own the world.


Design Goals

  • We must be able to move to the new architecture piece-wise. One service and feature at a time. We can not stop the world and convert everything in one operation. As of this writing, we've moved 19 services out of Profile.
    • We should only make small modifications at the callsite where a Profile is used to get the service in question.
  • We must fix Profile shutdown. When we started and only had a few objects hanging off of Profile, manual ordering was acceptable for destruction. Now we have over seventy five components and we know that our manual destruction ordering is incorrect as written today. We can not rely on manual ordering when we have so many components.
  • We must allow features to be compiled in and out. Now that we have chromium variants that don't contain all the features in a standard Windows/Mac/Linux Google Chrome build, we need a way to allow these variants to compile without #ifdefing profile.h and profile_impl.h into a mess. These variants also have their own services that they'd like to provide. (Letting chromium variants add their own services also touches on why we can't rely on manual ordering in Profile shutdown.)
    • Stretch goal: Separate features go in their own .a/.so files to further minimize our ridiculous linking time.

ProfileKeyedServiceFactory

The Old Way: Profile interface and ProfileImpl

In the previous design, services were fetched through an accessor on Profile:

class ProfileImpl {
  public:
    virtual FooService* GetFooService();
  private:
    scoped_ptr<FooService> foo_service_;
};

In the previous system, Profile was an interface with mostly pure virtual accessors. There were separate versions of Profile for Normal, Incognito and Testing profiles.

In this world, the Profile was the center of all activity. The profile owned all of its service and handed them out. Profile destruction was according to whatever order the services were listed in ProfileImpl. There wasn't a way for another variant to add its own services (or leave out ones it didn't need) without modifying the Profile interface.

The New Way: ProfileKeyedServiceFactory

Instead of having the Profile own FooService, we have a dedicated singleton FooServiceFactory, like this minimal one:

class FooServiceFactory : public ProfileKeyedServiceFactory {
 public:
  static FooService* GetForProfile(Profile* profile);

  static FooServiceFactory* GetInstance();

 private:
  friend struct DefaultSingletonTraits<FooServiceFactory>;

  FooServiceFactory();
  virtual ~FooServiceFactory();

  // ProfileKeyedServiceFactory:
  virtual ProfileKeyedService* BuildServiceInstanceFor(
    Profile* profile) const OVERRIDE;
};

We have a generalized ProfileKeyedServiceFactory which performs most of the work of associating a profile with an object provided by your BuildServiceInstanceFor() method. The ProfileKeyedServiceFactory provides an interface for you to override while managing the lifetime of your Service object in response to Profile lifetime events and making sure your service is shut down before services it depends on.

An absolutely minimal factory will supply the following methods:
  • A static GetInstance() method that refers to your Factory as a Singleton.
  • A constructor that associates this ProfileKeyedServiceFactory with the ProfileDependencyManager singleton, and makes DependsOn() declarations.
  • A GetForProfile() method that wrapsProfileKeyedServiceFactory, casting the result back to whatever type you need to return.
  • A BuildServiceInstanceFor() method which is called once by the framework for each |profile|, which must return a proper instance of your service.
In addition, ProfileKeyedServiceFactory provides these other knobs for how you can control behavior:
  • RegisterUserPrefs() is called once per Profile during initialization and is where you can place any user pref registration.
  • By default, PKSF return NULL when given an Incognito profile.
    • If you override ServiceRedirectedInIncognito() to return true, it will return the associated normal Profile's service.
    • If you override ServiceHasOwnInstanceInIncognito() to return true, it will create a new service for the incognito profile.
  • By default, PKSF will lazily create your service. If you override ServiceIsCreatedWithProfile() to return true, your service will be created alongside the profile.
  • PKSF gives you multiple ways to control behavior during unit tests. See the header for more details.
  • PKSF gives you a way to augment and tweak the shutdown and deallocation behavior.

A Brief Interlude About Complexity

So the above, from an implementation standpoint is significantly more complex than what came before it. Is all this really worth it?

Yes.

We absolutely have to address the interdependency of services. As it stands today, we do not shut down profiles after they are no longer needed in multiprofile mode because our crash rate when shutting down a profile is too high to ship to users. We have about 75 components that plug into the profile lifecycle and their dependency graph is complex enough that our naive manual ordering can not handle the complexity. All of the overrideable behavior above exists because it was implemented per service, ad hoc and copy pasted.

We likewise need to make it easy for other chromium variants to add their own features/compile features out of their build.

Dependency Management Overview

With that in mind, let's look at how dependency management works. There is a single ProfileDependencyManager singleton, which is what is alerted to Profile creation and destruction. A PKSF will register and unregister itself with the ProfileDependencyManager . The job of the ProfileDependencyManager is to make sure that individual services are created and destroyed in a safe ordering.

Consider the case of these three service factories:

AlphaServiceFactory::AlphaServiceFactory()
    : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
}

BetaServiceFactory::BetaServiceFactory()
    :
 ProfileKeyedServiceFactory( ProfileDependencyManager::GetInstance())  {
  DependsOn(AlphaServiceFactory::GetInstance());
     }

GammaServiceFactory::GammaServiceFactory()
    :
 ProfileKeyedServiceFactory( ProfileDependencyManager::GetInstance())  {
  DependsOn(BetaServiceFactory::GetInstance());
     }

The explicitly stated dependencies in this simplified graph mean that the only valid creation order for services is [Alpha, Beta, Gamma] and the destruction order is [Gamma, Beta, Alpha]. The above is all you, as a user of the framework, have to do to specify dependencies.

Behind the scenes, ProfileDependencyManager takes the stated dependency edges, performs a Kahn topological sort, and uses that in CreateProfileServices() and DestroyProfileServices() .

The Five Minute Tutorial of How to Convert Your Code

  1. Make Your Existing FooService derive from ProfileKeyedService.
  2. If possible, make your FooService no longer refcounted. Most of the refcounted objects that hang off of Profile appear to be that way because they aren't using base::bind/WeakPtrFactory instead of needing to own data on multiple threads.
  3. Build a simple FooServiceFactory derived from ProfileKeyedServiceFactory. Your FooServiceFactory will be the main access point consumers will ask for FooService. ProfileKeyedServiceFactory gives you a bunch of virtual methods that control behavior.
    1. ProfileKeyedService* ProfileKeyedServiceFactory::BuildServiceInstanceFor(Profile* profile) is the only required method. Given a Profile handle, return a valid FooService.
    2. You can control the incognito behavior with ServiceRedirectedInIncognito() and ServiceHasOwnInstanceInIncognito().
  4. Add your service to the AssertFactoriesBuilt() list in profile_dependency_manager.cc.
  5. Understand shutdown behavior. For historical reasons, we have a two phase deletion process:
    1. Every ProfileKeyedService will first have its Shutdown() method called. Use this method to drop weak references to the Profile or other service objects.
    2. Every ProfileKeyedService is deleted and its destructor is run. Minimal work should be done here. Attempts to call any *ServiceFactory::GetForProfile() will cause an assertion in debug mode.
  6. Change each instance of "profile_->GetFooService()" to "FooServiceFactory::GetForProfile(profile_)".
If you need an example of what the above looks like, try looking at these patches:
  • r100516: A simple example, adding a new ProfileKeyedService. This shows off a minimal ServiceFactory subclass.
  • r104806: plugin_prefs_factory.h gives an example of how to deal with things that are (and have to stay) refcounted. This patch also shows off how to move your preferences into your ProfileKeyedServiceFactory.

Debugging Tips

Using the dependency visualizer

Chrome has a built in method to dump the profile dependency graph to a file inGraphViz format. When you run chrome with the command line flag   --dump-profile-graph , chrome will write the dependency information to your /path/to/profile/profile-dependencies.dot file. You can then convert this text file with dot, which is part of GraphViz:

dot -Tpng /path/to/profile/profile-dependencies.dot > png-file.png

This will give you a visual graph like this (generated January 23rd, 2012, click through for full size):


Crashses at Shutdown

If you get a stack that looks like this:

ProfileDependencyManager::AssertProfileWasntDestroyed()
ProfileKeyedServiceFactory::GetServiceForProfile()
MyServiceFactory::GetForProfile()
... [Probably a bunch of frames] ...
OtherService::~OtherService()
ProfileKeyedServiceFactory::ProfileDestroyed()
ProfileDependencyManager::DestroyProfileServices()
ProfileImpl::~ProfileImpl()

The problem is that OtherSerivce is improperly depending on MyService. The framework asserts if you try to use a Shutdown()ed component.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值