summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorihc童鞋@提不起劲 <[email protected]>2021-12-20 15:26:59 +0800
committerGitHub <[email protected]>2021-12-20 15:26:59 +0800
commitad940663c648c9ee7b551a22e9dd8da883a5b880 (patch)
tree837b62c5fa75aeebfc66c8d04d41bc3eda29c4bd /docs
parent045590e1b27bd016c93ad4bfd7586dc5d070654a (diff)
feat: allow using shutdown to close connection write (#27)
Diffstat (limited to 'docs')
-rw-r--r--docs/en/compatiable-with-tokio-eco.md38
-rw-r--r--docs/zh/compatiable-with-tokio-eco.md38
2 files changed, 76 insertions, 0 deletions
diff --git a/docs/en/compatiable-with-tokio-eco.md b/docs/en/compatiable-with-tokio-eco.md
new file mode 100644
index 0000000..6ee451d
--- /dev/null
+++ b/docs/en/compatiable-with-tokio-eco.md
@@ -0,0 +1,38 @@
+---
+title: Compatible with Tokio ecology
+date: 2021-12-17 18:00:00
+author: ihciah
+---
+
+# Compatible with Tokio ecology
+A large number of existing Rust components are compatible with Tokio, and they directly rely on Tokio's `AsyncRead` and `AsyncWrite` interfaces.
+
+In Monoio, since the bottom layer is an asynchronous system call, we chose a similar tokio-uring approach: providing an IO interface that transfers the ownership of the buffer. But at this stage, obviously there are not many available libraries that can work, so we need to quickly support functions with a certain performance sacrifice.
+
+## monoio-compat
+`monoio-compat` is a compatibility layer that implements Tokio's `AsyncRead` and `AsyncWrite` based on the buffer ownership interface.
+
+### How it works
+For `poll_read`, the remaining capacity of the slice passed by the user will be read first, and then the buffer held by the user will be limited to this capacity and a future will be generated. After that, every time the user `poll_read` again, it will be forwarded to the `poll` method of this future. When returning to `Ready`, the contents of the buffer are copied to the slice passed by the user.
+
+For `poll_write`, the content of the slice passed by the user is first copied to its own buffer, and then a future is generated and stored. After that, every time the user `poll_write` again, it will be forwarded to the `poll` method of this future. Return the result to the user when returning `Ready`.
+
+In other words, using this compatibility layer will cost you an extra buffer copy overhead.
+
+### Usage restrictions
+The user must ensure that if the previous call to `poll_read` or `poll_write` returns `Pending`, the same slice must be used next time.
+
+There is a very simple length check inside compat, if it does not match, it will panic directly. Of course, this does not guarantee that all problems will be detected. The user must ensure that this complies with the above usage restrictions, otherwise undefined behavior may occur.
+
+For example, some optimizations are used in `h2`: when `poll_write` sends a frame and returns `Pending`, then next time you continue to try `poll_write`, if there is a higher priority data frame, this will be sent first Data Frame. This does not meet our usage restrictions, so it is problematic to work on our compatibility layer.
+
+## Poll-oriented interface and asynchronous system call
+There are two ways to express asynchrony in Rust, one is based on `poll` and the other is based on `async`. `poll` is synchronous, semantically expressing an immediate attempt; while `async` is essentially the syntactic sugar of poll, it will swallow the future and generate a state machine, which is executed in a loop during await.
+
+In Tokio, there are methods similar to `poll_read` and `poll_write`, both of which express the semantics of synchronous attempts.
+
+When it returns to `Pending`, it means that IO is not ready (and the waker of cx is registered for notification), and when it returns to `Ready` it means that IO has been completed. It is easy to implement these two interfaces based on synchronous system calls. Directly make the corresponding system calls and determine the return result. If the IO is not ready, it will be suspended to Reactor.
+
+However, these two interfaces are difficult to implement under asynchronous system calls. If we have pushed an Op into the io_uring SQ, the state of this syscall is uncertain before the corresponding CQE is consumed. We have no way to provide clear completed or unfinished semantics. In `monoio-compat`, we provide a poll-like interface through some hacks, so the lack of capabilities has led to our use restrictions. Under asynchronous system calls, it is more appropriate to pass the ownership of the buffer and cooperate with `async+await`.
+
+At present, the Rust standard library does not have a interface for asynchronous system calls, and there is no related component ecology. We are working hard to solve this problem. \ No newline at end of file
diff --git a/docs/zh/compatiable-with-tokio-eco.md b/docs/zh/compatiable-with-tokio-eco.md
new file mode 100644
index 0000000..e2c9f76
--- /dev/null
+++ b/docs/zh/compatiable-with-tokio-eco.md
@@ -0,0 +1,38 @@
+---
+title: 与 Tokio 生态兼容
+date: 2021-12-17 18:00:00
+author: ihciah
+---
+
+# 与 Tokio 生态兼容
+现有 Rust 组件中有大量的组件与 Tokio 是兼容的,它们直接依赖了 Tokio 的 `AsyncRead` 和 `AsyncWrite` 接口。
+
+而在 Monoio,由于底层是异步系统调用,所以我们选择了类似 tokio-uring 的做法:提供传 buffer 所有权的 IO 接口。但现阶段明显没有很多可用的库可以工作,所以我们需要以一定的性能牺牲来快速支持功能。
+
+## monoio-compat
+`monoio-compat` 是一个兼容层,它基于 buffer 所有权的接口实现 Tokio 的 `AsyncRead` 和 `AsyncWrite`。
+
+### 工作原理
+对于 `poll_read`,这里会先读到用户传递的 slice 的剩余容量,之后将自己持有的 buffer 限制为该容量并生成 future。之后每次用户再次 `poll_read`,都会转发到这个 future 的 `poll` 方法。在返回 `Ready` 时将 buffer 的内容拷贝到用户传递的 slice 中。
+
+对于 `poll_write`,会将用户传递 slice 的内容先拷贝至自有 buffer,之后生成 future 并存储。之后每次用户再次 `poll_write`,都会转发到这个 future 的 `poll` 方法。在返回 `Ready` 时返回结果给用户。
+
+也就是说,使用这个兼容层会让你额外付出一次 buffer 拷贝开销。
+
+### 使用限制
+用户必须保证,如果上次调用 `poll_read` 或 `poll_write` 返回了 `Pending`,那么下次调用必须使用相同的 slice。
+
+compat 内部有非常简单的长度检查,如果不符会直接 panic。当然这并不能保证检测出所有的问题,用户务必保证这符合上述使用限制,否则可能会产生未定义行为。
+
+例如,在 `h2` 里使用了一些优化:当 `poll_write` 发送一个帧并返回了 `Pending`,那么在下次继续尝试 `poll_write` 时,如果有优先级更高的数据帧,会优先发送这个数据帧。这不符合我们的使用限制,所以在我们的兼容层上工作起来是有问题的。
+
+## 面向 poll 的接口与异步系统调用
+Rust 中有两种表达异步的方式,一种是基于 `poll` 的,一种是基于 `async` 的。`poll` 是同步的,语义上表达立即尝试;而 `async` 本质上是 poll 的语法糖,它会吞掉 future 并生成状态机,await 的时候是在循环执行这个状态机。
+
+在 Tokio 中,有类似 `poll_read` 和 `poll_write` 的方法,它们都表达了同步尝试这个语义。
+
+当返回 `Pending` 时意味着 IO 未就绪(并给 cx 的 waker 注册了通知),当返回 `Ready` 时意味着 IO 已经完成了。基于同步系统调用实现这两个接口很容易,直接做对应系统调用并判断返回结果,如果 IO 未就绪则挂起到 Reactor。
+
+然而在异步系统调用下这两个接口很难实现。如果我们已经将一个 Op 推入 io_uring SQ,那么在消费到对应 CQE 之前,这次 syscall 的状态是不确定的。我们没有办法提供明确的已完成或未完成的语义。在 `monoio-compat` 中我们通过一些 hack 提供了 poll-like 的接口,所以能力的缺失导致了我们的使用限制。在异步系统调用下,传递 buffer 所有权并配合 `async+await` 是更合适的。
+
+目前 Rust 标准库并没有通用的面向异步系统调用的接口,相关组件生态也没有。我们正在努力改善这个问题。 \ No newline at end of file