Skip to content

Commit ba876d8

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

20 files changed

+456
-6
lines changed

gen/src/write.rs

Lines changed: 109 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,98 @@ 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!(out, " ::new (ptr) ::kj::Rc<{}>(refcounted->addRef());", inner);
1731+
writeln!(out, "}}");
1732+
}
1733+
1734+
// Writes static assertion that we do not use an Own with a static disposer
1735+
fn write_kj_arc(out: &mut OutFile, key: NamedImplKey) {
1736+
let ident = key.rust;
1737+
let resolve = out.types.resolve(ident);
1738+
let inner = resolve.name.to_fully_qualified();
1739+
let instance = resolve.name.to_symbol();
1740+
1741+
out.include.utility = true;
1742+
out.include.kj_rs = true;
1743+
1744+
// Static disposers are not supported, only Owns containing 2 pointers are allowed
1745+
writeln!(
1746+
out,
1747+
"static_assert(sizeof(::kj::Arc<{}>) == 2 * sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1748+
inner,
1749+
);
1750+
writeln!(
1751+
out,
1752+
"static_assert(alignof(::kj::Arc<{}>) == sizeof(void *), \"Static disposers for Own are not supported in workerd-cxx\");",
1753+
inner,
1754+
);
1755+
writeln!(
1756+
out,
1757+
"static_assert(::std::is_base_of<::kj::AtomicRefcounted, {}>::value, \"Value must inherit kj::AtomicRefcounted\");",
1758+
inner
1759+
);
1760+
1761+
begin_function_definition(out);
1762+
writeln!(
1763+
out,
1764+
"bool cxxbridge1$kj_rs$arc${}$is_shared(::kj::AtomicRefcounted *ptr) noexcept {{",
1765+
instance,
1766+
);
1767+
writeln!(out, " ptr->isShared();");
1768+
writeln!(out, "}}");
1769+
1770+
begin_function_definition(out);
1771+
writeln!(
1772+
out,
1773+
"void cxxbridge1$kj_rs$arc${}$add_ref(::kj::Arc<{}> *refcounted, ::kj::Arc<{}> *ptr) noexcept {{",
1774+
instance, inner, inner
1775+
);
1776+
writeln!(out, " ::new (ptr) ::kj::Arc<{}>(refcounted->addRef());", inner);
1777+
writeln!(out, "}}");
1778+
}
1779+
16721780
fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
16731781
let ty = UniquePtr::Ident(key.rust);
16741782
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: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//! Module for both [`KjRc`] and [`KjArc`], since they're nearly identical types
2+
3+
pub mod repr {
4+
use std::ops::Deref;
5+
use crate::Own;
6+
7+
pub unsafe trait Refcounted {
8+
fn is_shared(&self) -> bool;
9+
fn add_ref(rc: &KjRc<Self>) -> KjRc<Self>;
10+
}
11+
pub unsafe trait AtomicRefcounted {
12+
fn is_shared(&self) -> bool;
13+
fn add_ref(arc: &KjArc<Self>) -> KjArc<Self>;
14+
}
15+
16+
#[repr(C)]
17+
pub struct KjRc<T: Refcounted + ?Sized> {
18+
own: Own<T>,
19+
}
20+
21+
// TODO: `Send` and `Sync`
22+
#[repr(C)]
23+
pub struct KjArc<T: AtomicRefcounted + ?Sized> {
24+
own: Own<T>,
25+
}
26+
27+
impl<T: Refcounted> KjRc<T> {
28+
pub fn get(&self) -> *const T {
29+
self.own.as_ptr()
30+
}
31+
}
32+
33+
impl<T: AtomicRefcounted> KjArc<T> {
34+
pub fn get(&self) -> *const T {
35+
self.own.as_ptr()
36+
}
37+
}
38+
39+
impl<T: Refcounted> Deref for KjRc<T> {
40+
type Target = Own<T>;
41+
42+
fn deref(&self) -> &Self::Target {
43+
&self.own
44+
}
45+
}
46+
47+
impl<T: AtomicRefcounted> Deref for KjArc<T> {
48+
type Target = Own<T>;
49+
50+
fn deref(&self) -> &Self::Target {
51+
&self.own
52+
}
53+
}
54+
55+
impl<T: Refcounted> Clone for KjRc<T> {
56+
fn clone(&self) -> Self {
57+
T::add_ref(self)
58+
}
59+
}
60+
61+
impl<T: AtomicRefcounted> Clone for KjArc<T> {
62+
fn clone(&self) -> Self {
63+
T::add_ref(self)
64+
}
65+
}
66+
}

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: 21 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,26 @@ mod ffi {
7273
fn rust_take_own_driver();
7374
}
7475

76+
77+
unsafe extern "C++" {
78+
include!("kj-rs-demo/test-refcount.h");
79+
80+
type OpaqueRefcountedClass;
81+
82+
fn get_rc() -> KjRc<OpaqueRefcountedClass>;
83+
#[cxx_name = "getData"]
84+
fn get_data(&self) -> u64;
85+
}
86+
87+
unsafe extern "C++" {
88+
include!("kj-rs-demo/test-refcount.h");
89+
90+
type OpaqueAtomicRefcountedClass;
91+
92+
fn get_arc() -> KjArc<OpaqueAtomicRefcountedClass>;
93+
#[cxx_name = "getData"]
94+
fn get_data(&self) -> u64;
95+
}
7596
// Helper function to test moving `Own` to C++
7697
extern "Rust" {
7798
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_refcount.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
#[cfg(test)]
3+
pub mod tests {
4+
use crate::ffi;
5+
6+
#[test]
7+
fn test_rc() {
8+
let rc = ffi::get_rc();
9+
assert_eq!(rc.get_data(), 15);
10+
rc.clone();
11+
}
12+
13+
#[test]
14+
fn test_arc() {
15+
let arc = ffi::get_arc();
16+
assert_eq!(arc.get_data(), 16);
17+
arc.clone();
18+
}
19+
}

0 commit comments

Comments
 (0)