目標和動力
FIDL 通訊協定和通訊協定要求由 Zircon 支援 管理的所有頻道以下列 FIDL 定義為例:
library foo;
protocol Calculator {};
resource struct Record {
// Client endpoint of a channel speaking the Calculator protocol
Calculator c;
// Server endpoint of a channel speaking the Calculator protocol
request<Calculator> s;
};
我們使用 LLCPP 來產生包含兩個 Zircon 管道的結構:
struct Record {
zx::channel c;
zx::channel s;
};
任何 FIDL 通訊協定都只是一個管道,導致大門意外 通訊協定類型或方向 (以下是 識別 及 固定)。目的地: 提升類型安全和自我說明文件,修改產生的程式碼 改為執行以下動作:
struct Record {
// Now it's clear that |c| is a client channel endpoint speaking the |Calculator| protocol.
fidl::ClientEnd<foo::Calculator> c;
// Similarly, |s| is a server channel endpoint for that protocol.
fidl::ServerEnd<foo::Calculator> s;
};
同樣地,先前處理的 LLCPP 執行階段中的所有函式
已更新 zx::channel
,現在可說更準確的類型,編碼
通訊協定的方向和種類 (例如
fidl::BindServer
)。
不過,大多數使用者程式碼仍會使用 zx::channel
。並繼續
編譯功能,因為我們暫時支援
fidl::ClientEnd
/ fidl::ServerEnd
,費用為安全。讓
變更在程式碼集中的好處,使用者程式碼應該
透過公開介面取得 fidl::ClientEnd
/ fidl::ServerEnd
類型,如
而不是從原始管道投放內容
技術背景
如何提供協助
選取工作
搜尋含有 TODO(https://fxbug.dev/42148734)
字串的 BUILD.gn 檔案。這項服務
看起來會像這樣:
# TODO(https://fxbug.dev/42148734): This target uses raw zx::channel with LLCPP which is deprecated.
# Please migrate to typed channel APIs (fidl::ClientEnd<T>, fidl::ServerEnd<T>).
# See linked bug for details.
configs += [ "//build/cpp:fidl-llcpp-deprecated-raw-channels" ]
移除這幾行和 fx build
。如果建構成功,但未出現任何警告或
請跳到最後一個步驟。否則,警告和錯誤
指出已淘汰的用法。接著,以下列舉三種典型的情境:
情境 1:實作伺服器
遷移伺服器相當簡單明瞭,找出伺服器的位置
實作繼承自名為
RawChannelInterface
。該類別是一種填充
轉譯伺服器方法時採用 fidl::ClientEnd<P>
/ fidl::ServerEnd<P>
將引數轉換為採用 zx::channel
的引數變更為平常的 Interface
並更新方法引數以符合
FIDL
protocol Foo {
TakeBar(Bar bar);
HandleBar(request<Bar> bar);
};
使用前
class MyServer : public fidl::WireRawChannelInterface<Foo> {
void TakeBar(zx::channel bar, TakeBarCompleter::Sync& completer) override;
void HandleBar(zx::channel bar, HandleBarCompleter::Sync& completer) override;
};
使用後
class MyServer : public Foo::Interface {
void TakeBar(fidl::ClientEnd<Bar> bar, TakeBarCompleter::Sync& completer) override;
void HandleBar(fidl::ServerEnd<Bar> bar, HandleBarCompleter::Sync& completer) override;
};
情境 2:通訊協定要求管道
常見的做法是建立一組管道端點,並將伺服器端傳送至
通訊協定實作項目我們可以避免建立原始的 Zircon 頻道
fidl::CreateEndpoints<Protocol>
方法:
使用前
zx::channel client_end, server_end;
zx_status_t status = zx::channel::create(0, &client_end, &server_end);
if (status != ZX_OK)
return status;
foo.HandleBar(std::move(server_end));
fidl::WireClient<Bar> bar(std::move(client_end), &dispatcher);
使用後
auto bar_ends = fidl::CreateEndpoints<Bar>();
if (!bar_ends.is_ok())
return bar_ends.status_value();
foo.HandleBar(std::move(bar_ends->server));
fidl::WireClient bar(std::move(bar_ends->client), &dispatcher);
// Alternatively, |CreateEndpoints| supports returning the client-end by address,
// which would be useful when the client-end is an instance variable, for example
// in a test fixture.
fidl::ClientEnd<Foo> bar_client_end;
auto bar_server_end = fidl::CreateEndpoints(&bar_client_end);
if (!bar_server_end.is_ok())
return bar_server_end.status_value();
foo.HandleBar(std::move(*bar_server_end));
請注意,您可以省略 fidl::WireClient
的通訊協定範本參數
都使用輸入的管道時,產生更簡潔的程式碼。
同步處理用戶端
您可以使用 fidl::WireSyncClient
將 fidl::ClientEnd
轉換為
做為通訊協定的對應同步用戶端。這種做法的好處是
因此不必寫出通訊協定類型兩次 (適用於 ClientEnd
和
而在同步用戶端類別中)。
fidl::WireSyncClient bar{std::move(bar_ends->client)};
情境 3:連線至通訊協定
fdio_service_connect
常用來連線至
FIDL 服務。由於其簽章是 C,因此會變為
具體使用方式,特別是在具有類型的頻道時。我們
已建立人體工學包裝函式:component::Connect<Protocol>
、
component::ConnectAt<Protocol>
和
component::OpenServiceRoot
。它們位於
sdk/lib/sys/元件/cpp 程式庫。
連線至個別通訊協定
使用前
zx::channel client_end, server_end;
zx_status_t status = zx::channel::create(0, &client_end, &server_end);
if (status != ZX_OK)
return status;
status = fdio_service_connect("/svc/fuchsia.Foo", server_end.release());
if (status != ZX_OK)
return status;
fidl::WireClient<Foo> foo(std::move(client_end), &dispatcher);
使用後
// The channel creation and service connection is done in one function.
// By default it opens the protocol name.
// Returns |zx::result<fidl::ClientEnd<Foo>>|.
auto client_end = component::Connect<Foo>();
if (!client_end.is_ok())
return client_end.status_value();
// Note: can omit template argument
fidl::WireClient foo(std::move(*client_end), &dispatcher);
正在開啟 Service Directory
使用前
zx::channel client_end, server_end;
zx_status_t status = zx::channel::create(0, &client_end, &server_end);
if (status != ZX_OK)
return status;
status = fdio_service_connect("/svc", server_end.release());
if (status != ZX_OK)
return status;
fidl::WireClient<::fuchsia_io::Directory> dir(std::move(client_end));
使用後
// The channel creation and service connection is done in one function.
// Opens "/svc" and returns the client endpoint, as a
// |zx::result<fidl::ClientEnd<::fuchsia_io::Directory>>|.
auto client_end = component::OpenServiceRoot<Foo>();
if (!client_end.is_ok())
return client_end.status_value();
// Note: can omit template argument
fidl::WireClient dir(std::move(*client_end), &dispatcher);
注意事項:傳播通訊協定類型
盡可能在可能的情況下,將通訊協定類型套用到相關
函式與變數當您發現自己建立 ClientEnd
/
頻道有 ServerEnd
筆 (共 UnownedClientEnd
個),思考來源管道是否
也可以變更為型別管道。做為自我檢查
可能無法揭露對通訊協定種類的不正確假設
也都是從某個管道流向與 LLCPP 產生的結構不同,使用
公開 API 的類型頻道並不理想地預先丟棄介面
特定擁有權模式或一組類型
因為這種類型的頻道
只是 Zircon 管道的輕量包裝函式。這裡舉例說明
遷移 zx::unowned_channel
:
使用前
// |client| should speak the |fuchsia.foobar/Baz| protocol.
zx_status_t DoThing(zx::unowned_channel client, int64_t args) {
return fidl::WireCall<fuchsia_foobar::Baz>(std::move(client))->Method(args).status();
}
使用後
// The intended protocol is encoded in the type system. No need for comment.
zx_status_t DoThing(fidl::UnownedClientEnd<fuchsia_foobar::Baz> client, int64_t args) {
return fidl::WireCall(client)->Method(args).status();
}
注意事項:因通訊協定組合而解決類型不符問題
其中沒有「is-a」的指令(繼承、子假設) 關係
FIDL 通訊協定。這表示當通訊協定為 More
時
包含通訊協定 Less
,一個可能想要使用 fidl::ClientEnd<More>
呼叫 void
foo(fidl::ClientEnd<Less>)
函式,但我們不會
而這些類型之間的轉換就會直接促成轉換。
確定使用安全無虞後,可以手動
用戶端執行個體
fidl::ClientEnd<Less>(more_client_end.TakeChannel())
。偏好在
才是安全的理由 (例如,More
不會在
Less
之上)。
最後步驟:建立 CL
上傳變更後,請務必仔細檢查下列三個位置:
"//build/cpp:fidl-llcpp-deprecated-raw-channels"
設定已從 您的目標專屬BUILD.gn
檔案。- 在
//build/cpp/BUILD.gn
中,刪除顯示設定中的行 與 GN 目標相對應的部分 迴歸原始管道也能輕鬆透過圖表來呈現遷移作業 進度。 - 如果您確定要遷移的目標是
特定 FIDL 通訊協定的
RawChannelInterface
,您就可以刪除fidlgen_cpp
編譯器的通訊協定。別擔心 否則程式碼不會編譯。
接著,你就可以上傳 CL 並加上 Bug: 69585
🎉? 的代碼 🎉?
如果需要 FIDL 團隊進行特定審查,你可以新增 ianloic@ 和 yifeit@ 其中之一。
CL 範例
遷移期間發現的已知問題點:
- 轉換
fdio_get_service_handle
時,函式會使用zx_handle_t
,不含任何通訊協定類型。我們希望fidl::ClientEnd<T>
。 - 轉換
fdio_open(path, flags, server.release())
時,沒有任何結果fdio_open
的類型安全替代項目。 - 轉換 HLCPP 和 LLCPP 端點類型並不容易。我們希望
「
fidl::ClientEnd<::my_thing::Protocol>
」和fidl::InterfaceHandle<my::thing::Protocol>
,即可輕鬆轉換成 另一個對伺服器而言也是如此 - HLCPP 和舊版元件架構 API (
sys::ServiceDirectory
、sys::OutgoingDirectory
) 使用 HLCPPInterfaceHandle
和InterfaceRequest
因此需要額外轉換為 LLCPP 類型的管道。
贊助者
如有問題或想掌握最新狀態,請與我們聯絡:
- yifeit@google.com
- ianloic@google.com