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) 互動,也就是說,用戶端只會忽略公開的額外方法。在這裡,我們可以如預期定義「is a」關係。

不過,如果要在任一介面中新增事件,這種「是」的」關係就會消失。假設用戶端與 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();
};

附註:「interface」關鍵字表示每個方法都必須有實作項目,「protocol」關鍵字則表示符合通訊協定和介面的需求,並納入其中。舉例來說,我們不一定會預期 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 擴充功能,可簡化觀察 / 資料繫結。簡單來說,繫結會產生方法,用於存取、修改及/或觀察介面公開的值。