Various cleanups (#2046)

This includes a few safety improvements and a variety of other cleanups. See the individual commits.
This commit is contained in:
bjorn3 2021-05-01 20:07:06 +00:00
parent 82014a3abd
commit 3af3334cfe
11 changed files with 101 additions and 93 deletions

View File

@ -68,7 +68,8 @@ where
#[reflect(ignore)] #[reflect(ignore)]
handle_type: HandleType, handle_type: HandleType,
#[reflect(ignore)] #[reflect(ignore)]
marker: PhantomData<T>, // NOTE: PhantomData<fn() -> T> gives this safe Send/Sync impls
marker: PhantomData<fn() -> T>,
} }
enum HandleType { enum HandleType {
@ -229,10 +230,6 @@ impl<T: Asset> Clone for Handle<T> {
} }
} }
// SAFE: T is phantom data and Handle::id is an integer
unsafe impl<T: Asset> Send for Handle<T> {}
unsafe impl<T: Asset> Sync for Handle<T> {}
/// A non-generic version of [Handle] /// A non-generic version of [Handle]
/// ///
/// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and /// This allows handles to be mingled in a cross asset context. For example, storing `Handle<A>` and

View File

@ -12,11 +12,7 @@ pub trait Bytes {
} }
/// A trait that indicates that it is safe to cast the type to a byte array reference. /// A trait that indicates that it is safe to cast the type to a byte array reference.
pub unsafe trait Byteable pub unsafe trait Byteable: Copy + Sized {}
where
Self: Sized,
{
}
impl<T> Bytes for T impl<T> Bytes for T
where where
@ -46,7 +42,7 @@ pub trait FromBytes {
impl<T> FromBytes for T impl<T> FromBytes for T
where where
T: Byteable + Copy, T: Byteable,
{ {
fn from_bytes(bytes: &[u8]) -> Self { fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!( assert_eq!(
@ -79,13 +75,6 @@ where
} }
} }
unsafe impl<T> Byteable for [T]
where
Self: Sized,
T: Byteable,
{
}
unsafe impl<T, const N: usize> Byteable for [T; N] where T: Byteable {} unsafe impl<T, const N: usize> Byteable for [T; N] where T: Byteable {}
unsafe impl Byteable for u8 {} unsafe impl Byteable for u8 {}
@ -154,7 +143,7 @@ where
impl<T> Bytes for Vec<T> impl<T> Bytes for Vec<T>
where where
T: Sized + Byteable, T: Byteable,
{ {
fn write_bytes(&self, buffer: &mut [u8]) { fn write_bytes(&self, buffer: &mut [u8]) {
let bytes = self.as_slice().as_bytes(); let bytes = self.as_slice().as_bytes();
@ -168,7 +157,7 @@ where
impl<T> FromBytes for Vec<T> impl<T> FromBytes for Vec<T>
where where
T: Sized + Copy + Byteable, T: Byteable,
{ {
fn from_bytes(bytes: &[u8]) -> Self { fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!( assert_eq!(

View File

@ -168,7 +168,7 @@ impl System for FixedTimestep {
self.internal_system.is_send() self.internal_system.is_send()
} }
unsafe fn run_unsafe(&mut self, _input: Self::In, world: &World) -> Self::Out { unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun {
// SAFE: this system inherits the internal system's component access and archetype component // SAFE: this system inherits the internal system's component access and archetype component
// access, which means the caller has ensured running the internal system is safe // access, which means the caller has ensured running the internal system is safe
self.internal_system.run_unsafe((), world) self.internal_system.run_unsafe((), world)

View File

@ -8,9 +8,7 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
TokenStream::from(quote! { TokenStream::from(quote! {
#[no_mangle] #[no_mangle]
pub extern "C" fn _create_plugin() -> *mut bevy::app::Plugin { pub extern "C" fn _bevy_create_plugin() -> *mut bevy::app::Plugin {
// TODO: without this the assembly does nothing. why is that the case?
print!("");
// make sure the constructor is the correct type. // make sure the constructor is the correct type.
let object = #struct_name {}; let object = #struct_name {};
let boxed = Box::new(object); let boxed = Box::new(object);

View File

@ -2,23 +2,32 @@ use libloading::{Library, Symbol};
use bevy_app::{AppBuilder, CreatePlugin, Plugin}; use bevy_app::{AppBuilder, CreatePlugin, Plugin};
/// Dynamically links a plugin a the given path. The plugin must export the [CreatePlugin] function. /// Dynamically links a plugin a the given path. The plugin must export a function with the
pub fn dynamically_load_plugin(path: &str) -> (Library, Box<dyn Plugin>) { /// [`CreatePlugin`] signature named `_bevy_create_plugin`.
unsafe { ///
/// # Safety
///
/// The specified plugin must be linked against the exact same libbevy.so as this program.
/// In addition the `_bevy_create_plugin` symbol must not be manually created, but instead created
/// by deriving `DynamicPlugin` on a unit struct implementing [`Plugin`].
pub unsafe fn dynamically_load_plugin(path: &str) -> (Library, Box<dyn Plugin>) {
let lib = Library::new(path).unwrap(); let lib = Library::new(path).unwrap();
let func: Symbol<CreatePlugin> = lib.get(b"_create_plugin").unwrap(); let func: Symbol<CreatePlugin> = lib.get(b"_bevy_create_plugin").unwrap();
let plugin = Box::from_raw(func()); let plugin = Box::from_raw(func());
(lib, plugin) (lib, plugin)
} }
}
pub trait DynamicPluginExt { pub trait DynamicPluginExt {
fn load_plugin(&mut self, path: &str) -> &mut Self; /// # Safety
///
/// Same as [`dynamically_load_plugin`].
unsafe fn load_plugin(&mut self, path: &str) -> &mut Self;
} }
impl DynamicPluginExt for AppBuilder { impl DynamicPluginExt for AppBuilder {
fn load_plugin(&mut self, path: &str) -> &mut Self { unsafe fn load_plugin(&mut self, path: &str) -> &mut Self {
let (_lib, plugin) = dynamically_load_plugin(path); let (lib, plugin) = dynamically_load_plugin(path);
std::mem::forget(lib); // Ensure that the library is not automatically unloaded
plugin.build(self); plugin.build(self);
self self
} }

View File

@ -404,7 +404,7 @@ impl System for RunOnce {
true true
} }
unsafe fn run_unsafe(&mut self, _input: Self::In, _world: &World) -> Self::Out { unsafe fn run_unsafe(&mut self, (): (), _world: &World) -> ShouldRun {
if self.ran { if self.ran {
ShouldRun::No ShouldRun::No
} else { } else {

View File

@ -355,7 +355,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_graph_edges() { fn test_graph_edges() {
let mut graph = RenderGraph::default(); let mut graph = RenderGraph::default();
let a_id = graph.add_node("A", TestNode::new(0, 1)); let a_id = graph.add_node("A", TestNode::new(0, 1));
let b_id = graph.add_node("B", TestNode::new(0, 1)); let b_id = graph.add_node("B", TestNode::new(0, 1));
@ -411,7 +411,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_get_node_typed() { fn test_get_node_typed() {
struct MyNode { struct MyNode {
value: usize, value: usize,
} }
@ -443,7 +443,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_slot_already_occupied() { fn test_slot_already_occupied() {
let mut graph = RenderGraph::default(); let mut graph = RenderGraph::default();
graph.add_node("A", TestNode::new(0, 1)); graph.add_node("A", TestNode::new(0, 1));
@ -463,7 +463,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_edge_already_exists() { fn test_edge_already_exists() {
let mut graph = RenderGraph::default(); let mut graph = RenderGraph::default();
graph.add_node("A", TestNode::new(0, 1)); graph.add_node("A", TestNode::new(0, 1));

View File

@ -82,8 +82,12 @@ impl CountdownEvent {
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test] #[test]
pub fn countdown_event_ready_after() { fn countdown_event_ready_after() {
let countdown_event = CountdownEvent::new(2); let countdown_event = CountdownEvent::new(2);
countdown_event.decrement(); countdown_event.decrement();
countdown_event.decrement(); countdown_event.decrement();
@ -91,12 +95,13 @@ pub fn countdown_event_ready_after() {
} }
#[test] #[test]
pub fn countdown_event_ready() { fn countdown_event_ready() {
let countdown_event = CountdownEvent::new(2); let countdown_event = CountdownEvent::new(2);
countdown_event.decrement(); countdown_event.decrement();
let countdown_event_clone = countdown_event.clone(); let countdown_event_clone = countdown_event.clone();
let handle = let handle = std::thread::spawn(move || {
std::thread::spawn(move || futures_lite::future::block_on(countdown_event_clone.listen())); futures_lite::future::block_on(countdown_event_clone.listen())
});
// Pause to give the new thread time to start blocking (ugly hack) // Pause to give the new thread time to start blocking (ugly hack)
std::thread::sleep(instant::Duration::from_millis(100)); std::thread::sleep(instant::Duration::from_millis(100));
@ -106,7 +111,7 @@ pub fn countdown_event_ready() {
} }
#[test] #[test]
pub fn event_resets_if_listeners_are_cleared() { fn event_resets_if_listeners_are_cleared() {
let event = Event::new(); let event = Event::new();
// notify all listeners // notify all listeners
@ -129,3 +134,4 @@ pub fn event_resets_if_listeners_are_cleared() {
event.notify(std::usize::MAX); event.notify(std::usize::MAX);
futures_lite::future::block_on(listener3); futures_lite::future::block_on(listener3);
} }
}

View File

@ -279,7 +279,7 @@ mod tests {
}; };
#[test] #[test]
pub fn test_spawn() { fn test_spawn() {
let pool = TaskPool::new(); let pool = TaskPool::new();
let foo = Box::new(42); let foo = Box::new(42);
@ -310,7 +310,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_mixed_spawn_local_and_spawn() { fn test_mixed_spawn_local_and_spawn() {
let pool = TaskPool::new(); let pool = TaskPool::new();
let foo = Box::new(42); let foo = Box::new(42);
@ -355,7 +355,7 @@ mod tests {
} }
#[test] #[test]
pub fn test_thread_locality() { fn test_thread_locality() {
let pool = Arc::new(TaskPool::new()); let pool = Arc::new(TaskPool::new());
let count = Arc::new(AtomicI32::new(0)); let count = Arc::new(AtomicI32::new(0));
let barrier = Arc::new(Barrier::new(101)); let barrier = Arc::new(Barrier::new(101));

View File

@ -21,6 +21,18 @@ pub struct FlexSurface {
stretch: Stretch, stretch: Stretch,
} }
// SAFE: as long as MeasureFunc is Send + Sync. https://github.com/vislyhq/stretch/issues/69
unsafe impl Send for FlexSurface {}
unsafe impl Sync for FlexSurface {}
fn _assert_send_sync_flex_surface_impl_safe() {
fn _assert_send_sync<T: Send + Sync>() {}
_assert_send_sync::<HashMap<Entity, stretch::node::Node>>();
_assert_send_sync::<HashMap<WindowId, stretch::node::Node>>();
// FIXME https://github.com/vislyhq/stretch/issues/69
// _assert_send_sync::<Stretch>();
}
impl fmt::Debug for FlexSurface { impl fmt::Debug for FlexSurface {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FlexSurface") f.debug_struct("FlexSurface")
@ -183,10 +195,6 @@ pub enum FlexError {
StretchError(stretch::Error), StretchError(stretch::Error),
} }
// SAFE: as long as MeasureFunc is Send + Sync. https://github.com/vislyhq/stretch/issues/69
unsafe impl Send for FlexSurface {}
unsafe impl Sync for FlexSurface {}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn flex_node_system( pub fn flex_node_system(
windows: Res<Windows>, windows: Res<Windows>,

View File

@ -99,6 +99,7 @@ impl WgpuRenderer {
{ {
let winit_windows = world.get_resource::<bevy_winit::WinitWindows>().unwrap(); let winit_windows = world.get_resource::<bevy_winit::WinitWindows>().unwrap();
let winit_window = winit_windows.get_window(window.id()).unwrap(); let winit_window = winit_windows.get_window(window.id()).unwrap();
// SAFE: The raw window handle created from a `winit::Window` is always valid.
let surface = unsafe { self.instance.create_surface(winit_window.deref()) }; let surface = unsafe { self.instance.create_surface(winit_window.deref()) };
render_resource_context.set_window_surface(window.id(), surface); render_resource_context.set_window_surface(window.id(), surface);
} }