Skip to content

Commit b246933

Browse files
authored
update async futures support to use ToKjException as well (#61)
1 parent 0fc4f54 commit b246933

File tree

7 files changed

+56
-26
lines changed

7 files changed

+56
-26
lines changed

kj-rs/future.h

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@ class FuturePoller {
4040
switch (pollFunc(&result)) {
4141
case ::kj_rs::FuturePollStatus::Pending:
4242
return;
43-
case ::kj_rs::FuturePollStatus::Complete:
44-
output.value = toResult();
43+
case ::kj_rs::FuturePollStatus::Complete: {
44+
output.value = kj::mv(result);
45+
kj::dtor(result);
4546
return;
47+
}
4648
case ::kj_rs::FuturePollStatus::Error: {
47-
output.addException(toException());
49+
output.addException(kj::mv(*error));
50+
delete error;
4851
return;
4952
}
5053
}
@@ -53,22 +56,9 @@ class FuturePoller {
5356
}
5457

5558
private:
56-
T toResult() {
57-
auto ret = kj::mv(result);
58-
kj::dtor(result);
59-
return ret;
60-
}
61-
62-
kj::Exception toException() {
63-
auto description = ::kj::ArrayPtr<const char>(error.data(), error.size());
64-
auto exception = KJ_EXCEPTION(FAILED, kj::str(description));
65-
kj::dtor(error);
66-
return exception;
67-
}
68-
6959
union {
7060
T result;
71-
::rust::String error;
61+
kj::Exception* error;
7262
};
7363
};
7464

kj-rs/future.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ pub(crate) mod repr {
3838

3939
type DropCallback = unsafe extern "C" fn(fut: *mut c_void);
4040

41-
type FuturePtr<'a, T> = *mut (dyn Future<Output = Result<T, String>> + 'a);
41+
type FuturePtr<'a, T> = *mut (dyn Future<Output = Result<T, cxx::KjException>> + 'a);
4242

43-
/// Represents a `dyn Future<Output = Result<T, String>>`.
43+
/// Represents a `dyn Future<Output = Result<T, cxx::KjException>>`.
4444
#[repr(C)]
4545
pub struct RustFuture<'a, T> {
4646
pub fut: FuturePtr<'a, T>,
@@ -82,7 +82,12 @@ pub(crate) mod repr {
8282
FuturePollStatus::Complete
8383
}
8484
Poll::Ready(Err(error)) => {
85-
unsafe { std::ptr::write(ret.cast::<String>(), error.to_string()) };
85+
unsafe {
86+
std::ptr::write(
87+
ret.cast::<*mut c_void>(),
88+
error.into_raw().as_ptr().cast(),
89+
);
90+
};
8691
FuturePollStatus::Error
8792
}
8893
Poll::Pending => FuturePollStatus::Pending,
@@ -127,7 +132,7 @@ pub(crate) mod repr {
127132

128133
#[must_use]
129134
pub fn future<'a, T: Unpin>(
130-
fut: Pin<Box<dyn Future<Output = Result<T, String>> + 'a>>,
135+
fut: Pin<Box<dyn Future<Output = Result<T, cxx::KjException>> + 'a>>,
131136
) -> RustFuture<'a, T> {
132137
let fut = Box::into_raw(unsafe { Pin::into_inner_unchecked(fut) });
133138
let poll = RustFuture::<T>::poll;

kj-rs/tests/awaitables-cc-test.c++

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,23 @@ KJ_TEST("co_awaiting a fallible future from C++ can throw") {
118118
}().wait(waitScope);
119119
}
120120

121+
KJ_TEST("co_awaiting a KjError future from C++ can throw with proper exception type") {
122+
kj::EventLoop loop;
123+
kj::WaitScope waitScope(loop);
124+
125+
[]() -> kj::Promise<void> {
126+
kj::Maybe<kj::Exception> maybeException;
127+
try {
128+
co_await new_kj_errored_future_void();
129+
} catch (...) {
130+
maybeException = kj::getCaughtExceptionAsKj();
131+
}
132+
auto& exception = KJ_ASSERT_NONNULL(maybeException, "should have thrown");
133+
KJ_EXPECT(exception.getDescription() == "test error");
134+
KJ_EXPECT(exception.getType() == kj::Exception::Type::OVERLOADED);
135+
}().wait(waitScope);
136+
}
137+
121138
KJ_TEST(".awaiting a Promise<T> from Rust can produce an Err Result") {
122139
kj::EventLoop loop;
123140
kj::WaitScope waitScope(loop);

kj-rs/tests/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ mod test_refcount;
1717

1818
use test_futures::{
1919
new_awaiting_future_i32, new_error_handling_future_void_infallible, new_errored_future_void,
20-
new_layered_ready_future_void, new_naive_select_future_void, new_pending_future_void,
21-
new_ready_future_i32, new_ready_future_void, new_threaded_delay_future_void,
22-
new_waking_future_void, new_wrapped_waker_future_void,
20+
new_kj_errored_future_void, new_layered_ready_future_void, new_naive_select_future_void,
21+
new_pending_future_void, new_ready_future_i32, new_ready_future_void,
22+
new_threaded_delay_future_void, new_waking_future_void, new_wrapped_waker_future_void,
2323
};
2424

2525
use test_maybe::{
@@ -265,6 +265,9 @@ mod ffi {
265265
async fn new_wrapped_waker_future_void() -> Result<()>;
266266

267267
async fn new_errored_future_void() -> Result<()>;
268+
269+
async fn new_kj_errored_future_void() -> Result<()>;
270+
268271
async fn new_error_handling_future_void_infallible();
269272

270273
async fn new_awaiting_future_i32() -> Result<()>;

kj-rs/tests/test_futures.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ pub async fn new_errored_future_void() -> Result<()> {
210210
Err(std::io::Error::new(std::io::ErrorKind::Other, "test error"))
211211
}
212212

213+
pub async fn new_kj_errored_future_void() -> std::result::Result<(), cxx::KjError> {
214+
Err(cxx::KjError::new(
215+
cxx::KjExceptionType::Overloaded,
216+
"test error".to_string(),
217+
))
218+
}
219+
213220
pub async fn new_error_handling_future_void_infallible() {
214221
let err = crate::ffi::new_errored_promise_void()
215222
.await

macro/src/expand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ fn expand_rust_function_shim_super(
12771277
};
12781278

12791279
if fut.throws_tokens.is_some() {
1280-
quote!(-> std::pin::Pin<Box<dyn ::std::future::Future<Output = ::std::result::Result<#output, String>> #lifetimes>>)
1280+
quote!(-> std::pin::Pin<Box<dyn ::std::future::Future<Output = ::std::result::Result<#output, ::cxx::KjException>> #lifetimes>>)
12811281
} else {
12821282
quote!(-> std::pin::Pin<Box<dyn ::std::future::Future<Output = #output> #lifetimes>>)
12831283
}
@@ -1299,7 +1299,7 @@ fn expand_rust_function_shim_super(
12991299

13001300
let mut body = if let Some(Type::Future(fut)) = &sig.ret {
13011301
if fut.throws_tokens.is_some() {
1302-
quote_spanned!(span=> Box::pin(async move {#call(#(#vars,)*).await.map_err(|e| e.to_string())}))
1302+
quote_spanned!(span=> Box::pin(async move {#call(#(#vars,)*).await.map_err(|e| ::cxx::IntoKjException::into_kj_exception(e, ::cxx::core::file!(), ::cxx::core::line!()))}))
13031303
} else {
13041304
quote_spanned!(span=> Box::pin(#call(#(#vars,)*)))
13051305
}

src/exception.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::{
22
ffi::CStr,
33
fmt::{self, Display},
4+
mem::ManuallyDrop,
45
ptr::NonNull,
56
};
67

@@ -215,6 +216,13 @@ impl KjException {
215216

216217
Some(result)
217218
}
219+
220+
/// Consumes the exception, returning the raw pointer.
221+
/// # Safety
222+
/// The caller must ensure that the returned pointer is eventually dropped.
223+
pub unsafe fn into_raw(self) -> NonNull<repr::KjException> {
224+
ManuallyDrop::new(self).err
225+
}
218226
}
219227

220228
impl Drop for KjException {

0 commit comments

Comments
 (0)