RFC-0023:协议的组合模型 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 关键字界面已被关键字协议取代。明确了扩展协议以指明组合模型,其中一个协议可定义为一组消息,并通过一个或多个其他协议进行扩展。用于协议扩展的语法已从类似于继承的语法更改为类似于 mixins 的语法。 |
作者 | |
提交日期(年-月-日) | 2018-12-10 |
审核日期(年-月-日) | 2019-01-09 |
摘要
我们建议进行以下更改:
- 关键字 interface 被关键字 protocol 取代。 (我们将在本文档的其余部分使用“协议”一词。)
- 明确了扩展协议以指明组合模型,其中一个协议可以 被定义为一组消息,并通过一个或多个其他协议进行扩展。
- 用于协议扩展的语法已从类似于继承的语法进行更改 类似于 mixins 的一个模型。
- 具有约束力的作者必须避免采用(例如“是”层次结构、继承、子类型化) 使用目标语言表示组合协议时需要使用的内容。
设计初衷
术语“接口”所涵盖的上下文负担包括方法和 重载、构造函数和析构函数,以及作为消息接收者的对象模型等。
然而,FIDL 的目标较为宽泛,旨在描述协议 两个对等体(即一组可交换的消息)。
在启动 FIDL API 时,我们会清楚地说明这一点,例如 “尽管语法类似于面向对象的接口的定义,但其设计 更类似于网络协议,而不是对象系统。" 面对引入更多“面向对象的选项”时功能 我们已经避免了这种行为(例如,最近在评论中有关超载 RFC-0020:序数哈希)。
我们希望通过语言更清楚地区分这一区别,因此建议您将
将关键字 interface
替换为关键字 protocol
,从而更改语法。
此外,“是”借用继承语法暗示的关系是不合理的, 并导致错误的预期。 (为清晰起见,FIDL 不提供此类继承语义,但根据语法进行了建议。) 请参阅“是 A”该关系被视为有害 部分。
设计
此方案引入了正式语义来描述进程交互和协议。
此提案更改了 FIDL 源语言,以阐明协议扩展的语义, 并为绑定作者提供了新的指导。
目前,继承关系未在 JSON IR 中表示,因此无法 可供绑定作者使用。 因此,我们预计,这一新指南的修改方式应该不会 除了改进文档之外,您还可以学习到编写好的绑定代码。
此提案不会更改传输格式。
此方案不会更改 JSON IR,不过我们预计会在名称中包含密钥重命名 大量更改。
协议模型
Zircon 通道不需要为其携带的载荷指定特定架构。 FIDL 基于此原语构建,并限制通道只能承载特定协议。 这样一来,FIDL 就能为通道两端赋予意义和名称。 我们将一种称为“客户端”,另一种称为“服务器”。
我们的模型将协议描述为一组定向互动, (可选)摘要。 我们将会话称为客户端之间通信的特定实例 以及使用协议的服务器
方向可以是从客户端到服务器,也可以是从服务器到客户端。
互动始于请求,并且可选择性地需要响应。 我们经常使用“一劳永逸”的说法或“单向”实现无响应交互 以及“通话”一词。
请求和响应都是“消息”,以标头 其后是结构体的载荷、结构体的参数 请求或响应。
目前,我们限制服务器到客户端的消息具有响应。 简单来说,“事件只会触发和忽略”。
摘要是指结束会话的服务器到客户端的交互。 如需了解详情,请参阅 RFC-0053:墓碑。
此模型中不包含更复杂的交互,例如三次握手 SYN/SYN-ACK/ACK(针对 TCP)。 我们认为这不在范围内,并且不太可能用于未来模型优化。
组合模型
目前,协议既可定义交互,也可扩展一个或多个协议。 生成的协议(“组合协议”)包含直接定义的所有交互, 并且继承由其前代定义的所有互动(直接或间接)。
例如,定义如下的 Child
协议:
protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child { compose Parent1; compose Parent 2; Method3(); };
将具有所有这三个互动:Method1
、Method2
和 Method3
。
不过,Child
中是否因组合而定义了 Method1
和 Method2
,
不会传送到特定语言的后端,也就是说,
。
“Is A”视为有害关系
由于协议可以在两个方向上传递请求,因此具有子类型关系 需要更多关注 因此,我们不允许协议中包含“是”关系, 协议。
例如,假设我们有两个协议:
protocol Parent { Method(); };
protocol Child { ->Event(...); };
我们是否允许将采用“Child
”协议的频道视为Parent
(即“Child
为 Parent
”关系),我们会向客户端公开接收 Event
,
而他们无法处理
如需查看具体示例,请参阅下一部分。
而是希望支持特定的协议注释,例如“唯一的客户端” 与服务器交互”来支持和允许“是”关系。 发生这种情况时,此类关系将传送到 JSON IR 以供使用后端。
依赖“Is A”今天的婚恋状况
我们来看一个具体示例,fuchsia.media 库将各种协议组合在一起。 具体而言:
AudioCapturer 和 AudioRenderer 均不定义事件,即这些事件纯粹为 “客户端到服务器协议”- 是单向的。 (StreamSource 定义了两种事件,但我们在这里具体讨论的是 协议自己的定义。)
因此,如果客户端知道如何与 StreamBufferSet 或 StreamSource 进行交互 (分别为 StreamBufferSet 或 StreamSink),那么它还可以与 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.目前,这些接口依靠序号进行手动多路复用 来区分各个类型。
我们可以使用“->”语法。 另一种方法是仅在扩展包含 反向协议,这样无需引入任何方向语法, 因为我们会推迟使用反向协议的扩展程序。
我们允许将 Compose 代码块放置在协议定义中的任意位置, 我们还允许使用多个 Compose 块。 我们也可以只有一个代码块,也可以要求它位于顶部。 在这里,我们选择开放,而非依赖于自动的格式和/或样式指南 而不是将强制执行纳入到语言本身中。
JSON IR
在此次更改过程中,我们不会更改 JSON IR。
而是将其重命名为“interface_declarations”键为“protocol_declarations” 作为一系列更改的一部分 这一较大的更改集将需要多步骤方法,将架构版本从 0.0.1 到 0.0.2,并且后端有过渡期进行调整。
一定距离的中断,以及使用 [FragileBase]
在此提案中,距离发生损坏的可能性状态保持不变,
因此,我们重申对任何需要扩展的协议使用 [FragileBase]
1。
绑定作者指南
- 绑定必须避免采用(例如“是”层次结构、继承、子类型化) 使用目标语言表示组合协议时需要使用的内容。
- 应该收到未知序数时出错。 绑定应以“未知序数错误”形式提示此结果,然后关闭通道。
实施策略
三个步骤:
- 添加对新语法的支持;
- 转换所有 FIDL 文件以使用新语法;
- 不再支持旧语法。
工效学设计
此更改使 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 语法 embedding(嵌入)
示例:
protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child {
Parent1;
Parent2;
Method3();
};
备注:Go 语言规范 关于接口和嵌入的说明。
替代语法:使用声明 (jeffbrown@)
原因:看起来不像继承,而是重复使用现有关键字来指明名称 纳入考量范围 不太可能与方法声明或“属性2”混淆。
示例:
protocol Parent1 { Method1(); };
protocol Parent2 { Method2(); };
protocol Child {
using Parent1;
using Parent2;
Method3();
};
备注:FIDL、C++、Rust 和其他语言中的优先顺序。
替代关键字
“compose
”的替代词关键字:
extends
(pascallouis@)contains
(smklein@)
先验技术和参考资料
没有具体要求。
Cap'n Proto 具有 继承,包括多重继承(采用 mixins 的样式)。