線程模型

標記為oneway方法不會阻塞。對於未標記為oneway的方法,客戶端的方法呼叫將阻塞,直到伺服器完成執行或呼叫同步回呼(以先到者為準)。伺服器方法實作最多可以呼叫一個同步回調;額外的回調呼叫將被丟棄並記錄為錯誤。如果一個方法應該透過回調傳回值並且沒有呼叫它的回調,那麼這將被記錄為錯誤並向客戶端報告為傳輸錯誤。

直通模式下的線程

在直通模式下,大多數呼叫都是同步的。但是,為了保留oneway呼叫不會阻塞客戶端的預期行為,將為每個程序建立一個執行緒。有關詳細信息,請參閱HIDL 概述

綁定 HAL 中的線程

為了服務傳入的 RPC 呼叫(包括從 HAL 到 HAL 使用者的非同步回呼)和死亡通知,執行緒池與使用 HIDL 的每個進程相關聯。如果單一程序實作多個 HIDL 介面和/或死亡通知處理程序,則其執行緒池在所有這些介面之間共用。當進程收到來自客戶端的傳入方法呼叫時,它會從執行緒池中選擇一個空閒執行緒並在該執行緒上執行呼叫。如果沒有可用的空閒線程,它將阻塞,直到有可用的線程為止。

如果伺服器只有一個線程,則對伺服器的呼叫將按順序完成。即使客戶端只有一個線程,具有多個線程的伺服器也可能會無序地完成呼叫。但是,對於給定的介面對象,保證oneway呼叫是有序的(請參閱伺服器執行緒模型)。對於託管多個介面的多執行緒伺服器,對不同介面的oneway呼叫可以彼此並發處理或與其他阻塞呼叫同時處理。

多個嵌套呼叫將在同一個 hwbinder 線程上發送。例如,如果進程(A)從 hwbinder 線程向進程(B)進行同步調用,然後進程(B)向進程(A)進行同步調用,則該調用將在原始 hwbinder 線程上執行(A) 中的原始調用被阻止。這種最佳化使得單執行緒伺服器能夠處理巢狀調用,但它不能擴展到調用通過另一個 IPC 調用序列的情況。例如,如果進程(B)進行了binder/vndbinder調用,該調用調用了進程(C),然後進程(C)回調了(A),則它無法在(A)中的原始線程上提供服務。

伺服器線程模型

除直通模式外,HIDL 介面的伺服器實作與客戶端位於不同的進程中,並且需要一個或多個執行緒等待傳入的方法呼叫。這些線程是伺服器的線程池;伺服器可以決定在其線程池中運行多少個線程,並且可以使用大小為 1 的線程池來序列化其介面上的所有呼叫。如果伺服器的線程池中有多個線程,則它可以在其任何介面上接收並發傳入呼叫(在 C++ 中,這意味著必須仔細鎖定共享資料)。

對同一介面的單向呼叫是串行的。如果多執行緒用戶端呼叫介面IFoo上的method1method2 ,以及介面IBar上的method3 ,則method1method2將始終被序列化,但method3可能與method1method2並行執行。

單一客戶端執行緒可以透過兩種方式在具有多個執行緒的伺服器上引起並發執行:

  • oneway呼叫不會阻塞。如果執行oneway調用然後調用非oneway調用,則伺服器可以同時執行oneway調用和非oneway調用。
  • 透過同步回呼傳回資料的伺服器方法可以在從伺服器呼叫回呼時立即解鎖客戶端。

對於第二種方式,在呼叫回調之後執行的伺服器函數中的任何程式碼都可以並發執行,伺服器處理來自客戶端的後續呼叫。這包括伺服器函數中的程式碼以及在函數末尾執行的自動析構函數。如果伺服器的線程池中有多個線程,即使呼叫僅來自一個客戶端線程,也會出現並發問題。 (如果進程提供的任何 HAL 需要多個線程,則所有 HAL 都會具有多個線程,因為線程池是每個進程共享的。)

一旦伺服器呼叫提供的回調,傳輸就可以呼叫客戶端上實現的回調並解鎖客戶端。客戶端在呼叫回調(可能包括運行析構函數)後與伺服器實作執行的任何操作並行進行。回調後的伺服器函數中的程式碼不再阻塞客戶端(只要伺服器執行緒池有足夠的執行緒來處理傳入的呼叫),而是可能與客戶端將來的呼叫並發執行(除非伺服器執行緒池只有一個執行緒) )。

除了同步回調之外,來自單執行緒用戶端的oneway呼叫可以由其執行緒池中具有多個執行緒的伺服器同時處理,但前提是這些oneway呼叫在不同的介面上執行。同一介面上的oneway呼叫總是序列化的。

注意:我們強烈鼓勵伺服器函數在呼叫回調函數後立即返回。

例如(在 C++ 中):

Return<void> someMethod(someMethod_cb _cb) {
    // Do some processing, then call callback with return data
    hidl_vec<uint32_t> vec = ...
    _cb(vec);
    // At this point, the client's callback will be called,
    // and the client will resume execution.
    ...
    return Void(); // is basically a no-op
};

客戶端線程模型

用戶端上的執行緒模型在非阻塞呼叫(使用oneway關鍵字標記的函數)和阻塞呼叫(未指定oneway關鍵字的函數)之間有所不同。

阻止來電

對於阻塞調用,客戶端會阻塞,直到發生以下情況之一:

  • 發生傳輸錯誤; Return物件包含可以使用Return::isOk()來擷取的錯誤狀態。
  • 伺服器實作呼叫回調(如果有)。
  • 伺服器實作傳回一個值(如果沒有回呼參數)。

如果成功,客戶端作為參數傳遞的回調函數始終在函數本身返回之前由伺服器呼叫。回調在函數呼叫所在的相同執行緒上執行,因此實作者必須小心在函數呼叫期間持有鎖(並在可能的情況下完全避免它們)。沒有generates語句或oneway關鍵字的函數仍然是阻塞的;客戶端會阻塞,直到伺服器回傳Return<void>物件。

單向呼叫

當函數被標記為oneway時,客戶端立即傳回,而不等待伺服器完成其函數呼叫。從表面上看(總體而言),這意味著函數呼叫需要一半的時間,因為它執行了一半的程式碼,但是當編寫對效能敏感的實作時,這會產生一些調度影響。通常,使用單向呼叫會導致呼叫者繼續被調度,而使用普通同步呼叫會導致調度程序立即從呼叫者程序轉移到被呼叫者程序。這是binder中的一個效能優化。對於需要在高優先權的目標程序中執行單向呼叫的服務,可以改變接收服務的調度策略。在 C++ 中,使用libhidltransport的方法setMinSchedulerPolicy以及sched.h中定義的調度程序優先權和策略,可確保對服務的所有呼叫至少以設定的調度策略和優先權運行。