Type "held" across await point - possible bug?

I have this async fn here

async fn start_inner(world: BorrowedArc<World>) -> Query<'a, T, F> {
    let mut entity = Entity::from_raw_parts(EntityId::from_inner(0), world.borrow());
    unsafe { world.entities.get_semaphore().take(1).await };
    unsafe { world.caches.get_semaphore().take(1).await };
    let inner_lock = unsafe { world.caches.get_unchecked() }
        .get(&Self::type_id())
        .unwrap();
    unsafe { inner_lock.get_semaphore().take(1).await };
    for &id in unsafe { inner_lock.get_unchecked() }.iter() {
        entity.set_id(id);
        unsafe {
            if F::matches(&entity) {
                T::lock(&entity).await;
            }
        }
    }
    let inner = unsafe { inner_lock.get_unchecked() }.iter();
    Self(entity, inner, PhantomData)
}

and I require it to be Send + Sync. Sync it understandably has down fine, but there is an error with Send:

error: future cannot be sent between threads safely
   --> src/query.rs:232:10
    |
232 |     ) -> impl Future<Output = Query<'a, T, F>> + Send + Sync + 'a {
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `start_inner` is not `Send`
    |
    = help: the trait `std::marker::Send` is not implemented for `(dyn std::any::Any + std::marker::Sync + 'static)`
    = note: consider using `std::sync::Arc<(dyn std::any::Any + std::marker::Sync + 'static)>`; for more information visit <https://doc.rust-lang.org/book/ch16-03-shared-state.html>
note: future is not `Send` as this value is used across an await
   --> src/query.rs:238:57
    |
238 |         unsafe { world.entities.get_semaphore().take(1).await };
    |                  -----                                  ^^^^^  - `world` is later dropped here
    |                  |                                      |
    |                  |                                      await occurs here, with `world` maybe used later
    |                  has type `world::World` which is not `Send`

Now, this is cut-and-dried, right? I tried to use a world, which is not Send, across multiple await points. However, there's a type in between: BorrowedArc. BorrowedArc is a type of my own design, similar to an Arc but unconditionally Send (well, Send based on Sync) because it cannot be dropped before the main OwnedArc. Yet I still get this error. Why is it counted as held if it is never owned, just behind a smart pointer?

What is the signature of get_semaphore?

pub unsafe fn get_semaphore(&self) -> &Semaphore

Maybe world.entities derefs to World, and that creates some implicit temporary held across the next await?

Rust has trouble tracking lifetimes of variables across await points:

And the fix for it is to make explicit blocks around anything that may be non-send. So try spamming the code with more {} between uses of World and awaits.

unsafe { { world.entities.get_semaphore() }.take(1).await };

I found a solution related to that

unsafe { async { world.entities.get_semaphore().take(1).await }.await };

it's only slightly cursed. Should I make an issue on the repo, or add to that one?

Never mind, this actually just moves the error to the block. Neither of those solutions worked.

Have you tried it with regular block, not async?

Yeah, I spammed them all over, no luck.

Another thing you can do is to wrap expressions in this:

fn is_send<T: Send>(t: T) -> T { t }

To narrow down what exactly is non-Send there.

This all compiles

is_send(&world.entities);
is_send(unsafe { world.entities.get_semaphore() });
is_send(unsafe { world.entities.get_semaphore().take(1) });

It is likely the lifetime that stops Send+Sync.
Does it pass compiler if you comment out return value?

Nope.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.