線程模型

標記為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 上IBar method3method1method2將始終被序列化,但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的方法setMinSchedulerPolicysched.h中定義的調度程序優先級和策略一起使用可確保對服務的所有調用至少以設置的調度策略和優先級運行。