成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

ChromiumonAndroid:Chromium線程局部存儲(chǔ)系統(tǒng)

線程局部存儲(chǔ)(Thread Local Storage), 簡(jiǎn)稱TLS,提供了一種存儲(chǔ)線程私有數(shù)據(jù)的方式,每個(gè)線程的私有數(shù)據(jù)對(duì)其他線程均不可見。Chromium是一個(gè)多進(jìn)程多線程架構(gòu)的瀏覽器,運(yùn)行時(shí)會(huì)創(chuàng)建多達(dá)30幾個(gè)線程,其中很多線程需要擁有自己私有數(shù)據(jù),在TLS數(shù)量有限的系統(tǒng)上,例如Android 4.3或更早的系統(tǒng),可能會(huì)因?yàn)闊o法分配足夠的TLS而導(dǎo)致Chromium崩潰。本文將介紹最近在Chromium提交代碼中是如何解決這個(gè)問題的。

成都創(chuàng)新互聯(lián)公司從2013年創(chuàng)立,先為白塔等服務(wù)建站,白塔等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為白塔企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

TLS在Chromium中的使用

Chromium代碼中,有些類提供了一個(gè)current()方法用來返回調(diào)用線程的私有數(shù)據(jù),最典型的兩個(gè)例子是MessageLoop和RenderThread。以MessageLoop為例,因?yàn)槊總€(gè)線程可能運(yùn)行著一個(gè)主消息循環(huán),如何能夠獲取與線程相關(guān)的主消息循環(huán)呢?為此,MessageLoop提供了current()方法,當(dāng)線程在創(chuàng)建MessageLoop實(shí)例時(shí),會(huì)將這個(gè)實(shí)例設(shè)置為線程的私有數(shù)據(jù),當(dāng)調(diào)用current()方法時(shí),再將這個(gè)私有數(shù)據(jù)返回給調(diào)用者。

每個(gè)TLS槽都由一個(gè)唯一的鍵值(key)標(biāo)識(shí),這個(gè)鍵值由進(jìn)程負(fù)責(zé)向OS申請(qǐng)分配,如果當(dāng)前進(jìn)程申請(qǐng)的鍵值數(shù)量超過系統(tǒng)規(guī)定的上限,申請(qǐng)將失敗,即無法獲取一個(gè)新的TLS槽。線程可以將TLS槽綁定到一個(gè)私有數(shù)據(jù)上,這個(gè)私有數(shù)據(jù)實(shí)際上是一個(gè)指針,指向一塊由調(diào)用線程動(dòng)態(tài)分配的內(nèi)存塊。

Chromium代碼中,對(duì)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是對(duì)TLS槽(鍵值)類型的抽象,internal::ThreadLocalPlatform是對(duì)特定平臺(tái)的TLS實(shí)現(xiàn)的抽象。例如,在POSIX系統(tǒng)上,ThreadLocalPlatform::AllocateSlot的實(shí)現(xiàn)為:

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

一般地,會(huì)結(jié)合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);
}

當(dāng)創(chuàng)建RenderThread實(shí)例時(shí),當(dāng)前線程會(huì)通過訪問lazy_tls設(shè)置線程的私有數(shù)據(jù),此后每次調(diào)用current()方法時(shí),都會(huì)返回線程私有的RenderThread實(shí)例。

由于LazyInstance是延遲初始化的,上述代碼中,當(dāng)首次訪問Pointer()時(shí),會(huì)創(chuàng)建一個(gè)新的ThreadLocalPointer實(shí)例,也就是向OS申請(qǐng)分配一個(gè)新的TLS槽。

潛在的問題

在三大桌面操作系統(tǒng)上(Windows/Linux/Mac),上述對(duì)ThreadLocalPointer的封裝和實(shí)現(xiàn)都能工作的很好,但自從Chromium支持Android之后,潛伏在上述實(shí)現(xiàn)中的問題就浮出水面了。

如果一個(gè)線程中需要存儲(chǔ)多個(gè)私有數(shù)據(jù),比如除了RenderThread實(shí)例,還有MessageLoop實(shí)例等等,每個(gè)都要系統(tǒng)分配給進(jìn)程的TLS鍵值,那么一旦鍵值的數(shù)據(jù)達(dá)到上限,申請(qǐng)失敗導(dǎo)致程序崩潰。這個(gè)問題已經(jīng)在Chromium WebView中暴露了,原因是Android 4.3或更老的系統(tǒng),最大可用的TLS槽數(shù)量只有64個(gè),除去Android系統(tǒng)庫(kù)(opengl,jvm)需要占用一部分之外,留給Chromium使用的數(shù)量已經(jīng)不多了,一旦發(fā)現(xiàn)分配不成功,Chromium會(huì)觸發(fā)CHECK,程序立即異常退出。Android 4.4系統(tǒng)上,最大可用的TLS槽數(shù)量多達(dá)128個(gè),這個(gè)問題得到一定的緩解,但仍沒有從根本上杜絕這個(gè)問題。

解決方法

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

以POSIX系統(tǒng)為例,通過pthread_key_create創(chuàng)建的key對(duì)進(jìn)程內(nèi)所有的線程都可見,但每個(gè)線程可以綁定不同的私有數(shù)據(jù)到一個(gè)相同的key上,這就意味著整個(gè)Chromium可以共享同一個(gè)TLS槽,創(chuàng)建一個(gè)應(yīng)用層的TLS表來徹底解決系統(tǒng)級(jí)別TLS槽的數(shù)量限制。

具體來說,Chromium TLS系統(tǒng)會(huì)首先向OS申請(qǐng)一個(gè)key,每個(gè)線程都將為這個(gè)key綁定一張線程私有的TLS表,Chromium設(shè)置了這張表可以容納64個(gè)表項(xiàng)。當(dāng)線程需要一個(gè)新的TLS槽時(shí),首先會(huì)檢查這個(gè)線程是否為key已經(jīng)綁定了TLS表,如果沒有,則需要?jiǎng)?chuàng)建這樣一個(gè)TLS表并將其設(shè)置為線程私有,然后從表中按順序取一個(gè)可用項(xiàng),并設(shè)置新的私有數(shù)據(jù)。不難看出,新的TLS槽并不是向OS申請(qǐng)的,而是向Chromium TLS申請(qǐng)的。

ThreadLocalStorage模板類是新的ChromiumTLS系統(tǒng)中對(duì)TLS的抽象,StaticSlot和Slot封裝了初始化Chromium TLS系統(tǒng),創(chuàng)建TLS表以及設(shè)置和獲取線程私有數(shù)據(jù)的操作。因此,上述ThreadLocalPointer模板類將從直接使用ThreadLocalPlatform操作TLS,應(yīng)改為使用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>);
};

再次強(qiáng)調(diào),ThreadLocalStorage::Slot只是向Chromium TLS系統(tǒng)申請(qǐng)可用的TLS槽,而不是向系統(tǒng)直接申請(qǐng)。

更多信息

  • Chromium項(xiàng)目的bug列表里已經(jīng)報(bào)告了這個(gè)問題,參見這里。

  • Chromium TLS實(shí)現(xiàn)是最近才提交到Chromium代碼庫(kù)中的,參見這里。

  • Android源代碼中對(duì)TLS槽數(shù)量的定義在BIONIC_TLS_SLOTS宏常量中,見Android 4.3和Android 4.4。

  • 關(guān)于Chromium TLS系統(tǒng)實(shí)現(xiàn)細(xì)節(jié),感興趣的讀者可讀讀base/threading/thread_local_storage.h/cc等源文件。

  • 關(guān)于pthread_key_create,參見Linux Man Page。


標(biāo)題名稱:ChromiumonAndroid:Chromium線程局部存儲(chǔ)系統(tǒng)
分享路徑:http://jinyejixie.com/article48/poedhp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、品牌網(wǎng)站制作網(wǎng)站導(dǎo)航、軟件開發(fā)、、網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
泰宁县| 同仁县| 丹阳市| 乐至县| 咸阳市| 巴林右旗| 通江县| 弥勒县| 乌鲁木齐县| 镇江市| 香河县| 剑川县| 台山市| 南城县| 绩溪县| 抚松县| 宁化县| 靖边县| 凭祥市| 明星| 涞源县| 日喀则市| 乐安县| 武鸣县| 仪征市| 四会市| 石屏县| 邮箱| 绵阳市| 大荔县| 大化| 蒙城县| 大冶市| 都江堰市| 望奎县| 社旗县| 清远市| 定边县| 延安市| 凉城县| 时尚|