RFC-0265:計數器:用於跨程序同步的簡易 Zircon 物件 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 提出新的 Zircon 物件 zx::counter。 |
問題 | |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2024-12-09 |
審查日期 (年-月-日) | 2025-02-12 |
問題陳述
這項 RFC 的動機來自兩種不同的用途,即 Starnix 暫停和 Timestamped Graphics Fence。雖然 Starnix 暫停功能是主要用途,但時間戳記圖形柵欄也是重要的次要用途。
Starnix Suspension
我們需要一種方法,讓 Starnix Kernel 和 Starnix Runner 在有 Fuchsia 平台元件待處理訊息時,能協調並避免暫停。
這項作業涉及三個程序:
某些 Fuchsia 平台元件,會在管道上傳送訊息。
Starnix Kernel,需要讀取並依據該訊息採取行動。
Starnix Runner,其角色如下所述。
Starnix Runner 負責將所有執行緒置於 ZX_THREAD_SUSPENDED
狀態,並觀察 Fuchsia 平台元件傳送至 Starnix Kernel 的訊息,以便暫停 Starnix Kernel。Starnix Runner 會充當 Proxy,代表 Starnix Kernel 接收訊息,然後在 Starnix Kernel 未暫停時轉送訊息,或在 Starnix Kernel 暫停時先復原再轉送訊息。
我們希望確保不會有任何「遺漏喚醒」情形,因此 Starnix Runner 和 Starnix Kernel 必須同意是否有任何未完成或「傳送中」的訊息,以免 Starnix Kernel 遭到暫停。
目前,Starnix Runner 和 Starnix Kernel 會使用單一 zx::event
進行協調。這個事件用於指出有訊息正在傳送中。以這種方式使用事件可發揮作用,並防止喚醒失敗。不過,這項功能有限制,每次只能轉送一則訊息。沒有這項限制的話,Starnix Runner 就無法判斷 Starnix Kernel 是否已處理所有飛行中訊息,還是只處理已收到的訊息。
我們希望移除這項限制,並支援多個執行中的訊息。為了支援多個傳送中的訊息,我們需要某種方式來計算這些訊息,以便在計數器達到零時才暫停。
當有多個傳送中的訊息時,單獨使用 zx::event
是不夠的。不過,只要使用 zx::event
加上共用的整數即可。如果 Starnix Runner 和 Starnix Kernel 共用位址空間,我們可以直接將整數儲存在記憶體中。不過,這些程序不會共用位址空間,因此我們需要將整數儲存在其他共用資源 (例如 VMO) 中。由於這些程序位於相同的邏輯信任網域中,因此使用 VMO 進行協調完全可行。不過,如果我們有一種新的 Zircon 物件,可做為具有計數的事件,那麼我們就能做得更好,並簡化一些程序。
帶有時間戳記的圖形柵欄
ZirconVmoSemaphore 用於實作帶有時間戳記的圖形柵欄。它的作用類似於 zx::event
加上時間戳記,用來指出物件收到信號的時間。圖形堆疊會為每個影格建立多個這類物件。每個 WebView 都由 VMO 支援,因此每個 WebView 都會佔用一個記憶體頁面 (加上一些額外負擔)。使用 VMO 實作帶有時間戳記的圖形柵欄雖然可行,但效率不彰。如果我們有一個物件可做為 zx::event
加上時間戳記,那麼我們可以做得更好。
另請參閱 VkFence。
摘要
我們建議您建立新的 Zircon 物件 zx::counter
,該物件可容納整數、可遞增/遞減,並在值大於零或小於或等於零時宣告信號。
利害關係人
主要的利害關係人包括 Zircon、Starnix 和 Graphics。
協助人員:davemoore@google.com
審查者:emircan@google.com、lindkvist@google.com、mcgrathr@google.com
諮詢對象:adanis@google.com、eieio@google.com、rudymathu@google.com
社會化:在 RFC 第一版草案之前,我們已透過電子郵件與多位人員討論此提案的不同版本。另請參閱 go/zx-counter 中的內部討論。
目標和需求
解決手邊問題 - 啟用 Starnix Kernel 和 Starnix Runner,以便在 Fuchsia 平台元件有待處理訊息時協調並避免暫停。啟用圖形堆疊,以更有效率的同步工具取代以 VMO 為基礎的 ZirconVmoSemaphore。
保持簡單 - 解決問題時,請盡量簡單。請勿建構不必要的功能。如果有更多用途出現,我們可以針對這些未來用途改良解決方案 (或建立其他解決方案)。
設計
我們推出了新的 Zircon 物件 Counter。計數器就像事件,但使用帶正負號的 64 位元整數,可遞增、遞減、讀取或寫入。
當計數器的值大於零時,系統會宣告信號 ZX_COUNTER_POSITIVE
,並取消宣告 ZX_COUNTER_NON_POSITIVE
。如果值小於或等於零,ZX_COUNTER_POSITIVE
會解除斷言,並斷言 ZX_COUNTER_NON_POSITIVE
。
雖然 Starnix 暫停用途可透過增加和減少操作來滿足信號斷言/取消斷言,但經過時間戳記的圖形柵欄用途則需要讀取和寫入作業。用作邊界值的計數器會從零開始,代表未發出信號的邊界。正值代表已發出信號的邊界,而值本身則是邊界發出信號的時間。
為了發出柵欄訊號,圖形堆疊中的訊號元件會使用寫入作業,將計數器的值設為目前時間 (想想 zx_instant_mono_t
)。當計數器的值從零變更為這個正值時,系統會斷言 ZX_COUNTER_POSITIVE
。接收信號的圖形堆疊元件接著可以讀取計數器的值,判斷柵欄的信號時間。
為了支援這兩種用途,我們新增了四個新的系統呼叫 (zx_counter_create
、zx_counter_add
、zx_counter_read
、zx_counter_write
) 和兩個額外的訊號:
// Asserted when a counter's value is less than or equal to zero.
#define ZX_COUNTER_NON_POSITIVE __ZX_OBJECT_SIGNAL_4
// Asserted when a counter's value is greater than zero.
#define ZX_COUNTER_POSITIVE __ZX_OBJECT_SIGNAL_5
使用 zx_counter_create
建立計數器:
## Summary
Create a counter.
## Declaration
zx_status_t zx_counter_create(uint32_t options, zx_handle_t* out);
## Description
zx_counter_create() creates a counter, which is an object that encapsulates
a signed 64-bit integer value that can be incremented, decremented, read, or
written.
When the value is greater than zero, the signal ZX_COUNTER_POSITIVE is
asserted. Otherwise ZX_COUNTER_NON_POSITIVE is asserted. Exactly one of
these two signals is always asserted, and never both at once.
The newly-created handle will have rights ZX_RIGHTS_BASIC, ZX_RIGHTS_IO, and
ZX_RIGHT_SIGNAL. The value will be zero and the signal
ZX_COUNTER_NON_POSITIVE will be asserted on the newly-created object.
## Rights
Caller job policy must allow ZX_POL_NEW_COUNTER.
## Return value
zx_counter_create() returns ZX_OK and a valid event handle (via *out*) on
success.
On failure, an error value is returned.
## Errors
ZX_ERR_INVALID_ARGS *out* is an invalid pointer, or *options* is non-zero.
ZX_ERR_NO_MEMORY Failure due to lack of memory.
There is no good way for userspace to handle this (unlikely) error.
In a future build this error will no longer occur.
您可以使用 zx_counter_add
增加或減少計數器:
## Summary
Add an amount to a counter.
## Declaration
zx_status_t zx_counter_add(zx_handle_t handle, int64_t amount);
## Description
zx_counter_add() adds amount to the counter referenced by handle.
After the result of the addition, if the counter's value is:
* less than or equal to zero - ZX_COUNTER_NON_POSITIVE will be asserted
and ZX_COUNTER_POSITIVE will be deasserted.
* greater than zero - ZX_COUNTER_POSITIVE will be asserted and
ZX_COUNTER_NON_POSITIVE will be deasserted.
## Rights
handle must have both ZX_RIGHT_READ and ZX_RIGHT_WRITE. Because a counter's
value could be determined by checking for ZX_ERR_OUT_OF_RANGE on a series of
carefully crafted zx_counter_add calls, there is no way to create a counter
that cannot be read, but which can be added.
## Return value
zx_counter_add() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_WRITE.
ZX_ERR_OUT_OF_RANGE if the result of the addition would overflow or underflow.
您可以使用 zx_counter_read
讀取計數器:
## Summary
Read the value of a counter.
## Declaration
zx_status_t zx_counter_read(zx_handle_t handle, int64_t* value);
## Description
zx_counter_read() reads the value of the counter referenced by handle into the
integer value points at.
## Rights
handle must have ZX_RIGHT_READ.
## Return value
zx_counter_read() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_READ.
ZX_ERR_INVALID_ARGS if value is an invalid pointer.
您可以使用 zx_counter_write
編寫計數器:
## Summary
Write the value to a counter.
## Declaration
zx_status_t zx_counter_write(zx_handle_t handle, int64_t value);
## Description
zx_counter_write() writes value to the counter referenced by handle,
asserting/deasserting signals as necessary.
Because concurrent operations on a counter may be interleaved with one another,
an implementation of a "counting semaphore" synchronization protocol should use
zx_counter_add() instead of a sequence of zx_counter_read(), modify,
zx_counter_write().
## Rights
handle must have ZX_RIGHT_WRITE.
## Return value
zx_counter_write() returns ZX_OK on success.
On failure, an error value is returned.
## Errors
ZX_ERR_WRONG_TYPE if handle is not a counter handle.
ZX_ERR_ACCESS_DENIED if handle does not have ZX_RIGHT_WRITE.
ZX_ERR_INVALID_ARGS if value is an invalid pointer.
實作
計數器會由幾個 CL (可能只有一個) 在 Zircon 中實作。Starnix 核心和 Starnix Runner,以及圖形堆疊將在稍後更新為使用計數器。
我們會更新各種語言繫結、FIDL 句柄支援、fidlcat/fidl_coded 等。
成效
我們預期 zx_counter_create
的效能會與 zx_event_create
相似。我們預期 zx_counter_add
和 zx_counter_write
的效能會與 zx_object_signal
相似。
zx_counter_read
應該類似,但它可能也會執行 usercopyout 來傳回值。讀取作業是否執行使用者複製作業,其實是實作細節。我們可以從執行使用者複製的實作項目開始,然後變更 vDSO/核心介面,以便使用註冊更新 vDSO 來儲存傳回的值,以便稍後進行最佳化。
我們認為 Starnix Suspension 用途不會對效能造成任何重大影響 (改善或回歸)。
就 Timestamped Graphics Fence 用途而言,我們預期 CPU 和記憶體的效能會略為提升。計數器需要的記憶體比以 VMO 為基礎的訊號量更少 (數十個位元組,而非超過一頁),且建立成本應該更低。
Ergonomics
Counter 的設計目的是搭配現有的非同步等候模式 (例如 Zircon 連接埠) 使用。
回溯相容性
Counter 是新的 Zircon 物件,因此不會有回溯相容性問題。
安全性考量
沒有安全性考量。
隱私權注意事項
無隱私權考量。
測試
我們會新增核心測試,以便測試新的 Zircon 物件。
說明文件
我們會新增/更新 Syscall 文件。
缺點、替代方案和未知事項
自行捲動
如上所述,zx::event
無法滿足 Starnix 用途的需求。不過,只要使用 zx::event
加上共用的整數和協議即可。除了引入新的 Zircon 物件,Starnix Runner 和 Starnix Kernel 也可以使用 zx::event
和共用且對應的 VMO,其中包含可透過原子方式操控的整數。雖然這種解決方案可行,但較為複雜,且有某些細微之處可能難以正確處理。我們認為 zx::counter
是自然的 Zircon 物件,且加入後對核心複雜度的影響微乎其微。
一般用途和完整性 vs. 狹隘和受限
在設計和建構功能更完整的計數器或信號量物件,以及建構僅滿足目前使用情境的計數器或信號量物件之間,存在著矛盾。我們曾考慮提供類似的物件,但在找到更具體的用途之前,我們決定暫緩這項計畫。這次我們將建構一個相對簡單的應用程式,用於滿足一或兩種情況。我們的意圖是,隨著更多用途出現,我們會改進或取代這個物件,而不會造成類似但不完全相同的物件大量增加。
另請參閱「未來方向」。
無號計數
駕駛用途只需要未簽署計數器。我們可以變更行為,讓值在邏輯上未簽署。我們也可以使用選項標記,指定該值是否為帶符號或無符號,並在該值為無符號時,將遞減至小於零的動作視為錯誤。由於我們未發現可同時支援已簽署和未簽署的強力論點,且已簽署的語意更具彈性,可涵蓋更多未來用途,因此建議只提供已簽署的語意。
缺少初始值
我們考慮了另一種做法,可將初始值傳遞至 create 方法。不過,呼叫端只要在允許句柄「逃逸」前呼叫 add 即可達成相同的效果,因此我們認為這項功能並非必要。
未來方向
在短期至中期內,我們不預期 zx::counter
會廣泛採用。日後我們可能會以更通用且完整的信號機物件取代 zx::counter
(也許還有 zx::event
)。