Repository: bevyengine/bevy
File: crates/bevy_ecs/src/message/message_reader.rs (Rust)

Code before:
        // SAFETY: requirements are upheld by MessageReader's implementation
        unsafe {
            PopulatedMessageReader::<'world, 'state, M>(MessageReader::<M>::get_param(
                state,
                system_meta,
                world,
                change_tick,
            ))
        }
    }

    unsafe fn validate_param(
        state: &mut Self::State,
        system_meta: &crate::system::SystemMeta,
        world: crate::world::unsafe_world_cell::UnsafeWorldCell,
    ) -> Result<(), SystemParamValidationError> {
        // SAFETY: requirements are upheld by MessageReader's implementation
        unsafe { MessageReader::<M>::validate_param(state, system_meta, world) }?;

        let cursor = state.state.0.get();

        // SAFETY: this access is registered in MessageReader's `init_access`
        let messages = unsafe { world.get_resource::<Messages<M>>() }
            .expect("missing message queue just after system param was validated");

        if cursor.is_empty(messages) {
            Err(SystemParamValidationError::skipped::<Self>(
                "message queue is empty",
            ))
        } else {
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use core::sync::atomic::{AtomicBool, Ordering};

    use super::*;
    use crate::message::MessageRegistry;
    use crate::prelude::*;
    use bevy_platform::sync::Arc;

    #[test]
    fn test_populated_message_reader() {
        let system_ran = Arc::new(AtomicBool::new(false));

        let mut world = World::new();
        MessageRegistry::register_message::<TheMessage>(&mut world);


Review comment (performance):
`world.get_resource::<Messages<M>>()` will have to look up the `ComponentId` for `Messages<M>` on every call.  It would be a little more efficient to store it in the `State`, then use the untyped `get_resource_by_id` and `cast` the resulting `Ptr`.  I think `MessageReader` is already storing that, so if you're able to use `state.0` to get the cursor then maybe you can use `state.1` to get the `ComponentId`.  

Or, hmm, it might be even simpler to just create the `MessageReader` here.  Could you do something like

```suggestion
        let reader = unsafe { MessageReader::<M>::get_param(state, system_meta, world, world.change_tick()) };
        if reader.is_empty() {
```

?

Code after:
        // SAFETY: requirements are upheld by MessageReader's implementation
        unsafe {
            PopulatedMessageReader(MessageReader::get_param(
                state,
                system_meta,
                world,
                change_tick,
            ))
        }
    }

    unsafe fn validate_param(
        state: &mut Self::State,
        system_meta: &crate::system::SystemMeta,
        world: crate::world::unsafe_world_cell::UnsafeWorldCell,
    ) -> Result<(), SystemParamValidationError> {
        // SAFETY: requirements are upheld by MessageReader's implementation
        unsafe { MessageReader::<M>::validate_param(state, system_meta, world) }?;

        // SAFETY: requirements are upheld by MessageReader's implementation
        let reader =
            unsafe { MessageReader::get_param(state, system_meta, world, world.change_tick()) };
        if reader.is_empty() {
            Err(SystemParamValidationError::skipped::<Self>(
                "message queue is empty",
            ))
        } else {
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use core::sync::atomic::{AtomicBool, Ordering};

    use super::*;
    use crate::message::MessageRegistry;
    use crate::prelude::*;
    use bevy_platform::sync::Arc;

    #[test]
    fn test_populated_message_reader() {
        let system_ran = Arc::new(AtomicBool::new(false));

        let mut world = World::new();
        MessageRegistry::register_message::<TheMessage>(&mut world);

        let mut schedule = Schedule::default();
        schedule.add_systems({
            let system_ran = system_ran.clone();