歴史
- 昔はスケジューラ実装にsegment stackを使っていた(libgreenを同梱)
- 1.0なる前にやめた
- libgreenをライブラリとして分離 (しかしメンテされず...)
ライブラリ
libgreen以降に実装がいくつか
- context-rs/coroutine-rs
- libfringe
context-rs/coroutine-rs
- どちらもzonyitooさんが書かれたもの
- Boost.{Context,Coroutine}の移植
libfringe
- パフォーマンスがよいらしい
- なんでかよくわからん、、XMMあたりも保存レジスタ指定入っとるし...
- 分岐予測ミス回避のために
ret
でなくjmp+pop
にしてるとかはあった
- primitiveなcontext-rs相当とGeneratorのようないいかんじのやつ
libfringe (Cont.)
- Generatorの実装 (READMEのやつ)
extern crate fringe;
use fringe::{OsStack, Generator};
fn main() {
let stack = OsStack::new(1 << 16).unwrap();
let mut gen = Generator::new(stack, move |yielder, ()| {
for i in 1..4 { yielder.suspend(i) }
});
println!("{:?}", gen.resume(())); // Some(1)
println!("{:?}", gen.resume(())); // Some(2)
println!("{:?}", gen.resume(())); // Some(3)
println!("{:?}", gen.resume(())); // None
}
コルーチン使いどころ
- よくあるのが非同期I/Oと合わせて使う
- オリジナルはRuss Coxのlibtask?
- もっと前からありそう
非同期I/O
- high performaceなサーバを書くのに必須
- コルーチンでI/O待ちを減らしたい
- EWOULDBLOCK/EAGAINでスレッドを切り替え
Rustと非同期I/O
- mio(epoll/kqueueとかの薄い抽象化レイヤ)がデファクト
- 最近mioの上にtokio-rs/futuresなどなんかイケてるやつがのってる
mioco (mio + coroutine-rs)
- 一番star多いしたぶんなんかすごい, zmq-rsとかで使われてる
coio-rs (mio + coroutine-rs)
- 内部のスケジューラにChase-Lev's deque使ってる, flowggerというfluentd-likeなやつで使われてる
miocoを例に
mioco::start(|| -> io::Result<()> {
let listener = try!(TcpListener::bind(&addr));
loop {
let mut conn = try!(listener.accept());
mioco::spawn(move || -> io::Result<()> {
let mut buf = [0u8; 1024 * 16];
loop {
let size = try!(conn.read(&mut buf));
if size == 0 {/* eof */ break; }
let _ = try!(conn.write_all(&mut buf[0..size]));
}
Ok(())
});
}
}).unwrap().unwrap();
mioco (Cont.)
- mioco::spawnで軽量スレッド生成からスケジューラ登録まで
- パッと見はブロックしそうだけどうまく動くらしい
- 複数OSスレッドの例もexampleにあった(各OSスレッドがacceptorを持つ形)
- Rustなのでマルチスレッド拡張も容易?
- スレッドごとにeventloopもつっぽい
まとめ (まとまっていない)
- 非同期I/O+コルーチンなライブラリがぽつぽつ出てきた
- high performaceな鯖書くための部品が揃ってきた感ある
- 使っていきたみが高まっている