Skip to content

Commit 566f14f

Browse files
committed
[hyperlight_common] Add resource table abstraction
Signed-off-by: Lucy Menon <[email protected]>
1 parent fd48da2 commit 566f14f

File tree

4 files changed

+159
-1
lines changed

4 files changed

+159
-1
lines changed

src/hyperlight_common/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ log = "0.4.26"
2121
tracing = { version = "0.1.41", optional = true }
2222
strum = {version = "0.27", default-features = false, features = ["derive"]}
2323
arbitrary = {version = "1.4.1", optional = true, features = ["derive"]}
24+
spin = "0.9.8"
2425

2526
[features]
2627
default = ["tracing"]
2728
fuzzing = ["dep:arbitrary"]
29+
std = []
2830

2931
[dev-dependencies]
3032
hyperlight-testing = { workspace = true }

src/hyperlight_common/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ pub mod flatbuffer_wrappers;
3636
mod flatbuffers;
3737
/// cbindgen:ignore
3838
pub mod mem;
39+
40+
pub mod resource;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! Shared operations around resources
2+
3+
// "Needless" lifetimes are useful for clarity
4+
#![allow(clippy::needless_lifetimes)]
5+
6+
use alloc::sync::Arc;
7+
8+
#[cfg(feature = "std")]
9+
extern crate std;
10+
use core::marker::{PhantomData, Send};
11+
use core::ops::Deref;
12+
#[cfg(feature = "std")]
13+
use std::sync::{RwLock, RwLockReadGuard};
14+
15+
#[cfg(not(feature = "std"))]
16+
use spin::{RwLock, RwLockReadGuard};
17+
18+
/// The semantics of component model resources are, pleasingly,
19+
/// roughly compatible with those of Rust. Less pleasingly, it's not
20+
/// terribly easy to show that statically.
21+
///
22+
/// In particular, if the host calls into the guest and gives it a
23+
/// borrow of a resource, reentrant host function calls that use that
24+
/// borrow need to be able to resolve the original reference and use
25+
/// it in an appropriately scoped manner, but it is not simple to do
26+
/// this, because the core Hyperlight machinery doesn't offer an easy
27+
/// way to augment the host's context for the span of time of a guest
28+
/// function call. This may be worth revisiting at some time, but in
29+
/// the meantime, it's easier to just do it dynamically.
30+
///
31+
/// # Safety
32+
/// Informally: this only creates SharedRead references, so having a
33+
/// bunch of them going at once is fine. Safe Rust in the host can't
34+
/// use any earlier borrows (potentially invalidating these) until
35+
/// borrow passed into [`ResourceEntry::lend`] has expired. Because
36+
/// that borrow outlives the [`LentResourceGuard`], it will not expire
37+
/// until that destructor is called. That destructor ensures that (a)
38+
/// there are no outstanding [`BorrowedResourceGuard`]s alive (since
39+
/// they would be holding the read side of the [`RwLock`] if they
40+
/// were), and that (b) the shared flag has been set to false, so
41+
/// [`ResourceEntry::borrow`] will never create another borrow
42+
pub enum ResourceEntry<T> {
43+
Empty,
44+
Owned(T),
45+
Borrowed(Arc<RwLock<bool>>, *const T),
46+
}
47+
unsafe impl<T: Send> Send for ResourceEntry<T> {}
48+
49+
pub struct LentResourceGuard<'a> {
50+
flag: Arc<RwLock<bool>>,
51+
already_revoked: bool,
52+
_phantom: core::marker::PhantomData<&'a mut ()>,
53+
}
54+
impl<'a> LentResourceGuard<'a> {
55+
pub fn revoke_nonblocking(&mut self) -> bool {
56+
#[cfg(feature = "std")]
57+
let Ok(mut flag) = self.flag.try_write() else {
58+
return false;
59+
};
60+
#[cfg(not(feature = "std"))]
61+
let Some(mut flag) = self.flag.try_write() else {
62+
return false;
63+
};
64+
*flag = false;
65+
self.already_revoked = true;
66+
true
67+
}
68+
}
69+
impl<'a> Drop for LentResourceGuard<'a> {
70+
fn drop(&mut self) {
71+
if !self.already_revoked {
72+
#[allow(unused_mut)] // it isn't actually unused
73+
let mut guard = self.flag.write();
74+
#[cfg(feature = "std")]
75+
// If a mutex that is just protecting us from our own
76+
// mistakes is poisoned, something is so seriously
77+
// wrong that dying is a sensible response.
78+
#[allow(clippy::unwrap_used)]
79+
{
80+
*guard.unwrap() = false;
81+
}
82+
#[cfg(not(feature = "std"))]
83+
{
84+
*guard = false;
85+
}
86+
}
87+
}
88+
}
89+
pub struct BorrowedResourceGuard<'a, T> {
90+
_flag: Option<RwLockReadGuard<'a, bool>>,
91+
reference: &'a T,
92+
}
93+
impl<'a, T> Deref for BorrowedResourceGuard<'a, T> {
94+
type Target = T;
95+
fn deref(&self) -> &T {
96+
self.reference
97+
}
98+
}
99+
impl<T> ResourceEntry<T> {
100+
pub fn give(x: T) -> ResourceEntry<T> {
101+
ResourceEntry::Owned(x)
102+
}
103+
pub fn lend<'a>(x: &'a T) -> (LentResourceGuard<'a>, ResourceEntry<T>) {
104+
let flag = Arc::new(RwLock::new(true));
105+
(
106+
LentResourceGuard {
107+
flag: flag.clone(),
108+
already_revoked: false,
109+
_phantom: PhantomData {},
110+
},
111+
ResourceEntry::Borrowed(flag, x as *const T),
112+
)
113+
}
114+
pub fn borrow<'a>(&'a self) -> Option<BorrowedResourceGuard<'a, T>> {
115+
match self {
116+
ResourceEntry::Empty => None,
117+
ResourceEntry::Owned(t) => Some(BorrowedResourceGuard {
118+
_flag: None,
119+
reference: t,
120+
}),
121+
ResourceEntry::Borrowed(flag, t) => {
122+
let guard = flag.read();
123+
// If a mutex that is just protecting us from our own
124+
// mistakes is poisoned, something is so seriously
125+
// wrong that dying is a sensible response.
126+
#[allow(clippy::unwrap_used)]
127+
let flag = {
128+
#[cfg(feature = "std")]
129+
{
130+
guard.unwrap()
131+
}
132+
#[cfg(not(feature = "std"))]
133+
{
134+
guard
135+
}
136+
};
137+
if *flag {
138+
Some(BorrowedResourceGuard {
139+
_flag: Some(flag),
140+
reference: unsafe { &**t },
141+
})
142+
} else {
143+
None
144+
}
145+
}
146+
}
147+
}
148+
pub fn take(&mut self) -> Option<T> {
149+
match core::mem::replace(self, ResourceEntry::Empty) {
150+
ResourceEntry::Owned(t) => Some(t),
151+
_ => None,
152+
}
153+
}
154+
}

src/hyperlight_host/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ once_cell = { version = "1.21.1" }
3838
tracing = { version = "0.1.41", features = ["log"] }
3939
tracing-log = "0.2.0"
4040
tracing-core = "0.1.33"
41-
hyperlight-common = { workspace = true, default-features = true }
41+
hyperlight-common = { workspace = true, default-features = true, features = [ "std" ] }
4242
vmm-sys-util = "0.12.1"
4343
crossbeam = "0.8.0"
4444
crossbeam-channel = "0.5.14"

0 commit comments

Comments
 (0)