use std::future::Future;
use std::mem::replace;
use std::os::raw::c_void;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use cpp::cpp;
use crate::connections::SignalArgArrayToTuple;
static QT_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
|s: *const ()| {
RawWaker::new(
cpp!(unsafe [s as "Waker *"] -> *const() as "Waker *" {
s->refs++;
return s;
}),
&QT_WAKER_VTABLE,
)
},
|s: *const ()| {
cpp!(unsafe [s as "Waker *"] {
s->wake();
s->deref();
})
},
|s: *const ()| {
cpp!(unsafe [s as "Waker *"] {
s->wake();
})
},
|s: *const ()| {
cpp!(unsafe [s as "Waker *"] {
s->deref();
})
},
);
cpp! {{
#include <QtCore/QCoreApplication>
struct Waker : QObject {
TraitObject future;
bool woken = false;
bool completed = false;
QAtomicInt refs = 0;
Waker(TraitObject f): future(f), refs(1) {}
void customEvent(QEvent *e) override {
Q_UNUSED(e);
woken = false;
if (completed) {
return;
}
completed = rust!(ProcessQtEvent [
this: *const () as "Waker *",
future: *mut dyn Future<Output = ()> as "TraitObject"
] -> bool as "bool" {
poll_with_qt_waker(this, Pin::new_unchecked(&mut *future))
});
if (completed) {
deref();
}
}
void deref() {
if (!--refs) {
deleteLater();
}
}
void wake() {
if (woken) {
return;
}
woken = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
}
~Waker() {
rust!(QtDestroyFuture [future: *mut dyn Future<Output = ()> as "TraitObject"] {
drop(Box::from_raw(future));
});
}
};
}}
pub fn execute_async(f: impl Future<Output = ()> + 'static) {
let f: *mut dyn Future<Output = ()> = Box::into_raw(Box::new(f));
unsafe {
let waker = cpp!([f as "TraitObject"] -> *const() as "Waker *" {
return new Waker(f);
});
poll_with_qt_waker(waker, Pin::new_unchecked(&mut *f));
}
}
unsafe fn poll_with_qt_waker(waker: *const (), future: Pin<&mut dyn Future<Output = ()>>) -> bool {
cpp!([waker as "Waker *"] { waker->refs++; });
let waker = RawWaker::new(waker, &QT_WAKER_VTABLE);
let waker = Waker::from_raw(waker);
let mut context = Context::from_waker(&waker);
future.poll(&mut context).is_ready()
}
pub unsafe fn wait_on_signal<Args: SignalArgArrayToTuple>(
sender: *const c_void,
signal: crate::connections::Signal<Args>,
) -> impl Future<Output = <Args as SignalArgArrayToTuple>::Tuple> {
enum ConnectionFutureState<Args: SignalArgArrayToTuple> {
Init { sender: *const c_void, signal: crate::connections::Signal<Args> },
Started { handle: crate::connections::ConnectionHandle, waker: Waker },
Finished { result: <Args as SignalArgArrayToTuple>::Tuple },
Invalid,
}
impl<Args: SignalArgArrayToTuple> std::marker::Unpin for ConnectionFutureState<Args> {}
struct ConnectionFuture<Args: SignalArgArrayToTuple>(ConnectionFutureState<Args>);
impl<Args: SignalArgArrayToTuple> Drop for ConnectionFuture<Args> {
fn drop(&mut self) {
if let ConnectionFutureState::Started { ref mut handle, .. } = &mut self.0 {
handle.disconnect();
}
}
}
impl<Args: SignalArgArrayToTuple> Future for ConnectionFuture<Args> {
type Output = <Args as SignalArgArrayToTuple>::Tuple;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
let state = &mut self.0;
*state = match replace(state, ConnectionFutureState::Invalid) {
ConnectionFutureState::Finished { result } => {
return Poll::Ready(result);
}
ConnectionFutureState::Init { sender, signal } => {
let s_ptr = state as *mut ConnectionFutureState<_>;
let handle = unsafe { crate::connections::connect(sender, signal, s_ptr) };
debug_assert!(handle.is_valid());
ConnectionFutureState::Started { handle, waker: ctx.waker().clone() }
}
s @ ConnectionFutureState::Started { .. } => s,
ConnectionFutureState::Invalid => unreachable!(),
};
Poll::Pending
}
}
impl<Args: SignalArgArrayToTuple> crate::connections::Slot<Args>
for *mut ConnectionFutureState<Args>
{
unsafe fn apply(&mut self, a: *const *const c_void) {
if let ConnectionFutureState::Started { mut handle, waker } = replace(
&mut **self,
ConnectionFutureState::Finished { result: Args::args_array_to_tuple(a) },
) {
handle.disconnect();
waker.wake();
} else {
unreachable!();
}
}
}
ConnectionFuture(ConnectionFutureState::Init { sender, signal })
}