So this one was quite rough to implement. I needed to pass an FnOnce
to a foreign function through a c_void
which requires double boxing. Another fun detail is that the callback is not called until someone actually calls SwitchFiber
which isn't required to happen. So simply passing the ownership to the callback was not an option, because then it would like if it wasn't called. Instead I needed to keep track of whether the callback actually consumed the FnOnce
, which ended up being another Box
.
I would love some feedback to see if this code can be cleaned up in any way, perhaps reducing its complexity (or at least the amount of boxes).
use windows_sys::{
Win32::Foundation::*,
Win32::System::Threading::*,
};
use std::os::raw::c_void;
use crate::PlatformError;
struct FiberInternals {
handle: *mut c_void,
entry_point: *mut Box<dyn FnOnce()>
}
pub struct Fiber(Box<FiberInternals>);
impl Fiber {
fn new<F>(entry_point: F) -> Result<Fiber, PlatformError>
where F: FnOnce() -> () + 'static
{
let entry_point_box_box: Box<Box<dyn FnOnce()>> = Box::new(Box::new(entry_point));
let entry_point_box_ptr = Box::into_raw(entry_point_box_box);
let mut internals = Box::new(FiberInternals {
handle: std::ptr::null_mut(),
entry_point: entry_point_box_ptr
});
internals.handle = unsafe {
CreateFiber(0, Some(fiber_entry_point), &mut *internals as *mut _ as *mut c_void)
};
if internals.handle == std::ptr::null_mut() {
Err(PlatformError::new(unsafe { GetLastError() }))
} else {
Ok(Fiber(internals))
}
}
}
impl Drop for Fiber {
fn drop(&mut self) {
if self.0.entry_point != std::ptr::null_mut() {
let entry_point = unsafe { Box::from_raw(self.0.entry_point) };
}
}
}
extern "system" fn fiber_entry_point(parameter: *mut c_void) {
unsafe {
let mut internals = &mut *(parameter as *mut FiberInternals);
let entry_point = Box::from_raw(internals.entry_point);
internals.entry_point = std::ptr::null_mut();
entry_point();
}
}
```
1 Answer 1
FiberInternals::entry_point
could simply be an Option<Box<dyn FnOnce()>>
. In fiber_entry_point
, you could use Option::take()
to take ownership of the Box
and leave a None
in its place. You could then remove your Drop
implementation, because the compiler will drop the Box
automatically if the value is a Some
.