Skip to content

Commit b2ad28e

Browse files
committed
[workerd-cxx] Add kj::Rc and kj::Arc bindings to kj-rs
1 parent 440afe9 commit b2ad28e

21 files changed

+469
-10
lines changed

gen/src/write.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
233233
Type::SliceRef(_) => out.builtin.rust_slice = true,
234234
Type::Array(_) => out.include.array = true,
235235
Type::Ref(_) | Type::Void(_) | Type::Ptr(_) => {}
236-
Type::Future(_) | Type::Own(_) => out.include.kj_rs = true,
236+
Type::Future(_) | Type::Own(_) | Type::KjRc(_) | Type::KjArc(_) => {
237+
out.include.kj_rs = true;
238+
}
237239
}
238240
}
239241
}
@@ -1253,6 +1255,16 @@ fn write_type(out: &mut OutFile, ty: &Type) {
12531255
write_type(out, &ptr.inner);
12541256
write!(out, ">");
12551257
}
1258+
Type::KjRc(ptr) => {
1259+
write!(out, "::kj::Rc<");
1260+
write_type(out, &ptr.inner);
1261+
write!(out, ">");
1262+
}
1263+
Type::KjArc(ptr) => {
1264+
write!(out, "::kj::Arc<");
1265+
write_type(out, &ptr.inner);
1266+
write!(out, ">");
1267+
}
12561268
Type::SharedPtr(ptr) => {
12571269
write!(out, "::std::shared_ptr<");
12581270
write_type(out, &ptr.inner);
@@ -1354,6 +1366,8 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) {
13541366
| Type::RustBox(_)
13551367
| Type::UniquePtr(_)
13561368
| Type::Own(_)
1369+
| Type::KjRc(_)
1370+
| Type::KjArc(_)
13571371
| Type::SharedPtr(_)
13581372
| Type::WeakPtr(_)
13591373
| Type::Str(_)
@@ -1431,6 +1445,8 @@ fn write_generic_instantiations(out: &mut OutFile) {
14311445
ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident),
14321446
ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident),
14331447
ImplKey::Own(ident) => write_kj_own(out, ident),
1448+
ImplKey::KjRc(ident) => write_kj_rc(out, ident),
1449+
ImplKey::KjArc(ident) => write_kj_arc(out, ident),
14341450
ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident),
14351451
ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident),
14361452
ImplKey::CxxVector(ident) => write_cxx_vector(out, ident),
@@ -1669,6 +1685,106 @@ fn write_kj_own(out: &mut OutFile, key: NamedImplKey) {
16691685
);
16701686
}
16711687

1688+
// Writes static assertion that we do not use an Own with a static disposer
1689+
fn write_kj_rc(out: &mut OutFile, key: NamedImplKey) {
1690+
let ident = key.rust;
1691+
let resolve = out.types.resolve(ident);
1692+
let inner = resolve.name.to_fully_qualified();
1693+
let instance = resolve.name.to_symbol();
1694+
1695+
out.include.utility = true;
1696+
out.include.kj_rs = true;
1697+
1698+
// Static disposers are not supported, only Owns containing 2 pointers are allowed
1699+
writeln!(
1700+
out,
1701+
"static_assert(sizeof(::kj::Rc<{}>) == 2 * sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1702+
inner,
1703+
);
1704+
writeln!(
1705+
out,
1706+
"static_assert(alignof(::kj::Rc<{}>) == sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1707+
inner,
1708+
);
1709+
writeln!(
1710+
out,
1711+
"static_assert(::std::is_base_of<::kj::Refcounted, {}>::value, \"Value must inherit kj::Refcounted\");",
1712+
inner
1713+
);
1714+
1715+
begin_function_definition(out);
1716+
writeln!(
1717+
out,
1718+
"bool cxxbridge1$kj_rs$rc${}$is_shared(::kj::Refcounted *ptr) noexcept {{",
1719+
instance,
1720+
);
1721+
writeln!(out, " ptr->isShared();");
1722+
writeln!(out, "}}");
1723+
1724+
begin_function_definition(out);
1725+
writeln!(
1726+
out,
1727+
"void cxxbridge1$kj_rs$rc${}$add_ref(::kj::Rc<{}> *refcounted, ::kj::Rc<{}> *ptr) noexcept {{",
1728+
instance, inner, inner
1729+
);
1730+
writeln!(
1731+
out,
1732+
" ::new (ptr) ::kj::Rc<{}>(refcounted->addRef());",
1733+
inner
1734+
);
1735+
writeln!(out, "}}");
1736+
}
1737+
1738+
// Writes static assertion that we do not use an Own with a static disposer
1739+
fn write_kj_arc(out: &mut OutFile, key: NamedImplKey) {
1740+
let ident = key.rust;
1741+
let resolve = out.types.resolve(ident);
1742+
let inner = resolve.name.to_fully_qualified();
1743+
let instance = resolve.name.to_symbol();
1744+
1745+
out.include.utility = true;
1746+
out.include.kj_rs = true;
1747+
1748+
// Static disposers are not supported, only Owns containing 2 pointers are allowed
1749+
writeln!(
1750+
out,
1751+
"static_assert(sizeof(::kj::Arc<{}>) == 2 * sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1752+
inner,
1753+
);
1754+
writeln!(
1755+
out,
1756+
"static_assert(alignof(::kj::Arc<{}>) == sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1757+
inner,
1758+
);
1759+
writeln!(
1760+
out,
1761+
"static_assert(::std::is_base_of<::kj::AtomicRefcounted, {}>::value, \"Value must inherit kj::AtomicRefcounted\");",
1762+
inner
1763+
);
1764+
1765+
begin_function_definition(out);
1766+
writeln!(
1767+
out,
1768+
"bool cxxbridge1$kj_rs$arc${}$is_shared(::kj::AtomicRefcounted *ptr) noexcept {{",
1769+
instance,
1770+
);
1771+
writeln!(out, " ptr->isShared();");
1772+
writeln!(out, "}}");
1773+
1774+
begin_function_definition(out);
1775+
writeln!(
1776+
out,
1777+
"void cxxbridge1$kj_rs$arc${}$add_ref(::kj::Arc<{}> *refcounted, ::kj::Arc<{}> *ptr) noexcept {{",
1778+
instance, inner, inner
1779+
);
1780+
writeln!(
1781+
out,
1782+
" ::new (ptr) ::kj::Arc<{}>(refcounted->addRef());",
1783+
inner
1784+
);
1785+
writeln!(out, "}}");
1786+
}
1787+
16721788
fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
16731789
let ty = UniquePtr::Ident(key.rust);
16741790
write_unique_ptr_common(out, ty);

kj-rs/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ mod awaiter;
1515
mod future;
1616
mod own;
1717
mod promise;
18+
mod refcount;
1819
mod waker;
1920

2021
pub mod repr {
2122
pub use crate::future::repr::*;
2223
pub use crate::own::repr::*;
24+
pub use crate::refcount::repr::*;
2325
}
2426

2527
pub type Result<T> = std::io::Result<T>;

kj-rs/own.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub mod repr {
2121
/// - Currently, it is runtime asserted in the bridge macro that no null Own can be passed
2222
/// to Rust
2323
#[repr(C)]
24-
pub struct Own<T> {
24+
pub struct Own<T: ?Sized> {
2525
disposer: *const c_void,
2626
ptr: NonNull<T>,
2727
}
@@ -153,7 +153,7 @@ pub mod repr {
153153
}
154154
}
155155

156-
impl<T> Drop for Own<T> {
156+
impl<T: ?Sized> Drop for Own<T> {
157157
fn drop(&mut self) {
158158
unsafe extern "C" {
159159
#[link_name = "cxxbridge$kjrs$own$drop"]

kj-rs/refcount.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! Module for both [`KjRc`] and [`KjArc`], since they're nearly identical types
2+
3+
pub mod repr {
4+
use crate::Own;
5+
use std::ops::Deref;
6+
7+
/// # Safety
8+
/// - Should only be automatically implemented by the bridge macro
9+
pub unsafe trait Refcounted {
10+
fn is_shared(&self) -> bool;
11+
fn add_ref(rc: &KjRc<Self>) -> KjRc<Self>;
12+
}
13+
/// # Safety
14+
/// - Should only be automatically implemented by the bridge macro
15+
pub unsafe trait AtomicRefcounted {
16+
fn is_shared(&self) -> bool;
17+
fn add_ref(arc: &KjArc<Self>) -> KjArc<Self>;
18+
}
19+
20+
#[repr(C)]
21+
pub struct KjRc<T: Refcounted + ?Sized> {
22+
own: Own<T>,
23+
}
24+
25+
// TODO: `Send` and `Sync`
26+
#[repr(C)]
27+
pub struct KjArc<T: AtomicRefcounted + ?Sized> {
28+
own: Own<T>,
29+
}
30+
31+
impl<T: Refcounted> KjRc<T> {
32+
#[must_use]
33+
pub fn get(&self) -> *const T {
34+
self.own.as_ptr()
35+
}
36+
}
37+
38+
impl<T: AtomicRefcounted> KjArc<T> {
39+
#[must_use]
40+
pub fn get(&self) -> *const T {
41+
self.own.as_ptr()
42+
}
43+
}
44+
45+
impl<T: Refcounted> Deref for KjRc<T> {
46+
type Target = Own<T>;
47+
48+
fn deref(&self) -> &Self::Target {
49+
&self.own
50+
}
51+
}
52+
53+
impl<T: AtomicRefcounted> Deref for KjArc<T> {
54+
type Target = Own<T>;
55+
56+
fn deref(&self) -> &Self::Target {
57+
&self.own
58+
}
59+
}
60+
61+
impl<T: Refcounted> Clone for KjRc<T> {
62+
fn clone(&self) -> Self {
63+
T::add_ref(self)
64+
}
65+
}
66+
67+
impl<T: AtomicRefcounted> Clone for KjArc<T> {
68+
fn clone(&self) -> Self {
69+
T::add_ref(self)
70+
}
71+
}
72+
}

kj-rs/tests/BUILD.bazel

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ rust_cxx_bridge(
4545
src = "lib.rs",
4646
hdrs = [
4747
"test-promises.h",
48-
"test-own.h"
48+
"test-own.h",
49+
"test-refcount.h"
4950
],
5051
include_prefix = "kj-rs-demo",
5152
deps = [
@@ -57,7 +58,8 @@ cc_library(
5758
name = "test-promises",
5859
srcs = [
5960
"test-promises.c++",
60-
"test-own.c++"
61+
"test-own.c++",
62+
"test-refcount.c++"
6163
],
6264
linkstatic = select({
6365
"@platforms//os:windows": True,

kj-rs/tests/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
mod test_futures;
1010
mod test_own;
11+
mod test_refcount;
1112

1213
use test_futures::{
1314
new_awaiting_future_i32, new_error_handling_future_void_infallible, new_errored_future_void,
@@ -72,6 +73,29 @@ mod ffi {
7273
fn rust_take_own_driver();
7374
}
7475

76+
unsafe extern "C++" {
77+
include!("kj-rs-demo/test-refcount.h");
78+
79+
type OpaqueRefcountedClass;
80+
81+
#[allow(dead_code)]
82+
fn get_rc() -> KjRc<OpaqueRefcountedClass>;
83+
#[allow(dead_code)]
84+
#[cxx_name = "getData"]
85+
fn get_data(&self) -> u64;
86+
}
87+
88+
unsafe extern "C++" {
89+
include!("kj-rs-demo/test-refcount.h");
90+
91+
type OpaqueAtomicRefcountedClass;
92+
93+
#[allow(dead_code)]
94+
fn get_arc() -> KjArc<OpaqueAtomicRefcountedClass>;
95+
#[allow(dead_code)]
96+
#[cxx_name = "getData"]
97+
fn get_data(&self) -> u64;
98+
}
7599
// Helper function to test moving `Own` to C++
76100
extern "Rust" {
77101
fn modify_own_return(cpp_own: Own<OpaqueCxxClass>) -> Own<OpaqueCxxClass>;

kj-rs/tests/test-refcount.c++

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "test-refcount.h"
2+
3+
namespace kj_rs_demo {
4+
kj::Rc<OpaqueRefcountedClass> get_rc() {
5+
return kj::rc<OpaqueRefcountedClass>(15);
6+
}
7+
8+
kj::Arc<OpaqueAtomicRefcountedClass> get_arc() {
9+
return kj::arc<OpaqueAtomicRefcountedClass>(16);
10+
}
11+
}

kj-rs/tests/test-refcount.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#pragma once
2+
3+
#include "kj/refcount.h"
4+
5+
#include <cstdint>
6+
7+
namespace kj_rs_demo {
8+
9+
class OpaqueRefcountedClass: public kj::Refcounted {
10+
public:
11+
OpaqueRefcountedClass(uint64_t data): data(data) {}
12+
~OpaqueRefcountedClass() {}
13+
uint64_t getData() const {
14+
return this->data;
15+
}
16+
void setData(uint64_t val) {
17+
this->data = val;
18+
}
19+
20+
private:
21+
uint64_t data;
22+
};
23+
24+
class OpaqueAtomicRefcountedClass: public kj::AtomicRefcounted {
25+
public:
26+
OpaqueAtomicRefcountedClass(uint64_t data): data(data) {}
27+
~OpaqueAtomicRefcountedClass() {}
28+
uint64_t getData() const {
29+
return this->data;
30+
}
31+
void setData(uint64_t val) {
32+
this->data = val;
33+
}
34+
35+
private:
36+
uint64_t data;
37+
};
38+
39+
kj::Rc<OpaqueRefcountedClass> get_rc();
40+
kj::Arc<OpaqueAtomicRefcountedClass> get_arc();
41+
42+
} // namespace kj_rs_demo

kj-rs/tests/test_futures.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,7 @@ pub async fn new_layered_ready_future_void() -> Result<()> {
149149
}
150150

151151
// From example at https://doc.rust-lang.org/std/future/fn.poll_fn.html#capturing-a-pinned-state
152-
async fn naive_select<T>(
153-
a: impl Future<Output = T>,
154-
b: impl Future<Output = T>,
155-
) -> T {
152+
async fn naive_select<T>(a: impl Future<Output = T>, b: impl Future<Output = T>) -> T {
156153
let (mut a, mut b) = (pin!(a), pin!(b));
157154
future::poll_fn(move |cx| {
158155
if let Poll::Ready(r) = a.as_mut().poll(cx) {

0 commit comments

Comments
 (0)