LoginSignup
7
1

More than 3 years have passed since last update.

RustのPollってなんなのさ

Posted at

この記事について

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

注意

今回調べたPollstd::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ではイベントが発火するときの方式が2種類あります。

エッジトリガ

割り込み通知などにおいて、信号がある状態から別の状態へ変化した時点でトリガが引かれる方式。

レベルトリガ

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

という感じのようです。(参考

これが実際に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型のインスタンスがイベントを返し続けます。パフォーマンスを求めるならレベルトリガの方は避けるべきみたいです。

サンプルコード

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インスタンスにstreamToken(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

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1