Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Waking the current task leads to a deadlock #3

Open
piodul opened this issue Mar 12, 2023 · 1 comment
Open

Waking the current task leads to a deadlock #3

piodul opened this issue Mar 12, 2023 · 1 comment

Comments

@piodul
Copy link

piodul commented Mar 12, 2023

If a Rust future is co_awaited from C++ and that future decides to wake the current task, then a deadlock will occur.

Found while trying to use futures::FuturesUnordered which wakes the current task here.

I attach a minimal example below.

main.hh:

#pragma once

#include "rust/cxx.h"
#include "cxx-async/include/rust/cxx_async.h"

CXXASYNC_DEFINE_FUTURE(void, RustFutureVoid);

RustFutureVoid run_cpp();

main.cc:

#include "main.hh"
#include "cxx-async-self-wake-bug/src/main.rs.h"

RustFutureVoid run_cpp() {
    co_await run_rust();
}

main.rs:

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use crate::ffi::run_cpp;

#[cxx::bridge]
mod ffi {
    extern "Rust" {
        fn run_rust() -> RustFutureVoid;
    }
    unsafe extern "C++" {
        include!("main.hh");
        fn run_cpp() -> RustFutureVoid;
        type RustFutureVoid = crate::RustFutureVoid;
    }
}

#[cxx_async::bridge]
unsafe impl Future for RustFutureVoid {
    type Output = ();
}

pub fn run_rust() -> RustFutureVoid {
    RustFutureVoid::infallible(YieldFuture { yielded: false })
}

struct YieldFuture {
    yielded: bool,
}

impl Future for YieldFuture {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if !self.yielded {
            // Yield, but also wake immediately
            self.as_mut().yielded = true;
            cx.waker().wake_by_ref();
            Poll::Pending
        } else {
            Poll::Ready(())
        }
    }
}

fn main() {
    println!("Start!");
    futures::executor::block_on(run_cpp()).unwrap();
    println!("Finish!");
}

Only "Start!" is printed, then the program deadlocks.

@piodul
Copy link
Author

piodul commented Mar 12, 2023

This is the backtrace from the moment of the deadlock, according to gdb:

#0  futex_wait (private=0, expected=2, futex_word=0x5555555f9940) at ../sysdeps/nptl/futex-internal.h:146
#1  __GI___lll_lock_wait (futex=futex@entry=0x5555555f9940, private=0) at lowlevellock.c:49
#2  0x00007ffff7ab1412 in lll_mutex_lock_optimized (mutex=0x5555555f9940) at pthread_mutex_lock.c:48
#3  ___pthread_mutex_lock (mutex=0x5555555f9940) at pthread_mutex_lock.c:93
#4  0x00005555555869ca in __gthread_mutex_lock (__mutex=0x5555555f9940) at /usr/include/c++/12/x86_64-redhat-linux/bits/gthr-default.h:749
#5  0x0000555555586cce in std::mutex::lock (this=0x5555555f9940) at /usr/include/c++/12/bits/std_mutex.h:100
#6  0x0000555555588ce6 in std::lock_guard<std::mutex>::lock_guard (this=0x7fffffffc9b8, __m=...) at /usr/include/c++/12/bits/std_mutex.h:229
#7  0x0000555555588620 in rust::async::RustFutureReceiver<RustFutureVoid>::wake (this=0x5555555f9940, coroutine=0x5555555f99a0)
    at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:852
#8  0x0000555555587d9a in rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}::operator()(rust::async::SuspendedCoroutine*) const (__closure=0x5555555f99e0, coroutine=0x5555555f99a0)
    at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:879
#9  0x00005555555895c2 in std::__invoke_impl<rust::async::FutureWakeStatus, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*>(std::__invoke_other, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*&&) (__f=...) at /usr/include/c++/12/bits/invoke.h:61
#10 0x00005555555891cf in std::__invoke_r<rust::async::FutureWakeStatus, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*>(rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*&&) (__fn=...) at /usr/include/c++/12/bits/invoke.h:114
#11 0x0000555555588e29 in std::_Function_handler<rust::async::FutureWakeStatus (rust::async::SuspendedCoroutine*), rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}>::_M_invoke(std::_Any_data const&, rust::async::SuspendedCoroutine*&&) (__functor=..., 
    __args#0=@0x7fffffffcab0: 0x5555555f99a0) at /usr/include/c++/12/bits/std_function.h:290
#12 0x0000555555587c19 in std::function<rust::async::FutureWakeStatus (rust::async::SuspendedCoroutine*)>::operator()(rust::async::SuspendedCoroutine*) const (
    this=0x5555555f99b0, __args#0=0x5555555f99a0) at /usr/include/c++/12/bits/std_function.h:591
#13 0x000055555558728f in rust::async::SuspendedCoroutine::wake (this=0x5555555f99a0)
    at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:688
#14 0x0000555555594bb9 in cxxasync_suspended_coroutine_wake_by_ref (ptr=0x5555555f99a0 "\003") at src/cxx_async.cpp:47
#15 0x000055555559451b in cxx_async::rust_suspended_coroutine_wake_by_ref (address=0x5555555f99a0) at src/lib.rs:892
#16 0x00005555555814c2 in core::task::wake::Waker::wake_by_ref (self=0x7fffffffcda8) at /builddir/build/BUILD/rustc-1.67.1-src/library/core/src/task/wake.rs:291
#17 0x00005555555825b3 in cxx_async_self_wake_bug::{impl#0}::poll (self=..., cx=0x7fffffffcd18) at src/main.rs:40
#18 0x0000555555584c23 in cxx_async::IntoCxxAsyncFuture::infallible::{async_block#0}<cxx_async_self_wake_bug::RustFutureVoid, cxx_async_self_wake_bug::YieldFuture> ()
    at /home/piodul/.cargo/git/checkouts/cxx-async-30622540fc81757a/c8f4b77/cxx-async/src/lib.rs:610
#19 0x00005555555832b7 in core::future::future::{impl#1}::poll<alloc::boxed::Box<(dyn core::future::future::Future<Output=core::result::Result<(), cxx_async::CxxAsyncException>> + core::marker::Send), alloc::alloc::Global>> (self=..., cx=0x7fffffffcd18) at /builddir/build/BUILD/rustc-1.67.1-src/library/core/src/future/future.rs:124
#20 0x00005555555826a1 in cxx_async_self_wake_bug::{impl#4}::poll (self=..., cx=0x7fffffffcd18) at src/main.rs:20
#21 0x00005555555846bb in cxx_async::future_poll::{closure#0}<cxx_async_self_wake_bug::RustFutureVoid, ()> ()
    at /home/piodul/.cargo/git/checkouts/cxx-async-30622540fc81757a/c8f4b77/cxx-async/src/lib.rs:835
#22 0x00005555555815f1 in core::panic::unwind_safe::{impl#23}::call_once<u32, cxx_async::future_poll::{closure_env#0}<cxx_async_self_wake_bug::RustFutureVoid, ()>> (
    self=..., _args=()) at /builddir/build/BUILD/rustc-1.67.1-src/library/core/src/panic/unwind_safe.rs:271
#23 0x0000555555583061 in std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<cxx_async::future_poll::{closure_env#0}<cxx_async_self_wake_bug::RustFutureVoid, ()>>, u32> (data=0x7fffffffcef0) at /builddir/build/BUILD/rustc-1.67.1-src/library/std/src/panicking.rs:483
#24 0x000055555558310b in __rust_try ()
#25 0x0000555555582ecc in std::panicking::try<u32, core::panic::unwind_safe::AssertUnwindSafe<cxx_async::future_poll::{closure_env#0}<cxx_async_self_wake_bug::RustFutureVoid, ()>>> (f=...) at /builddir/build/BUILD/rustc-1.67.1-src/library/std/src/panicking.rs:447
#26 0x0000555555582dd8 in std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<cxx_async::future_poll::{closure_env#0}<cxx_async_self_wake_bug::RustFutureVoid, ()>>, u32> (f=...) at /builddir/build/BUILD/rustc-1.67.1-src/library/std/src/panic.rs:137
#27 0x00005555555844fe in cxx_async::future_poll<cxx_async_self_wake_bug::RustFutureVoid, ()> (this=..., result=0x5555555f9978, waker_data=0x5555555f99a0) at /home/piodul/.cargo/git/checkouts/cxx-async-30622540fc81757a/c8f4b77/cxx-async/src/lib.rs:833
#28 0x0000555555588660 in rust::async::RustFutureReceiver<RustFutureVoid>::wake (this=0x5555555f9940, coroutine=0x5555555f99a0) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:861
#29 0x0000555555587d9a in rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}::operator()(rust::async::SuspendedCoroutine*) const (__closure=0x5555555f99e0, coroutine=0x5555555f99a0) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:879
#30 0x00005555555895c2 in std::__invoke_impl<rust::async::FutureWakeStatus, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*>(std::__invoke_other, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*&&) (__f=...) at /usr/include/c++/12/bits/invoke.h:61
#31 0x00005555555891cf in std::__invoke_r<rust::async::FutureWakeStatus, rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*>(rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}&, rust::async::SuspendedCoroutine*&&) (__fn=...) at /usr/include/c++/12/bits/invoke.h:114
#32 0x0000555555588e29 in std::_Function_handler<rust::async::FutureWakeStatus (rust::async::SuspendedCoroutine*), rust::async::RustAwaiter<RustFutureVoid>::await_suspend(std::__n4861::coroutine_handle<void>)::{lambda(rust::async::SuspendedCoroutine*)#1}>::_M_invoke(std::_Any_data const&, rust::async::SuspendedCoroutine*&&) (__functor=..., __args#0=@0x7fffffffd210: 0x5555555f99a0) at /usr/include/c++/12/bits/std_function.h:290
#33 0x0000555555587c19 in std::function<rust::async::FutureWakeStatus (rust::async::SuspendedCoroutine*)>::operator()(rust::async::SuspendedCoroutine*) const (this=0x5555555f99b0, __args#0=0x5555555f99a0) at /usr/include/c++/12/bits/std_function.h:591
#34 0x000055555558728f in rust::async::SuspendedCoroutine::wake (this=0x5555555f99a0) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:688
#35 0x00005555555872aa in rust::async::SuspendedCoroutine::initial_suspend (this=0x5555555f99a0) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:696
#36 0x0000555555587f0c in rust::async::RustAwaiter<RustFutureVoid>::await_suspend (this=0x5555555f97f0, next=...) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-391fb75ee5f1fc50/out/cxxbridge/crate/cxx-async/include/rust/cxx_async.h:881
#37 0x00005555555876e6 in run_cpp(_Z7run_cppv.Frame *) (frame_ptr=0x5555555f97b0) at src/main.cc:5
#38 0x00005555555874dd in run_cpp () at src/main.cc:4
#39 0x00005555555867bf in cxxbridge1$run_cpp (return$=0x7fffffffd3e0) at /home/piodul/code/cxx-async-self-wake-bug/target/debug/build/cxx-async-self-wake-bug-578275989b282d27/out/cxxbridge/sources/cxx-async-self-wake-bug/src/main.rs.cc:79
#40 0x00005555555822b8 in cxx_async_self_wake_bug::ffi::run_cpp () at src/main.rs:15
#41 0x0000555555582606 in cxx_async_self_wake_bug::main () at src/main.rs:50

It looks like rust::async::RustFutureReceiver<RustFutureVoid>::wake is called recursively and it tries to acquire the same lock again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant