-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use a slice for the event list of kevent() #1044
base: main
Are you sure you want to change the base?
Conversation
This removes the need for dynamically allocating a Vec when the kevent() function is called. The documentation is also rephrased slightly as slices don't really have a "capacity", but only a fixed-size length. This fixes bytecodealliance#1043.
Ugh, I'm now reminded of why the One option is to require |
Using const generics you can do this: pub unsafe fn kevent<'a, const N: usize>(
kqueue: impl AsFd,
changes: &[Event],
timeout: Option<Duration>,
) -> io::Result<&'a [Event]> {
let timeout = timeout.map(|timeout| backend::c::timespec {
tv_sec: timeout.as_secs() as _,
tv_nsec: timeout.subsec_nanos() as _,
});
let mut out_slice: [MaybeUninit<Event>; N] = unsafe { MaybeUninit::uninit().assume_init() };
syscalls::kevent(kqueue.as_fd(), changes, &mut out_slice, timeout.as_ref())
.map(|res| from_raw_parts(out_slice.as_ptr().cast(), res as usize))
} You then use it like so: kevent::<10>(&kq, &subs, None) In case of the out.extend_from_slice(unsafe { kevent::<10>(&kq, &subs, None) }?); I'm not a fan of having to specify the size using the turbofish syntax though, but I do like the idea of |
Another option is this: We implement impl Default for Event {
fn default() -> Self {
Event {
inner: kevent_t {
..unsafe { zeroed() }
},
}
}
} The let mut events = [Event; 10] = [Event::default(); 10];
for event in kevent(..., ..., &mut events, None) {
} This removes the need for const generics and gives users control over the slice. The big downside is that your slice of 10 values (in this case) may only contain 3 values that are in fact initialized, so the use of this setup would very much remain unsafe. |
I pushed the const generics example so it's easier to discuss, but I'd very much like some feedback on what the best approach would be here. |
Why not use |
One potential option that comes to mind is this: instead of taking raw slices for the event list, we introduce a use std::ptr::null_mut;
// For the sake of this example I'm just using a raw pointer here.
type Event = *mut ();
struct Events<const N: usize> {
events: [Event; N],
}
impl<const N: usize> Events<N> {
fn new() -> Events<N> {
Events { events: [null_mut(); N] }
}
fn as_slice(&self) -> &[Event] {
&self.events
}
}
fn main() {
let ev: Events<10> = Events::new();
println!("length: {}", ev.as_slice().len());
} You still need to specify the explicit length in the In this particular example I've added an The downside is that |
@notgull Taking a |
To illustrate things:
In terms of usage, I quite like the second approach as it no longer requires the caller to use unsafe code (e.g. |
}, | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As Event
implements Clone
I think this is technically not required, as we can just do the zero'ing in the Events
type. This is in fact probably better such that users can't create a zero'd (and thus basically invalid) Event
type themselves.
This removes the need for dynamically allocating a Vec when the kevent() function is called. The documentation is also rephrased slightly as slices don't really have a "capacity", but only a fixed-size length.