enif_select() shenanigans

This commit is contained in:
lilly 2026-05-22 15:38:11 +02:00
commit 2446dcf05d
Signed by: lilly
SSH key fingerprint: SHA256:y9T5GFw2A20WVklhetIxG1+kcg/Ce0shnQmbu1LQ37g
4 changed files with 47 additions and 9 deletions

View file

@ -8,6 +8,6 @@ edition = "2021"
crate-type = ["cdylib"]
[dependencies]
rustler = "0.37.4"
rustler = { workspace = true }
tun-rs = { version = "2.8.3", default-features = false, features = ["blocking", "experimental"] }

View file

@ -1,7 +1,9 @@
use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::ops::Deref;
use std::os::fd::AsRawFd;
use rustler::sys::{ErlNifEvent, ERL_NIF_SELECT_STOP};
use rustler::types::tuple::{get_tuple, make_tuple};
use rustler::{
resource_impl, Binary, Decoder, Encoder, NifResult, OwnedBinary, Resource, ResourceArc, Term,
@ -11,6 +13,7 @@ use tun_rs::{DeviceBuilder, SyncDevice};
mod atoms {
rustler::atoms! {
ok,
undefined,
resource_busy,
permission_denied,
would_block,
@ -92,7 +95,12 @@ impl<'a> Decoder<'a> for WrappedIpAddr {
}
#[resource_impl]
impl Resource for TunHandle {}
impl Resource for TunHandle {
fn stop<'a>(&'a self, _env: rustler::Env<'a>, event: ErlNifEvent, is_direct_call: bool) {
println!("event: {event:?}");
println!("is_direct_call: {is_direct_call:?}");
}
}
#[rustler::nif]
fn make_tun_device(env: rustler::Env, packet_info: bool) -> NifResult<Term> {
@ -101,9 +109,10 @@ fn make_tun_device(env: rustler::Env, packet_info: bool) -> NifResult<Term> {
.packet_information(packet_info)
.build_sync()
.map_err(map_io_error)?;
device.set_nonblocking(true).map_err(map_io_error)?;
// device.set_nonblocking(true).map_err(map_io_error)?;
let handle = ResourceArc::new(TunHandle { device });
register_io(env, &handle);
Ok(make_ok_tuple(env, handle))
}
@ -198,6 +207,32 @@ fn set_mtu(handle: ResourceArc<TunHandle>, mtu: u16) -> NifResult<()> {
handle.device.set_mtu(mtu).map_err(map_io_error)
}
/// Register pending IO with the erlang vm to later receive a callback when
/// something happened to the device and IO is available.
fn register_io(env: rustler::Env, handle: &ResourceArc<TunHandle>) {
// Safety: yolo! I have no idea what I'm doing
unsafe {
let enif_env = env.as_c_arg();
let enif_event: ErlNifEvent = handle.device.as_raw_fd();
let enif_flags = rustler::sys::ERL_NIF_SELECT_READ;
let enif_obj = handle.as_c_arg();
let enif_pid = std::ptr::null();
let enif_ref = atoms::undefined().to_term(env).as_c_arg();
rustler::sys::enif_select(
enif_env, enif_event, enif_flags, enif_obj, enif_pid, enif_ref,
);
rustler::sys::enif_select(
enif_env,
enif_event,
ERL_NIF_SELECT_STOP,
enif_obj,
enif_pid,
enif_ref,
);
}
}
fn map_io_error(error: io::Error) -> rustler::Error {
rustler::Error::Term(match error.kind() {
io::ErrorKind::ResourceBusy => Box::new(atoms::resource_busy()),