RFC-0023:通訊協定的組合模型

RFC-0023:通訊協定的組合模型
狀態已接受
區域
  • FIDL
說明

關鍵字介面已由關鍵字通訊協定取代。擴充通訊協定:明確指出組合模型,其中一個通訊協定可定義為一組訊息,並由一或多個其他通訊協定擴增。通訊協定擴充功能使用的語法已從類似於繼承的語法,變更為類似於 mixin 的語法。

作者
提交日期 (年-月-日)2018-12-10
審查日期 (年-月-日)2019-01-09

摘要

我們建議進行下列變更:

  • 關鍵字 interface 會由關鍵字 protocol 取代。 (本文其餘部分將使用「通訊協定」一詞)。
  • 擴充通訊協定會明確指出組合模型,其中一個通訊協定可定義為一組訊息,並由一或多個其他通訊協定擴增。
  • 通訊協定擴充功能的語法已從類似於「繼承」的語法,變更為類似於「混入」的語法。
  • 在目標語言中表示組合式通訊協定時,繫結作者必須避免涵蓋 (例如「is-a」階層、繼承、子型別)。

提振精神

「介面」一詞的背景脈絡包括方法多載、建構函式和解構函式、做為訊息接收者的物件模型等。

不過,FIDL 的目標較為簡單,旨在說明兩個對等互連裝置之間的通訊協定,也就是一組可交換的訊息。

我們一開始就明確指出 FIDL API 的設計考量,例如「雖然語法類似於物件導向介面的定義,但設計考量更接近網路通訊協定,而非物件系統」。當面臨導入更多「物件導向」功能的選項時,我們避開了這類功能 (例如最近在有關 RFC-0020:序數雜湊的評論中)。

我們希望語言中的差異更明確,建議您將關鍵字 interface 替換為關鍵字 protocol,藉此變更語法。

此外,借用繼承語法所隱含的「is-a」關係並不健全,會導致錯誤的期望。(為求明確,FIDL 不提供這類繼承語意,但語法暗示了這一點)。詳情請參閱「『Is A』關係是否視為有害」一節。

設計

這項提案引進正式語意,用於說明程序互動和通訊協定。

這項提案會變更 FIDL 來源語言,釐清通訊協定擴充功能的語意,並為繫結作者提供新指引。

目前 JSON IR 中不會顯示繼承關係,因此繫結作者無法運用這項資訊。因此,除了改善文件外,我們預期這項新指引對產生的繫結程式碼影響不大。

這項提案不會變更線路格式。

這項提案不會變更 JSON IR,但我們預計日後會進行大規模變更,屆時會將金鑰重新命名。

通訊協定模型

Zircon 管道不需要特定結構定義的酬載。 FIDL 以這個基本項目為基礎,並限制管道傳輸特定通訊協定。這樣一來,FIDL 就能為管道兩端賦予意義和名稱。我們將其中一個稱為「用戶端」,另一個稱為「伺服器」

我們的模型將「通訊協定」描述為「一組導向互動」,並可選擇性地加入「墓誌銘」。我們將用戶端與伺服器之間使用通訊協定的特定通訊執行個體稱為「工作階段」

方向可以是從用戶端到伺服器,也可以是從伺服器到用戶端

互動會從要求開始,視需要回應。 對於沒有回應的互動,我們通常會使用「fire and forget」或「one way」一詞,而對於預期會收到回應的要求,則會使用「call」一詞。

要求和回應都是訊息,會以標頭表示,後面接著結構體的酬載,也就是要求或回應的引數

目前,我們限制伺服器到用戶端訊息不得有回覆。 簡單來說,「事件只會啟動即棄用」。

墓誌銘是伺服器與用戶端之間的互動,用於結束工作階段。詳情請參閱 RFC-0053:墓誌銘

這個模型中沒有更複雜的互動,例如 TCP 的三向交握 (SYN/SYN-ACK/ACK)。我們認為這超出範圍,且模型日後不太可能涵蓋這類內容。

組合模型

如今,通訊協定既可定義互動,也能擴充一或多個通訊協定。產生的通訊協定 (「組合通訊協定」) 會直接攜帶定義的所有互動,並繼承其前身 (直接或間接) 定義的所有互動。

舉例來說,Child 通訊協定定義如下:

protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child { compose Parent1; compose Parent 2; Method3(); };

會包含所有三種互動:Method1Method2Method3

不過,Method1Method2 是在 Child 中定義為組合結果,還是直接定義,不會傳送至語言專屬後端,也就是說,這不會以 JSON IR 表示。

「Is A」關係有害

由於通訊協定可以雙向傳送要求,因此建立子型別關係時需要更加謹慎。因此,我們不允許通訊協定與其擴充的通訊協定具有「是」關係。

舉例來說,假設我們有以下兩個通訊協定:

protocol Parent { Method(); };
protocol Child { ->Event(...); };

如果我們允許將攜帶通訊協定 Child 的管道視為 Parent (即「ChildParent」關係),用戶端就會收到 Event,但無法處理。如需具體範例,請參閱下一節

我們將支援「僅限用戶端與伺服器互動」等特定通訊協定註解,以支援及允許「是」關係。發生這種情況時,這類關係會傳送至 JSON IR,供後端使用。

目前依賴「Is A」關係

以具體範例來說,fuchsia.media 程式庫會將各種通訊協定組合在一起。 請特別注意以下幾點:

AudioCapturerAudioRenderer 都不會定義事件,也就是說,這些純粹是「用戶端對伺服器通訊協定」,屬於單向通訊。(StreamSource 定義了兩個事件,但我們在此特別討論各個通訊協定本身的定義)。

因此,如果用戶端知道如何與 StreamBufferSetStreamSource (分別為 StreamBufferSetStreamSink) 互動,也可以與 AudioCapturer (和 AudioRenderer) 互動,也就是說,用戶端只會忽略公開的額外方法。在這裡,我們可以如預期定義「是」關係。

不過,如果要在任一介面中新增事件,這種「是」的」關係就會消失。假設用戶端正在與 StreamBufferSet 互動,但實際上是與伺服器端的 AudioRenderer 互動。如果 AudioRenderer 觸發事件,會發生什麼情況? 該客戶會如何處理?

由於我們 (目前) 無法在 fidlc 中提供這項區別,因此我們確認不支援「是」關係。這項提案基本上是釐清現狀。

如同 fuchsia.media 案例,如果作者知道某些關係為真,可以根據需求調整繫結 (使用轉型等)。

在後續提案中,我們預計會導入屬性或新關鍵字,以擷取這個方向性限制,並根據此限制在繫結中提供「是」關係。在此提案之前,我們無法提供更好的支援,因為這是 FIDL 工具鍊的一部分。

語法變更

在設計階段,我們提出了幾種不同的替代方案,詳情請參閱下方的「缺點、替代方案和未知事項」

使用可接受語法的擴充通訊協定如下所示:

protocol Parent1 {
  Method1OfParent1();
  Method2OfParent1();
};

protocol Parent2 {
  Method1OfParent2();
  Method2OfParent2();
};

protocol Child {
  compose Parent1;
  compose Parent2;
  Method1OfChild();
  Method2OfChild();
};

正式來說,文法變更如下:

declaration = const-declaration | enum-declaration | protocol-declaration |
              struct-declaration | union-declaration | table-declaration ;

protocol-declaration = ( attribute-list ) , "protocol" , IDENTIFIER ,
                       "{" , ( method-or-compose-declaration , ";" )*  , "}";

method-or-compose = method-declaration | compose-declaration ;

method-declaration = ( ordinal , ":" ) , method-parameters ;

method-parameters = IDENTIFIER , parameter-list , ( "->" , parameter-list )
                     | "->" , IDENTIFIER , parameter-list ;

compose-declaration = "compose", compound-identifier ;

複合通訊協定只能提及一次。

可能延期

我們預期後續提案會額外允許伺服器與用戶端互動,因此可讓管道上的多工通訊協定 (可能以相反順序) 要求回應。舉例來說,coordinator.fidl 定義了兩個指令回應通訊協定,一個是從 devmgr -> devhost,另一個是從 devhost -> devmgr。目前這些項目是手動多工處理,並依賴序數調度來分類。

我們可能會在撰寫區塊中使用「->」語法,以便稍後以反向方式導入多工處理。 另一種做法是,只有在擴充功能包含反向通訊協定時,才需要明確指示,這樣做的好處是不會導入任何方向語法,因為我們正在延後推出包含反向通訊協定的擴充功能。

我們允許將組合區塊放在通訊協定的定義中,也允許使用多個組合區塊。我們也可以只使用一個區塊,並要求這個區塊位於頂端。 我們選擇開放,並依賴自動格式和/或樣式指南來提供建議,而不是將強制執行功能內建於語言本身。

JSON IR

我們不會在這次異動中變更 JSON IR。

我們不會移除「interface_declarations」鍵,而是會將其重新命名為「protocol_declarations」,這是大規模變更的一部分。這項重大變更需要分階段進行,將結構定義版本從 0.0.1 升級至 0.0.2,並為後端提供適應期。

遠距中斷和 [FragileBase] 的使用

這項提案不會改變遠距中斷的可能性,因此我們重申,任何擴充通訊協定都應使用 [FragileBase]1

繫結作者指南

  • 在目標語言中表示組合式通訊協定時,繫結必須避免從屬關係 (例如「is-a」階層、繼承、子型別)。
  • 應該會收到不明序數的錯誤。 繫結應將此錯誤泡泡化為「不明序數錯誤」,並關閉管道。

導入策略

三個步驟:

  1. 新增對新語法的支援;
  2. 將所有 FIDL 檔案轉換為使用新語法;
  3. 停止支援舊語法。

人體工學

這項變更可讓 FIDL 更容易理解,請參閱動機一節。 這項變更可能無法讓您立即瞭解 FIDL,但可避免日後產生誤解和期望落差。

說明文件和範例

我們需要更新語言、文法、評分標準和其他相關文件。

回溯相容性

這項變更會破壞目前使用繼承的 FIDL 檔案的來源相容性。如實作所述,我們會分階段導入新語法、遷移所有 FIDL 檔案,然後移除對舊語法的支援。

這項變更不會變更 FIDL 連線格式,因此是回溯相容的 ABI 變更。

效能

不會影響效能。

安全性

我們可能會利用更嚴格的型別語意來保護或觀察管道。 這並非本提案的目標,也不會使現狀倒退,甚至可以說是有所改善。

測試

這項變更的測試完全可以在 fidlc 層級進行單元測試。

缺點、替代方案和未知事項

以下各節記錄設計階段提出的替代語法。

替代語法 (pascallouis@)

範例

protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child {
  compose {
    -> Parent1();
    -> Parent2();
  };
  Method1OfChild();
}

附註:這是原始建議的語法。 compose 區塊似乎不自然,而且與現有語言的差異過大。 因此,相較於組合單一通訊協定,組合多個通訊協定成為較好的做法,因為前者會顯得冗長。此外,我們也不清楚是否允許多個 compose 區塊,以及會是什麼樣子。最後,我們選擇不採用偏好在通訊協定上顯示方向的「->」指標,而是日後再推出這項功能,並搭配多向多工處理 (如果考慮推出這類功能的話)。

替代語法 (jeremymanson@)

原因:釐清我們預期實作的方法清單,以及定義通訊協定的方法清單之間的差異:

範例

protocol Parent1 {
  Method1OfParent1();
  Method2OfParent1();
};

protocol Parent2 {
  Method1OfParent2();
  Method2OfParent2();
};

interface Child {
  compose {
    -> Parent1();
    -> Parent2();
  };
  Method1OfChild();
};

附註:「介面」關鍵字表示每個方法都必須有實作項目,「通訊協定」關鍵字則表示要納入通訊協定和介面時的規定。舉例來說,我們不一定會預期 StreamSource 會有自己的實作項目。這項做法明確指出不會發生實作繼承,因此我們離實作繼承更遠。您無法將介面組合到另一個介面中。

替代語法:類似 Go 的介面組合 (proppy@)

原因:不像是繼承,熟悉介面嵌入的 Golang 語法

範例

protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child {
    Parent1;
    Parent2;
    Method3();
};

附註:Go 語言規格介面和嵌入。

替代語法:使用宣告 (jeffbrown@)

原因:看起來不像繼承,而是重複使用現有關鍵字來指出帶入範圍的名稱。較不容易與方法宣告或「property2」混淆。

範例

protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child {
    using Parent1;
    using Parent2;
    Method3();
};

附註:FIDL、C++、Rust 和其他語言的先例。

替代關鍵字

compose」關鍵字的替代方案:

  • extends (pascallouis@)
  • contains (smklein@)

既有技術和參考資料

沒有特別之處。

Cap'n Proto 具有支援多重繼承 (採用 Mixin 樣式) 的介面。


  1. 導入方法適用的序數雜湊,並在日後的提案中將方法序數從 32 位元改為 64 位元,可望在實務上避免遠距中斷,屆時我們也會重新評估是否使用 FragileBase。 

  2. 屬性:假設的 FIDL 擴充功能,可簡化觀察 / 資料繫結作業。簡單來說,繫結會產生方法,用於存取、修改及/或觀察介面公開的值。