RFC-0031:类型化表情符号

RFC-0031:类型化书文
状态已拒绝
领域
  • FIDL
说明

指明书名类型的能力和语法。

Gerrit 更改
  • 486059
作者
  • pascallouis@google.com
提交日期(年-月-日)2019-02-05
审核日期(年-月-日)2021-04-07

遭拒的理由

此方案被拒绝,因为它与服务发现的互动质量不佳,并且对估算的实现复杂性影响较小。

与服务发现交互

Fuchsia 上非常常见的模式是,通过调用 fuchsia.io/Directory.Open 按名称请求特定协议。我们将此服务称为“服务发现”。在服务发现期间,客户端与实现 fuchsia.io/Directory 协议的服务器进行交互。当该服务器收到服务发现请求时,它会查找所请求的相应服务,并将通道的服务器端端传输到请求的服务。这意味着客户端正在与一个服务器交互(支持 fuchsia.io/Directory),然后与另一个服务器交互(支持请求的服务)。

遗憾的是,服务发现对 epitaphs 施加了严格的限制。如果故障导致发送 epitaph,客户端将无法判断哪个对等方发布了 epitaph - 是服务器支持 fuchsia.io/Directory,还是请求的服务?

在实践中,服务发现可能不止包括两个服务器,如上所述。因此,书名必须非常宽泛,且不得包含特定领域的详细信息。从本质上讲,表谱的痛点在于必须满足所有可检测到的协议的最低共同标准。

在此方案中,为了解决这一限制,我们讨论了让参与服务发现的所有协议组成 fuchsia/IsDiscoverable 协议。此协议将定义一个类型化 epitaph:


protocol IsDiscoverable {
    epitaph zx.status;
};

fuchsia/IsDiscoverable 的子项都无法定义自定义 epitaph,因此可正确捕获类型系统中的限制。具体而言,这部分提案会:

一个协议(包括以递归方式包括任何和所有组合协议)最多只能有一个 epitaph 类型声明。我们明确阻止两个语义上等效的同一类型声明类型声明。

不过,我们认为将所有可发现协议组合成这个新的 fuchsia/IsDiscoverable 的做法不可行。例如,无法提供静态强制执行,因为按照设计,服务发现是动态的。

与请求流水线交互

请求流水线模式可以视为服务发现模式的泛化,并对表词施加类似的严格限制。

实现的复杂性

虽然 FIDL 功能很复杂(语言规则、对 JSON IR 的扩展、绑定代码、生成的代码、工效学),类型化表征在复杂性方面非常高,而且实用性很低。但这并没有让我们欣喜若狂

接下来该怎么做?

此部分代表作者的观点 (pascallouis@google.com)

推出 epitaphs 时,既定目标是“说明客户端到服务器的连接关闭的原因”。

圣书没有达到这一目标,其用处微乎其微。 依赖于 epitaph 的协议也可以具有所使用的自定义事件,这些事件不会存在上述任何缺点,也不存在由于 FIDL 背后的低层级优先原则而施加的任何载荷限制

epitaph 的一个好处是协议的“干净终止”,服务器大多可以保证客户端对等端将解除与信道的绑定,并避免发出后续请求。

它一直处于浮动状态,引入了适用于事件的 terminal 修饰符,以便从使用标志转换为自定义事件,而不会丢失此“干净的终止”属性。借助终端事件,库作者可以根据需要自由定义事件的载荷。为了支持这一点,我们将扩展传输格式,以分配事务标头的一个标记来指示协议的终止。收到标记为终端的消息后,客户端除了进行正常处理外,还会终止连接。如果客户端不知道该事件(很可能在复杂的请求流水线场景中),客户端可以理解事务标头,而载荷会被舍弃。

总结

我们建议:

  1. 新增用于指示协议上书卷类型类型的语法(我们不更改 epitaph 的默认类型 zx.status);
  2. 一种向绑定公开 epitaph 类型的方法,以便在代码生成期间适当地利用这些信息;
  3. 对合成模型进行补充,指定歌词类型对于每个协议都是唯一的,并且由乐曲继承。

我们希望使用 epitaph 类型:在“leaf”协议(即由自己定义或组成其他协议)中使用;或者,在“composition 树”中使用 epitaph 类型,将 epitaph 类型置于“top”或“root”协议中。

设计初衷

tl;dr 我们喜欢类型。类型也很好,让我们提供更多类型。

语法和错误类型

RFC-0053: Epitaphs 中,我们引入了 epitaphs 的概念,“一种可让服务器在关闭连接之前发送消息的机制,提供关于连接关闭原因的指示”。epitaph 包含错误状态,该状态目前固定为 int32。在查看 epitaphs 时,我们选择将类型修复为 int32,以便与 zx.status 重叠,以便后续将“用户错误”和“协议错误”都折叠起来。

RFC-0060:错误处理中,我们引入了用于明确类型的错误的语法,特别要指出的是“错误类型必须是 int32、uint32 或这些类型之一的枚举”。我们希望允许输入 epitaph 的错误,并选择与错误处理相匹配的表示法。

RFC-023:协议的组合模型中,我们引入了用于声明协议和编写协议的新语法。我们在此针对台词建议的语法遵循这种风格。

我们认为,先前的所有这些决策在方向上都与 (1) 和 (2) 主张一致。

包含墓碑的组合模型

提案 (3) 有两个方面,即每种协议类型的唯一性,以及组合下的行为。

歌词的语义类似于特殊事件的语义,其响应类型对于每个协议都是唯一的。

乐曲下的行为也遵循类似的思维方式,肯定了歌谱所属的类型。下文讨论了另一种定义及其缺点。

通过允许 epitaphs 类型组合,我们将在距离场景引入潜在的中断情况。假设协议 ChildWithEpitaph 构成了一个协议 FarawayParent,并将其 epitaph 类型定义为 SomeSpecificErrorCode 枚举。如果 FarawayParent 事后决定指定作品类型,则系统将禁止乐曲,且 ChildWithEpitaph 的编译将失败。

替代方案:圣书没有 Compose

另一种方法是考虑只有已定义的消息(方法和事件)才能组合成另一个协议。在此替代模型中,如果父协议定义一个许可牌类型,则此类型将独立于子协议的可能独立的许可牌类型。例如,我们允许以下定义:

protocol Parent {
    epitaph ParentErrorCode;
};

protocol Child {
    compose Parent;
    epitaph ChildErrorCode;
};

由于我们未提供协议之间的任何关系(例如,没有子条件、没有进化规则),因此这种替代模型有一些优点。对于同时使用列名输入和构图的情况,该机制提供了更大的自由度。

不过,在不久的将来,我们可以通过所有意图和目的定义协议之间的关系,因此应该根据此设计目标权衡此选择。例如,当我们引入正式的归属关系(“is a”)时,如果协议构成另一个协议类型,并且两者都定义了不兼容的 epitaph 类型,则这些协议将立即无法通过提交测试:希望获得一种类型荣誉类型的客户端可能不适合处理另一种变体类型。

因此,我们认为在今天限制流行语类型的使用方式是更好的选择,这为明天提供了扩展机会。

设计

语法

我们扩展了语法,允许在协议声明中使用 epitaph 节:


protocol SomeProtocol {
    ExampleMethod(...) -> (...);

    epitaph SomeErrorCode;
};

epitaph 节在语法上类似于 compose 节,并且遵循为错误规范选择的语法。

正式地,语法修改如下:

protocol-declaration = ( attribute-list ) , "protocol" , IDENTIFIER ,
                        "{" , ( protocol-member , ";" )*  , "}" ;

protocol-member = ...
                | "epitaph" type-constructor ; [NOTE]

NOTE: The epitaph stanza allows the more liberal type-constructor in the
grammar, but the compiler limits this to int32, uint32, or enum thereof. There
may be only one epitaph stanza per protocol definition.

ABI 和源代码兼容性

当我们引入 epitaphs 时,我们将类型固定为 int32,并且我们预期会将其约束为 32 位。后来,在引入错误语法时确认了将错误代码修正为 32 位。

在这里,我们将保留这一选择,并且如上所述,将 epitaph 类型限制为 int32、uint32 或其枚举。

因此,更改拼音类型(可能是从默认值更改为指定类型)不会修改 ABI 兼容性。

但是,更改音谱类型最有可能是对源代码级别的破坏性更改。绑定作者可能会破坏源代码兼容性。我们预料到这个问题并不是什么问题,因为书名如今并不广泛使用。

JSON IR

我们向 definition/interface 对象添加 definitions/type 类型的成员 epitaph。

例如,我们可能会:

    {
      "name": "example/SomeProtocol",
      "epitaph": {
          "kind": "primitive",
          "subtype": "uint32"
      },
      "methods": [
        {
          "ordinal": 296942602,

epitaph 类型始终存在于接口声明中,如果未另行指定,则设置为默认的 zx.status

撰写圣书

将协议编写成其他协议时,系统会沿用唱片的类型。 例如,对于以下协议:

protocol Parent {
    epitaph SomeErrorCode;
};

protocol Child {
    compose Parent;
};

所生成的 ParentChild 的此书类型为 SomeErrorCode

一个协议最多只能有一个 epitaph 类型声明(以递归方式包括任何和所有组合协议)。我们明确阻止两个语义上等效的同一类型声明类型声明。

此示例无效,应无法编译:

protocol Parent1 {
    epitaph SomeErrorCode1;
};

protocol Parent2 {
    epitaph SomeErrorCode2;
};

protocol Child {
    compose Parent1;
    compose Parent2;
};

此示例也无效,应无法编译:

protocol Parent {
    epitaph SomeErrorCode;
};

protocol Child {
    compose Parent;
    epitaph SomeErrorCode;
};

实施策略

(将由 FIDL 小组在审核后确定。这项变更与之前的许多变更没有什么不同。)

工效学设计

不适用

文档和示例

至少:

  • Wire 格式规范应指明映像为 int32/uint32,需要根据适当的类型进行解释。
  • 应使用“ZX_OK(或相关成功代码)”等语句阐明开发者指南,以便将建议扩展到开发者指定的类型。

向后兼容性

FIDL 源代码:严格向后兼容,因为我们要扩展语言语法。在此变更之前,任何 FIDL 文件都不得在协议声明中包含“epitaph type-constructor;”节。

JSON IR:向后兼容允许额外键的非严格解析器,因为我们将“epitaph”键添加到所有接口声明中。

性能

对性能没有影响。

安全性

没有影响,或因额外类型安全而略有积极性。

测试

fidlc 中的单元测试和绑定生成测试。

缺点、替代方案和未知情况

不适用

早期技术和参考资料

(如文字中所述。)