Rustその2 Advent Calendar 2018の14日目の記事です。
mioの実装を読むという記事を書いていたのですが、記述量が多くなって来たので、Mac内でLinux用のコードのマクロを展開したコードを取得する方法という内容だけ別記事に切り出しました。
まず、mioはRustの低レイヤーな非同期ioライブラリなのですが、内部はLinuxならepoll、Mac(BSD)ならkqueueを使うという実装になっています。(Windowsは...)
epoll.rsにdlsym!(fn epoll_create1(c_int) -> c_int);
というマクロを使っている所があります。
impl Selector {
pub fn new() -> io::Result<Selector> {
let epfd = unsafe {
// Emulate `epoll_create` by using `epoll_create1` if it's available
// and otherwise falling back to `epoll_create` followed by a call to
// set the CLOEXEC flag.
dlsym!(fn epoll_create1(c_int) -> c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => {
cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
}
None => {
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
};
...
}
...
}
マクロはdlsym.rs内に下記のものがあります。
macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(bad_style)]
static $name: ::sys::unix::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
::sys::unix::dlsym::DlSym {
name: concat!(stringify!($name), "\0"),
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
_marker: ::std::marker::PhantomData,
};
)
}
<unsafe extern fn($($t),*) -> $ret>
は文法的に
<unsafe extern "C" fn ... -> ...>
となると思ったんだけど、マクロ内には "C"
が無いんだよな...と思い、これを展開したものを見たかったので調べたらcargo expandでマクロを展開出来ることを知りました。
ただ、上のロジックはMac(BSD)では通らないので、 単純にcargo expand
しただけではみることが出来ません...
なので、 Linux用にコンパイルする必要があります。幸いRustはクロスコンパイル出来きます。
いける!と思ったのですが、rustupのデフォルトをnightly
にしないと上手く動かず、半日潰しました...
# rustupを事前にインストールしdefaultをnightlyにする
# クロスコンパイル用のmuslをインストール
$ brew install FiloSottile/musl-cross/musl-cross
# x86_64-unknown-linux-muslのコンパイル環境をセットアップ
$ rustup target add x86_64-unknown-linux-musl
# linux用のマクロを展開したソースコードを取得
$ cargo expand --release --target=x86_64-unknown-linux-musl
展開した結果が下記になります。
impl Selector {
pub fn new() -> io::Result<Selector> {
let epfd = unsafe {
#[allow(bad_style)]
static epoll_create1: ::sys::unix::dlsym::DlSym<unsafe extern "C" fn(c_int) -> c_int> =
::sys::unix::dlsym::DlSym{
name:"epoll_create1\u{0}",
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
_marker: ::std::marker::PhantomData,
};
match epoll_create1.get() {
Some(epoll_create1_fn) => {
cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
}
None => {
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
};
...
}
...
}
展開してみると<unsafe extern "C" fn(c_int) -> c_int>
にちゃんとなっています...
何でだろう...
そもそも、FFIを調べてみるとextern fn
でも良いけど、expand時に補ってくれているだけかも...?