0
\$\begingroup\$

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();
 }
}
```
asked Jan 30, 2022 at 21:41
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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.

answered Jan 31, 2022 at 4:34
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.