Skip to content

Commit 0b22b8c

Browse files
committed
[hyperlight_common] Add resource table abstraction
Signed-off-by: Lucy Menon <[email protected]>
1 parent 02fc773 commit 0b22b8c

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed

src/hyperlight_common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ 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"]

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

0 commit comments

Comments
 (0)