Chromium on Android: Chromium線程局部儲存系統

來源:互聯網
上載者:User

標籤:chromium   thread local storage   

線程局部儲存(Thread Local Storage), 簡稱TLS,提供了一種儲存線程私人資料的方式,每個線程的私人資料對其他線程均不可見。Chromium是一個多進程多線程架構的瀏覽器,運行時會建立多達30幾個線程,其中很多線程需要擁有自己私人資料,在TLS數量有限的系統上,例如Android 4.3或更早的系統,可能會因為無法分配足夠的TLS而導致Chromium崩潰。本文將介紹最近在Chromium提交代碼中是如何解決這個問題的。

TLS在Chromium中的使用

Chromium代碼中,有些類提供了一個current()方法用來返回調用線程的私人資料,最典型的兩個例子是MessageLoop和RenderThread。以MessageLoop為例,因為每個線程可能運行著一個主訊息迴圈,如何能夠擷取與線程相關的主訊息迴圈呢?為此,MessageLoop提供了current()方法,當線程在建立MessageLoop執行個體時,會將這個執行個體設定為線程的私人資料,當調用current()方法時,再將這個私人資料返回給調用者。

每個TLS槽都由一個唯一的索引值(key)標識,這個索引值由進程負責向OS申請分配,如果當前進程申請的索引值數量超過系統規定的上限,申請將失敗,即無法擷取一個新的TLS槽。線程可以將TLS槽綁定到一個私人資料上,這個私人資料實際上是一個指標,指向一塊由調用線程動態分配的記憶體塊。

Chromium代碼中,對TLS的使用都抽象在ThreadLocalPointer模板類中(參見base/threading/thread_local.h檔案):

template <typename Type>class ThreadLocalPointer { public:  ThreadLocalPointer() : slot_() {    internal::ThreadLocalPlatform::AllocateSlot(slot_);  }  ~ThreadLocalPointer() {    internal::ThreadLocalPlatform::FreeSlot(slot_);  }  Type* Get() {    return static_cast<Type*>(        internal::ThreadLocalPlatform::GetValueFromSlot(slot_));  }  void Set(Type* ptr) {    internal::ThreadLocalPlatform::SetValueInSlot(        slot_, const_cast<void*>(static_cast<const void*>(ptr)));  } private:  typedef internal::ThreadLocalPlatform::SlotType SlotType;  SlotType slot_;  DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);};

其中SlotType是對TLS槽(索引值)類型的抽象,internal::ThreadLocalPlatform是對特定平台的TLS實現的抽象。例如,在POSIX系統上,ThreadLocalPlatform::AllocateSlot的實現為:

void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {  int error = pthread_key_create(&slot, NULL);  CHECK_EQ(error, 0);}

一般地,會結合LazyInstance使用ThreadLocalPointer,例如:

staticbase::LazyInstance<base::ThreadLocalPointer<RenderThread> >lazy_tls =   LAZY_INSTANCE_INITIALIZER; RenderThread* RenderThread::current() { return lazy_tls.Pointer()->Get();} RenderThread::RenderThread() { lazy_tls.Pointer()->Set(this);} RenderThread::~RenderThread() { lazy_tls.Pointer()->Set(NULL);}

當建立RenderThread執行個體時,當前線程會通過訪問lazy_tls設定線程的私人資料,此後每次調用current()方法時,都會返回線程私人的RenderThread執行個體。

由於LazyInstance是延遲初始化的,上述代碼中,當首次訪問Pointer()時,會建立一個新的ThreadLocalPointer執行個體,也就是向OS申請分配一個新的TLS槽。

潛在的問題

在三大案頭作業系統上(Windows/Linux/Mac),上述對ThreadLocalPointer的封裝和實現都能工作的很好,但自從Chromium支援Android之後,潛伏在上述實現中的問題就浮出水面了。

如果一個線程中需要儲存多個私人資料,比如除了RenderThread執行個體,還有MessageLoop執行個體等等,每個都要系統分配給進程的TLS索引值,那麼一旦索引值的資料達到上限,申請失敗導致程式崩潰。這個問題已經在Chromium WebView中暴露了,原因是Android 4.3或更老的系統,最大可用的TLS槽數量只有64個,除去Android系統庫(opengl,jvm)需要佔用一部分之外,留給Chromium使用的數量已經不多了,一旦發現分配不成功,Chromium會觸發CHECK,程式立即異常退出。Android 4.4系統上,最大可用的TLS槽數量多達128個,這個問題得到一定的緩解,但仍沒有從根本上杜絕這個問題。

解決方案

Chromium提供了一種新的方式解決上述問題,主要思路是Chromium自己管理TLS。

以POSIX系統為例,通過pthread_key_create建立的key對進程內所有的線程都可見,但每個線程可以綁定不同的私人資料到一個相同的key上,這就意味著整個Chromium可以共用同一個TLS槽,建立一個應用程式層的TLS表來徹底解決系統層級TLS槽的數量限制。

具體來說,Chromium TLS系統會首先向OS申請一個key,每個線程都將為這個key綁定一張線程私人的TLS表,Chromium設定了這張表可以容納64個表項。當線程需要一個新的TLS槽時,首先會檢查這個線程是否為key已經綁定了TLS表,如果沒有,則需要建立這樣一個TLS表並將其設定為線程私人,然後從表中按順序取一個可用項,並設定新的私人資料。不難看出,新的TLS槽並不是向OS申請的,而是向Chromium TLS申請的。

ThreadLocalStorage模板類是新的ChromiumTLS系統中對TLS的抽象,StaticSlot和Slot封裝了初始化Chromium TLS系統,建立TLS表以及設定和擷取線程私人資料的操作。因此,上述ThreadLocalPointer模板類將從直接使用ThreadLocalPlatform操作TLS,應改為使用ThreadLocalStorage::Slot, 如下:

template <typename Type>class ThreadLocalPointer { public:  ThreadLocalPointer() {}  ~ThreadLocalPointer() { slot_.Free(); }  Type* Get() {    return static_cast<Type*>(slot_.Get());  }  void Set(Type* ptr) {    slot_.Set(const_cast<void*>(static_cast<const void*>(ptr)));  } private:  ThreadLocalStorage::Slot slot_;  DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);};

再次強調,ThreadLocalStorage::Slot只是向Chromium TLS系統申請可用的TLS槽,而不是向系統直接申請。

更多資訊
  • Chromium項目的bug列表裡已經報告了這個問題,參見這裡。

  • Chromium TLS實現是最近才提交到Chromium程式碼程式庫中的,參見這裡。

  • Android原始碼中對TLS槽數量的定義在BIONIC_TLS_SLOTS宏常量中,見Android 4.3和Android 4.4。

  • 關於Chromium TLS系統實現細節,感興趣的讀者可讀讀base/threading/thread_local_storage.h/cc等源檔案。

  • 關於pthread_key_create,參見Linux Man Page。


本文出自 “洪波的技術專欄” 部落格,請務必保留此出處http://hongbo.blog.51cto.com/4699661/1548489

Chromium on Android: Chromium線程局部儲存系統

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.