Skip to content

Commit 57ca927

Browse files
committed
[hyperlight_common] Add resource table abstraction
Signed-off-by: Lucy Menon <[email protected]>
1 parent 2db117f commit 57ca927

File tree

3 files changed

+133
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)