模型執行緒

標示為 oneway 的方法不會封鎖。對於未標示為 oneway 的方法,用戶端的方法呼叫會阻斷,直到伺服器完成執行或呼叫同步回呼 (以先發生者為準)。伺服器方法實作最多可呼叫一個同步回呼;其他回呼呼叫會遭到捨棄,並記錄為錯誤。如果方法應透過回呼傳回值,但未呼叫回呼,系統會將此記錄為錯誤,並向用戶端回報為傳輸錯誤。

直通模式中的執行緒

在直通模式中,大多數呼叫都是同步的。不過,為了維持 oneway 呼叫不會封鎖用戶端的預期行為,系統會為每個程序建立一個執行緒。詳情請參閱 HIDL 總覽

繫結器化 HAL 中的執行緒

為了處理傳入的 RPC 呼叫 (包括從 HAL 傳回至 HAL 使用者的非同步回呼) 和死亡通知,每個使用 HIDL 的程序都會與執行緒集區建立關聯。如果單一程序實作多個 HIDL 介面和/或死亡通知處理程序,則其執行緒集區會在所有程序之間共用。當程序收到來自用戶端的傳入方法呼叫時,會從執行緒集區挑選空閒的執行緒,並在該執行緒上執行呼叫。如果沒有可用的空閒執行緒,則會阻斷,直到有可用的執行緒為止。

如果伺服器只有一個執行緒,則對伺服器的呼叫會依序完成。即使用戶端只有一個執行緒,擁有多個執行緒的伺服器可能會以錯誤順序完成呼叫。不過,針對特定介面物件,系統會保證 oneway 呼叫有順序 (請參閱「伺服器執行緒模型」)。對於代管多個介面的多執行緒伺服器,對不同介面的 oneway 呼叫可能會與其他阻斷呼叫同時處理。

系統會在同一個 hwbinder 執行緒上傳送多個巢狀呼叫。舉例來說,如果程序 (A) 從 hwbinder 執行緒發出同步呼叫至程序 (B),然後程序 (B) 發出同步回呼至程序 (A),則呼叫會在 (A) 中的原始 hwbinder 執行緒上執行,而該執行緒會在原始呼叫上遭到封鎖。這項最佳化功能可讓單執行緒伺服器處理巢狀呼叫,但不適用於呼叫透過另一個 IPC 呼叫序列傳送的情況。舉例來說,如果程序 (B) 發出 binder/vndbinder 呼叫,並呼叫至程序 (C),然後程序 (C) 回呼至 (A),則無法在 (A) 的原始執行緒上提供服務。

伺服器執行緒模型

除了直通模式外,HIDL 介面的伺服器實作會在與用戶端不同的程序中執行,因此需要一或多個執行緒等待傳入的方法呼叫。這些執行緒是伺服器的執行緒集區;伺服器可以決定要在執行緒集區中執行的執行緒數量,並使用大小為 1 的執行緒集區來序列化其介面上的所有呼叫。如果伺服器的執行緒集區中有一個以上的執行緒,則可在任何介面上接收並行傳入的呼叫 (在 C++ 中,這表示共用資料必須小心上鎖)。

對同一個介面的單向呼叫會序列化。如果多執行緒用戶端在介面 IFoo 上呼叫 method1method2,以及在介面 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 is called,
    // and the client resumes execution.
    ...
    return Void(); // is basically a no-op
};

用戶端執行緒模型

在用戶端上,非阻斷式呼叫 (標示為 oneway 關鍵字的函式) 和阻斷式呼叫 (未指定 oneway 關鍵字的函式) 的執行緒模型有所不同。

封鎖來電

對於封鎖呼叫,用戶端會封鎖,直到發生下列任一情況為止:

  • 發生傳輸錯誤;Return 物件包含可透過 Return::isOk() 擷取的錯誤狀態。
  • 伺服器實作項目會呼叫回呼 (如有)。
  • 伺服器實作會傳回值 (如果沒有回呼參數)。

如果成功,伺服器一律會在函式本身傳回之前,先呼叫用戶端以引數形式傳遞的回呼函式。回呼會在函式呼叫所在的執行緒上執行,因此實作者必須小心在函式呼叫期間保留鎖定 (並盡可能完全避免鎖定)。沒有 generates 陳述式或 oneway 關鍵字的函式仍會封鎖;用戶端會封鎖,直到伺服器傳回 Return<void> 物件為止。

單向通話

當函式標示為 oneway 時,用戶端會立即傳回,不會等待伺服器完成函式呼叫的叫用。從表面 (和匯總) 來看,這表示函式呼叫只會執行一半的程式碼,因此所需時間會減少一半,但在編寫效能敏感的實作項目時,這會對排程造成影響。一般來說,使用單向呼叫會導致呼叫端繼續排程,而使用一般同步呼叫會導致排程器立即從呼叫端轉移至被呼叫端程序。這是 Binder 中的效能最佳化功能。如果服務必須以高優先順序在目標程序中執行單向呼叫,則可變更接收服務的排程政策。在 C++ 中,使用 libhidltransport 的方法 setMinSchedulerPolicy 搭配 sched.h 中定義的排程器優先順序和政策,可確保服務的所有呼叫至少以設定的排程政策和優先順序執行。