Chrome kitchen solution source (1) - threading model

  Many people like Chrome, like its simplicity, like its "fast." I glance simple, needless to say, the focus here say it is "fast." What is a "fast" mean? The first reaction is probably a lot of people on the Nikkei Javascript cnBeta run sub-paste, Chrome called a Kuaia. (In fact, each time point to open this type of article, I want to sympathize with what we are poor students of IE, but recently it makes IE9 really looking forward to.) But Javascript running points really so important to you, we really You can understand the big gap it? Anyway, I do not feel it. Why the hell said that Chrome fast? Chrome quickly fast on UI Responsive (UI response). It means you double-click the desktop icon, it can quickly start up (Firefox users should emotionally bar); click on a hyperlink, it can give you a moment to pop up a new page (with IE friend did not encounter less the phenomenon of suspended animation over it). Therefore, Chrome is not just a run-point tool. That's how good is Chrome UI Responsive do it? This would be a model thanks to its thread.

  In order to avoid reinventing the wheel, here I do not intend to discuss the overall framework of Chrome threading model, but will focus more on the specific implementation . Chrome threading model of the framework are not familiar with friends can read the duguguiyu of this article . Conditional friends can go here to download a Chromium source code, or to here viewed online. Without further ado, Let's Go!

 

NO.1 Smart Pointers 

Chromium source code throughout the hundreds of thousands of documents should have it, the face of such vast sea of ​​code, we where to start? Old saying, we must first of its profits. We have the most number of smart pointer from the start Chromium with it.

Scoped_ptr look (see base / scoped_ptr.h). This is when a destructor will automatically remove the smart pointer holding the object. Their behavior and std :: tr1 :: scoped_ptr basically the same, not to say any more.

Chromium in a more interesting pointers are used scoped_refptr (see base / ref_counted.h). And scoped_ptr differ slightly, scoped_refptr achieved only refer explicitly reference count type interface:

ExpandedBlockStart.gif Code
template  < class  T >
class  scoped_refptr {
 
public :
  scoped_refptr(T
*  p) : ptr_(p) {
    
if  (ptr_)
      ptr_
-> AddRef();
  }
  
~ scoped_refptr() {
    
if  (ptr_)
      ptr_
-> Release();
  }
 
protected :
  T
*  ptr_;
};

   As described above, class T must explicitly AddRef and Release implement interfaces, constructors and destructors in scoped_refptr when they are called for increasing / decreasing the reference count. When an object reference count is 0, class T is responsible for the Release method will automatically delete the object. Many people could not help but ask, then write a class T would not be too much trouble. Do not worry, Google already provides two default implementations for us: RefCounted and RefCountedThreadSafe (both in the base / ref_counted.h in), we can inherit them to make our class implements reference counting function.

RefCounted relatively simple, but it is worth noting that it does not guarantee thread safety, it can only be applied to an object held by a single thread. If you need an object in multiple threads can be cited, how to do it? Then RefCountedThreadSafe comes in handy (personally do not like the name, perhaps called ThreadSafeRefCounted would be more appropriate).

ExpandedBlockStart.gif Code
template  < class  T, typename Traits  =  DefaultRefCountedThreadSafeTraits < T >   >
class  RefCountedThreadSafe :  public  subtle::RefCountedThreadSafeBase {
 
public :
  
void  AddRef() {
    subtle::RefCountedThreadSafeBase::AddRef();
  }
  
void  Release() {
    
if  (subtle::RefCountedThreadSafeBase::Release()) {
      Traits::Destruct(static_cast
< T *> ( this ));
    }
  }
};
void  RefCountedThreadSafeBase::AddRef() {
  AtomicRefCountInc(
& ref_count_);
}

  We can see, RefCountedThreadSafe when adding and deleting references to technology, have adopted the atomic operations to ensure the thread safety.

Of course, if you think RefCountedThreadSafe It's that simple, you underestimate it . A closer look at the type of instantiation, in addition to passing a class T, there is Traits. Traits that this is What happens to it? Note Release method, when the reference count is 0, it is used Traits to be responsible for deleting the object. This gives us the opportunity to delete a custom object, which in many cases is very useful. For example, in general, many of the UI object is not beyond the destruction of the main thread. Once we gave it to scoped_refptr to manage, we can not guarantee that it will reference count is set to 0 in which thread, and then need to do is use a custom Traits to send a message to the main thread, so that the main thread is responsible to remove the object, which avoids generating many problems.

I write to you, can not help but think a little compare scoped_refptr :: tr1 :: shared_ptr and std . Also used to achieve reference count of the object is automatically deleted, but requires scoped_refptr objects are referenced explicitly AddRef and Release implement interfaces, but also the object destructor private, and the scoped_refptr to friend; only the shared_ptr the object is to provide a public claim destructor on it. At first glance, it seems that a lot of shared_ptr elegance. But it is elegant on this surface, so many people intentionally and unintentionally very easy to ignore the object may be used in a multithreaded environment, which gives us countless troubles. Compared to this elegant, I personally appreciate that there is a clear scoped_refptr semantics, and not prone to misuse design. Another point is that the above-mentioned Traits, this is a very useful feature. Unfortunately, if we object is delivered to the shared_ptr, there is no way to go to control its destructed. If not, you see this code, maybe I would not think the original shared_ptr there are so many unsatisfactory, so it is strongly recommended that you read this and more to high-quality code.

There is also a pointer is WeakPtr (base / weak_ptr.h), described here do not need to know friends can read its own code. 

 

No.2 Tasks 

With smart pointer knowledge as a foundation, we are going to take a look at the Task achieved in the Chrome.

Task class (base / task.h) package is very simple, as follows:

class  Task :  public  tracked_objects::Tracked {
 
public :
  
virtual   ~ Task();
  
//  Tasks are automatically deleted after Run is called.
   virtual   void  Run()  =   0 ;
};

  Little can be said that under it inherits Tracked class, which is used for debugging and performance tracking used. It records the time Task generation and destruction, and where the Post message to the thread to loop (via __LINE__, __FILE__ like macros defined by the compiler). More details can be found in base / tracked.h and related documents. Others, such as CancelableTask, DeleteTask and other sub-interface and subclasses are also a small role, nothing more to say.

Read achieve the above, a lot of people may ask: If every other thread I want to distribute a task, we must write a class to implement the Task interfaces, is it annoying? If you have the same concerns, then congratulations, you and Chromium designers think of a go. task.h NewRunnableMethod also provides many ways to allow us to easily use an object and its methods to construct Task.

ExpandedBlockStart.gif Code
template  < class  T,  class  Method,  class  A >
inline CancelableTask
*  NewRunnableMethod(T *   object , Method method,  const  A &  a) {
  
return   new  RunnableMethod < T, Method, Tuple1 < A >   > ( object , method, MakeTuple(a));
}

template 
< class  T,  class  Method,  class  Params >
class  RunnableMethod :  public  CancelableTask {
 
public :
  RunnableMethod(T
*  obj, Method meth,  const  Params &   params )
      : obj_(obj), meth_(meth), params_(
params ) {
    traits_.RetainCallee(obj_);
    COMPILE_ASSERT((MethodUsesScopedRefptrCorrectly
< Method, Params > ::value),
                   badrunnablemethodparams);
  }
 
private :
  T
*  obj_;
  Method meth_;
  Params params_;
  RunnableMethodTraits
< T >  traits_;
};

  Note that the phrase: COMPILE_ASSERT ((MethodUsesScopedRefptrCorrectly <Method, Params> :: value), badrunnablemethodparams). Meaning of this sentence is to ensure that all incoming Method pointer type parameters are scoped_refptr, otherwise the compiler will report an error. (About how to compile Assert, access to source code or can self refer "Modern C ++ Design") Careful friends you may also find: how there is also a Traits? Just as its name describes all Traits in fact, are just some of the tricks. RunnableMethodTraits nothing more than when it comes to multi-threaded, whether of Class T implements RefCountedThreadSafe interface to do some checks (checks only in the Debug version). Further, the method of passing Method parameters are copied (copy pointer is a value, instead of pointing to the object), if you have some type of custom to use a special copy semantics, can go on to do TrupleTraits template specialization.

With these, our Task realize it is not complete it? The answer is clearly no. We are likely to build up a Task and distributing it to the specified thread with NewRunnableMethod (new T, & T :: SomeMethod). But the problem is that if before the Task execution, new T created out of the object has been destroyed it? That Task held not to become a Pending Pointer, which will lead to Access Violation not such a serious problem? So Chromium provides us with another set of solutions: Scoped factories. The principle of not reinventing the wheel, and willing to do to learn more about a friend, please read Optman of this article .

 

No.3 Cross-Thread Request

  Chrome, all threads have a clear division of labor, such as UI thread is responsible for responding to user requests, File thread is responsible for access to the file system.

A very natural situation is that the user might want to view the contents of a file. In Chrome, the UI thread to complete the task need to add a Task to read the file File thread's message queue, then the Task will be performed (if there are other threads File Task, possibly in File thread need to wait). Etc. After this Task executed, and then further UI thread's message queue to add another Task specific content for display files. Wait until the contents of the file Task executed, the file was only displayed. It looks very complicated, right? And among these processes, there is no way to ensure that all threads have not been destructed. Of course, the designer is not possible to put such complex functions directly exposed to the user. CancelableRequest class is used to encapsulate this complexity. Thanks Optman again, his article for us to elaborate on the implementation CancelableRequest class.

But if we just know how to achieve, not to care about Why do so, it became the sage who said learning without thinking, the inevitable consequence is "indiscriminately" the. After not think relative to read the file directly to the UI thread, Chrome toss so a lot of things, but many threads per cent more time scheduling and Task waiting to do? That it is not in Scrapped it? Savor, it is entirely justified. It is because of this design, which makes Chrome has excellent UI Responsive. We know, UI unresponsive to perform time-consuming operation that is the culprit in the UI thread. Read the file in the UI thread, the most immediate consequence is that users easily find the card. You might question, if I read it just a small file, so users should not feel completely ah? However, if this time your Kaspersky "Kaka Ka" to scan your hard drive it, your program in such extreme hard disk IO below can maintain good responsiveness it? Look Chrome, no matter how hard you "click", UI thread just to go back and add in response to user actions to File a Task thread after, but after reading a good File thread, can be quickly updated. Perhaps the operation from the user to the real contents of the file to be displayed on the time, Chrome still to be longer, but this time, the user is able to smoothly switch tabs, open a new page and so on. But if you use the UI thread directly access files, which can not be done.

 

No.4 Sample

Although there MessageLoop, Thread and other categories did not say, but we have to understand without prejudice to the example of the practical. Many people may still have questions, the threading model in Chrome, in the end is how to avoid the use of locks? This would benefit from it provided that the object can only survive in a thread in the object design. You may say so or not understood. There's an old saying, Yihuhuhuapiao. Now we put out a gourd Chou Chou long-sawed in the end. The next administration will Chrome history as an example to be described in detail. (See specific code and history.h history.cc chrome / browser at / history directory.)

  history.h there is an important class HistoryService, it is run and provide services to the UI in the UI thread. It has two important members thread_ and history_backend_. thread_ is used to initialize the thread to access a background history (History threads), and history_backend_ is diligently working on this thread to help HistoryService complete real data access. This is called object can only survive in a thread: HistoryService survive in the UI thread, and history_backend_ is to survive in the History thread. Notice how history_backend_ is initialized and deleted.

ExpandedBlockStart.gif Code
class  HistoryService :  public  CancelableRequestProvider,
                       
public  NotificationObserver,
                       
public   base ::RefCountedThreadSafe < HistoryService >  {
  .........
 
private :
  scoped_refptr
< history::HistoryBackend >  history_backend_;
  
base ::Thread *  thread_;
};

HistoryService::HistoryService()
    : thread_(
new   base ::Thread(kHistoryThreadName)) ... {
  ......
}

bool  HistoryService::Init(...) {
  
if  ( ! thread_ -> Start()) {
    Cleanup();
    
return   false ;
  }
  ......
  
//  Create the history backend.
  LoadBackendIfNecessary();
  
return   true ;
}

void  HistoryService::LoadBackendIfNecessary() {
  
if  ( ! thread_  ||  history_backend_)
    
return ;   //  Failed to init, or already started loading.

  scoped_refptr
< HistoryBackend >  backend( new  HistoryBackend(...));
  history_backend_.swap(backend);

  ScheduleAndForget(PRIORITY_UI, 
& HistoryBackend::Init, no_db_);
}

HistoryService::
~ HistoryService() {
  Cleanup();
}

void  HistoryService::Cleanup() {
  
if  ( ! thread_) {
    
//  We've already cleaned up.
     return ;
  }
  
//  Unload the backend.
  UnloadBackend();
  
base ::Thread *  thread  =  thread_;
  thread_ 
=  NULL;
  delete thread;
}

void  HistoryService::UnloadBackend() {
  
if  ( ! history_backend_)
    
return ;   //  Already unloaded.
  Task *  closing_task  =  NewRunnableMethod(history_backend_. get (),  & HistoryBackend::Closing);
  history_backend_ 
=  NULL;
  ScheduleTask(PRIORITY_NORMAL, closing_task);
}

   It should be noted that, when history_backend_ structure simply all set to NULL pointer member without specific initialization operation (HistoryBackend :: Init method), which not only ensures the UI thread can be done quickly without having to wait when constructing HistoryService , but also because it requires the thread thread_ represented in to complete the initialization. When HistoryService HistoryBackend the method needs to call, but also to the message loop thread_ distribution Task to complete asynchronously through. HistoryService on how to distribute tasks to be launched on thread_ and Cross-Thread Request, please refer to the code on their own, are basically using the methods mentioned above, is better understood.


      Pull so much, the summary of what the. It seems to have no special feeling to be made, but that the code is really high quality and very subtle, a little finishing can also be used in our own projects. Of course, please remember to follow the rules of the open source game. To learn more about their friends can go to read the code. I am limited, as described above, there inappropriate places, please feel free to point out. 

Reproduced in: https: //www.cnblogs.com/marco/archive/2010/09/09/1820724.html

Guess you like

Origin blog.csdn.net/weixin_34050427/article/details/94448868