Skip to content

Commit e5730c4

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

21 files changed

+548
-11
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+
"void cxxbridge1$kj_rs$rc${}$is_shared(::kj::Refcounted *ptr, bool *ret) noexcept {{",
1719+
instance,
1720+
);
1721+
writeln!(out, " *ret = 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+
"void cxxbridge1$kj_rs$arc${}$is_shared(::kj::AtomicRefcounted *ptr, bool *ret) noexcept {{",
1769+
instance,
1770+
);
1771+
writeln!(out, " *ret = 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: 3 additions & 3 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
}
@@ -62,7 +62,7 @@ pub mod repr {
6262
/// Returns a raw const pointer to the object owned by this [`Own`]
6363
#[must_use]
6464
pub fn as_ptr(&self) -> *const T {
65-
self.ptr.as_ptr().cast()
65+
self.ptr.as_ptr().cast_const()
6666
}
6767
}
6868

@@ -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: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
/// Bindings to the kj type `kj::Rc`. Represents and owned and reference counted type,
22+
/// like Rust's [`std::rc::Rc`].
23+
pub struct KjRc<T: Refcounted + ?Sized> {
24+
own: Own<T>,
25+
}
26+
27+
#[repr(C)]
28+
/// Bindings to the kj type `kj::Arc`. Represents and owned and atomically reference
29+
/// counted type, like Rust's [`std::sync::Arc`].
30+
pub struct KjArc<T: AtomicRefcounted + ?Sized> {
31+
own: Own<T>,
32+
}
33+
34+
unsafe impl<T: AtomicRefcounted> Send for KjArc<T> where T: Send {}
35+
unsafe impl<T: AtomicRefcounted> Sync for KjArc<T> where T: Sync {}
36+
37+
impl<T: Refcounted> KjRc<T> {
38+
#[must_use]
39+
pub fn get(&self) -> *const T {
40+
self.own.as_ptr()
41+
}
42+
}
43+
44+
impl<T: AtomicRefcounted> KjArc<T> {
45+
#[must_use]
46+
pub fn get(&self) -> *const T {
47+
self.own.as_ptr()
48+
}
49+
50+
// The return value here represents exclusive access to the internal `Own`.
51+
// This allows for exclusive mutation of the inner value.
52+
pub fn get_mut(&mut self) -> Option<&mut Own<T>> {
53+
if self.own.is_shared() {
54+
None
55+
} else {
56+
Some(&mut self.own)
57+
}
58+
}
59+
}
60+
61+
impl<T: Refcounted> Deref for KjRc<T> {
62+
type Target = T;
63+
64+
fn deref(&self) -> &Self::Target {
65+
&self.own
66+
}
67+
}
68+
69+
impl<T: AtomicRefcounted> Deref for KjArc<T> {
70+
type Target = T;
71+
72+
fn deref(&self) -> &Self::Target {
73+
&self.own
74+
}
75+
}
76+
77+
// TODO: DerefMut? Is exposing mutable access to these types really the
78+
// ideal behavior? We shouldn't expose traditional mutable access at
79+
// least, we don't want to break ownership. Convert into Rust Rc? Idk.
80+
// Maybe just making any mutable API unsafe is sufficient for now.
81+
82+
/// Using clone to create another count, like how Rust does it.
83+
impl<T: Refcounted> Clone for KjRc<T> {
84+
fn clone(&self) -> Self {
85+
T::add_ref(self)
86+
}
87+
}
88+
89+
impl<T: AtomicRefcounted> Clone for KjArc<T> {
90+
fn clone(&self) -> Self {
91+
T::add_ref(self)
92+
}
93+
}
94+
95+
// No `Drop` needs to be implemented for `KjRc` or `KjArc`, because the
96+
// internal `Own` `Drop` is sufficient.
97+
}

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: 34 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,36 @@ 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+
#[allow(dead_code)]
87+
#[cxx_name = "setData"]
88+
fn set_data(self: Pin<&mut OpaqueRefcountedClass>, data: u64);
89+
}
90+
91+
unsafe extern "C++" {
92+
include!("kj-rs-demo/test-refcount.h");
93+
94+
type OpaqueAtomicRefcountedClass;
95+
96+
#[allow(dead_code)]
97+
fn get_arc() -> KjArc<OpaqueAtomicRefcountedClass>;
98+
99+
#[allow(dead_code)]
100+
#[cxx_name = "getData"]
101+
fn get_data(&self) -> u64;
102+
#[allow(dead_code)]
103+
#[cxx_name = "setData"]
104+
fn set_data(self: Pin<&mut OpaqueAtomicRefcountedClass>, data: u64);
105+
}
75106
// Helper function to test moving `Own` to C++
76107
extern "Rust" {
77108
fn modify_own_return(cpp_own: Own<OpaqueCxxClass>) -> Own<OpaqueCxxClass>;
@@ -122,6 +153,9 @@ mod ffi {
122153
}
123154
}
124155

156+
unsafe impl Send for ffi::OpaqueAtomicRefcountedClass {}
157+
unsafe impl Sync for ffi::OpaqueAtomicRefcountedClass {}
158+
125159
pub fn modify_own_return(mut own: Own<ffi::OpaqueCxxClass>) -> Own<ffi::OpaqueCxxClass> {
126160
own.pin_mut().set_data(72);
127161
own

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+
}

0 commit comments

Comments
 (0)