RustのPollってなんなのさ

# この記事について
Rustのnetモジュールを使っていたら出てきた`Poll`について自分なりにまとめてみています。
間違っていたら教えてもらえると嬉しいです。

## 注意
今回調べた`Poll`は`std::task::Poll`ではなく`mio::Poll`です。

# Pollとは？
> `Poll` allows a program to monitor a large number of `Evented` types, waiting until one or more become "ready" for some class of operations; e.g. reading and writing. An Evented type is considered ready if it is possible to immediately perform a corresponding operation; e.g. read or write.

`Evented`型のインスタンスを監視し，中身が`Ready`になるまで待ちます。
`Evented`型のインスタンスがすぐに値を返せる状態になったら，`Ready`だとみなします。

## エッジトリガとレベルトリガ
`Poll`ではイベントが発火するときの方式が２種類あります。
### エッジトリガ
> 割り込み通知などにおいて、信号がある状態から別の状態へ変化した時点でトリガが引かれる方式。

### レベルトリガ
> 信号レベルが変化した瞬間ではなく「信号レベルがある基準より上か下か」という信号の状態に注目してトリガのオン・オフを決める方式。

という感じのようです。（[参考](http://mackey-lab.hatenablog.com/entry/2015/04/22/005214)）

これが実際に`Poll`の中だとどのように使われているのかというと，

>With edge-triggered events, operations must be performed on the `Evented` type until `WouldBlock` is returned. In other words, after receiving an event indicating readiness for a certain operation, one should assume that `Poll::poll` may never return another event for the same token and readiness until the operation returns `WouldBlock`.
> By contrast, when level-triggered notfications was requested, each call to `Poll::poll` will return an event for the socket as long as data remains in the socket buffer. Generally, level-triggered events should be avoided if high performance is a concern.

エッジトリガの方は`Event`型のインスタンスが`WouldBlock`を返すまで動き続けます。
レベルトリガの方は，ソケット内にデータが残っている間は`Event`型のインスタンスがイベントを返し続けます。パフォーマンスを求めるならレベルトリガの方は避けるべきみたいです。

## サンプルコード

```rust
use mio::{Events, Poll, Ready, PollOpt, Token};
use mio::tcp::TcpStream;

use std::net::{TcpListener, SocketAddr};

// Bind a server socket to connect to.
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let server = TcpListener::bind(&addr).unwrap();

// Construct a new `Poll` handle as well as the `Events` we'll store into
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);

// Connect the stream
let stream = TcpStream::connect(&server.local_addr().unwrap()).unwrap();

// Register the stream with `Poll`
poll.register(&stream, Token(0), Ready::all(), PollOpt::edge()).unwrap();

// Wait for the socket to become ready. This has to happens in a loop to
// handle spurious wakeups.
loop {
    poll.poll(&mut events, None).unwrap();

    for event in &events {
        if event.token() == Token(0) && event.readiness().is_writable() {
            // The socket connected (probably, it could still be a spurious
            // wakeup)
            return;
        }
    }
}
```
まず，指定したアドレスにサーバーをバインドします。
次に，`Poll`インスタンスを`Poll::new()`で作成します。
そして`Events`型のインスタンスを作成します。これらは両方とも`mio`クレートのものです。
次は`TcpStream`をバインドしたアドレスに接続します。ここがソケットと呼ばれる部分になります。
そのあとに，`poll`インスタンスに`stream`と`Token(0)`を登録します。
そして`loop`に突入します。

`Poll::poll()`にはこのような説明が書いてあります。
>Wait for readiness events

>Blocks the current thread and waits for readiness events for any of the Evented handles that have been registered with this Poll instance. The function will block until either at least one readiness event has been received or timeout has elapsed. A timeout of None means that poll will block until a readiness event has been received.

なので，`poll.poll(&mut events, None).unwrap();`の部分でソケットになにかデータが来るまで待ちます。
受け取った`events`から要素をforループで取り出し，要素の中身が`Token(0)`で，if式の条件を満たしていたら`return`が実行されます。

# まとめ
`Poll`でTCPのソケット周りの管理ができるっていう認識でいこうと思います！

# 参考リンク
> https://doc.rust-lang.org/std/task/enum.Poll.html
> https://tech-blog.optim.co.jp/entry/2019/07/05/173000
> http://mackey-lab.hatenablog.com/entry/2015/04/22/005214