https://github.com/rust-lang/rust/blob/e9271846294c4ee5bd7706df68180320c0b5ff20/library/std/src/sys/wasi/thread.rs#L137 *1
pub fn sleep(dur: Duration) { let nanos = dur.as_nanos(); assert!(nanos <= u64::MAX as u128); const USERDATA: wasi::Userdata = 0x0123_45678; let clock = wasi::SubscriptionClock { id: wasi::CLOCKID_MONOTONIC, timeout: nanos as u64, precision: 0, flags: 0, }; let in_ = wasi::Subscription { userdata: USERDATA, u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, }; unsafe { let mut event: wasi::Event = mem::zeroed(); let res = wasi::poll_oneoff(&in_, &mut event, 1); match (res, event) { ( Ok(1), wasi::Event { userdata: USERDATA, error: wasi::ERRNO_SUCCESS, type_: wasi::EVENTTYPE_CLOCK, .. }, ) => {} _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), } } }
このファイルが何かはあまりよくわかっていません。
src/sysディレクトリにunix
やwindows
があります。
rustでクロスコンパイルするときに、ターゲットOSに合わせて埋め込まれる実装だろうと思っています。
なんとなく読み方がわかったのでメモしておきます。
poll_oneoff in wasi - Rust にpoll_oneoff
関数のリファレンスがあります。
pub unsafe fn poll_oneoff( in_: *const Subscription, out: *mut Event, nsubscriptions: Size ) -> Result<Size, Errno>
実際の呼び出しが
wasi::poll_oneoff(&in_, &mut event, 1);
です。引数を3つ受け取っています。
- _in
- event
- 1
_in
は少し上で定義している構造体です。
let in_ = wasi::Subscription { userdata: USERDATA, u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, };
APIリファレンスとSubscription
という型名が一致しています。
Subscription
にはSubscriptionU
と言う構造体が入っていて、さらにその中にSubscriptionUU
構造体があります。
その中にclock
が入ります。
clock
は
let clock = wasi::SubscriptionClock { id: wasi::CLOCKID_MONOTONIC, timeout: nanos as u64, precision: 0, flags: 0, };
です。
要するにこの関数はpoll_oneoff
を呼ぶのに必要なデータを作って、poll_oneoff
を呼び出しています。
CLOCKID_MONOTONIC
というのは
CLOCK_REALTIMEとCLOCK_MONOTONIC #Linux - Qiita
CLOCK_MONOTONIC 時刻はかならず単調増加する システムの時刻変更の影響を受けるが、大きく変化することはないし、時間が戻ったりもしない
経過時間をみる指定のようです。
timeout: nanos as u64
がタイムアウトまでのナノ秒を指定しているようです。
poll_oneoff
関数の戻り値の処理は
match (res, event) { ( Ok(1), wasi::Event { userdata: USERDATA, error: wasi::ERRNO_SUCCESS, type_: wasi::EVENTTYPE_CLOCK, .. }, ) => {} _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), }
パターンマッチで成功か失敗を判定して、失敗したら例外をあげているようです。
おそらくこれと似たような関数を実装して、Kernel#sleep
を置き換えるgemを作れば、ruby.wasmでKernel#sleep
が使えるようになるはずです。
そうそうruby.wasmはごく最近、gemをwasmバイナリにpackできるようになりました*2。
さて、RubyのRust拡張ってどうやって作ればいいのでしょうか?
*1:前回の記事の後により新しいバージョンを発見しました。