The Wayback Machine - https://web.archive.org/web/20201003041524/https://github.com/amethyst/specs
Skip to content
master
Go to file
Code

Latest commit

707: Allow reentrant laziness r=WaDelma a=WaDelma

<!-- Before creating a PR, please make sure you read the contribution guidelines. -->
<!-- Feel free to delete points if they don't make sense for this PR. -->
<!-- You can tick boxes by putting an "x" inside the braces, or by clicking them once the comment is published. -->

<!-- Please use "Fixes #nr" and "Related #nr", respectively. -->

## Checklist

* [x] I've added tests for all code changes and additions (where applicable)
* [ ] I've added a demonstration of the new feature to one or more examples
* [ ] I've updated the book to reflect my changes
* [ ] Usage of new public items is shown in the API docs

## API changes

None
<!-- Please make it clear if your change is breaking. -->

## Description
Before this change if one tried to do `World::system_data<Read<LazyUpdate>>()` within `LazyUpdate::exec` on would get the following error:
```
thread 'main' panicked at 'Already borrowed mutably: InvalidBorrow', /home/delma/.cargo/registry/src/github.com-1ecc6299db9ec823/shred-0.7.2/src/cell.rs:103:9
stack backtrace:                                                                                                                                              
   0: backtrace::backtrace::libunwind::trace                                                             
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized                                                         
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/mod.rs:66 
   2: std::sys_common::backtrace::_print_fmt                                                             
             at src/libstd/sys_common/backtrace.rs:78                                                    
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt                  
             at src/libstd/sys_common/backtrace.rs:59                                                    
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1069
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1504
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:198
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:218
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:511
  11: rust_begin_unwind
             at src/libstd/panicking.rs:419
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:111
  13: core::option::expect_none_failed
             at src/libcore/option.rs:1268
  14: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
  15: specs::world::World::maintain
```
This problem arised in our project when I refactored code such that piece of code so that previously wasn't used inside `LazyUpdate::exec` was now used there. The piece of code is also used outside of `LazyUpdate` so just removing `LazyUpdate` usage in it is non-trivial. 

This PR changes the behavior to allow reentrant `LazyUpdate` usage. After the change reentrant `LazyUpdate::exec`:s will be applied in the order they are created on the same call of `World::maintain`.  Another option was to execute them on the next call to maintain. This option wasn't chosen to make applying lazy updates as soon as possible as `LazyUpdate` is not meant for delaying, but for giving more flexibility.

I think allowing this kind of reentrancy makes using `LazyUpdate` more robust.

Co-authored-by: Delma <delma@del.ma>
6cba11b

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Specs

Specs Parallel ECS

Build Status Crates.io Gitter MIT/Apache Docs.rs Code coverage LoC

Specs is an Entity-Component System written in Rust. Unlike most other ECS libraries out there, it provides

  • easy parallelism
  • high flexibility
    • contains 5 different storages for components, which can be extended by the user
    • its types are mostly not coupled, so you can easily write some part yourself and still use Specs
    • Systems may read from and write to components and resources, can depend on each other and you can use barriers to force several stages in system execution
  • high performance for real-world applications

Minimum Rust version: 1.40

Link to the book

Example

use specs::prelude::*;

// A component contains data
// which is associated with an entity.
#[derive(Debug)]
struct Vel(f32);

impl Component for Vel {
    type Storage = VecStorage<Self>;
}

#[derive(Debug)]
struct Pos(f32);

impl Component for Pos {
    type Storage = VecStorage<Self>;
}

struct SysA;

impl<'a> System<'a> for SysA {
    // These are the resources required for execution.
    // You can also define a struct and `#[derive(SystemData)]`,
    // see the `full` example.
    type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);

    fn run(&mut self, (mut pos, vel): Self::SystemData) {
        // The `.join()` combines multiple component storages,
        // so we get access to all entities which have
        // both a position and a velocity.
        for (pos, vel) in (&mut pos, &vel).join() {
            pos.0 += vel.0;
        }
    }
}

fn main() {
    // The `World` is our
    // container for components
    // and other resources.
    let mut world = World::new();
    world.register::<Pos>();
    world.register::<Vel>();

    // An entity may or may not contain some component.

    world.create_entity().with(Vel(2.0)).with(Pos(0.0)).build();
    world.create_entity().with(Vel(4.0)).with(Pos(1.6)).build();
    world.create_entity().with(Vel(1.5)).with(Pos(5.4)).build();

    // This entity does not have `Vel`, so it won't be dispatched.
    world.create_entity().with(Pos(2.0)).build();

    // This builds a dispatcher.
    // The third parameter of `with` specifies
    // logical dependencies on other systems.
    // Since we only have one, we don't depend on anything.
    // See the `full` example for dependencies.
    let mut dispatcher = DispatcherBuilder::new().with(SysA, "sys_a", &[]).build();
    // This will call the `setup` function of every system.
    // In this example this has no effect since we already registered our components.
    dispatcher.setup(&mut world);

    // This dispatches all the systems in parallel (but blocking).
    dispatcher.dispatch(&mut world);
}

Please look into the examples directory for more.

Public dependencies

crate version
hibitset hibitset
rayon rayon
shred shred
shrev shrev

Contribution

Contribution is very welcome! If you didn't contribute before, just filter for issues with "easy" or "good first issue" label. Please note that your contributions are assumed to be dual-licensed under Apache-2.0/MIT.

You can’t perform that action at this time.