1use super::{Access, Buffer, Engine, GpuAccess, Runtime, scheduler::Core};
2use crate::{Error, Result};
3use std::{collections::BTreeMap, sync::Arc};
4
5struct Lease {
6 buffers: Vec<Buffer>,
7 core: Arc<Core>,
8 uncertain: bool,
9}
10impl Drop for Lease {
11 fn drop(&mut self) {
12 let _scheduler = self.core.state.lock().unwrap_or_else(|e| e.into_inner());
13 for buffer in &self.buffers {
14 let mut host = buffer
15 .storage
16 .host
17 .lock()
18 .unwrap_or_else(|e| e.into_inner());
19 if self.uncertain {
20 host.poison = Some("external GPU completion is uncertain".into());
21 }
22 host.external = false;
23 }
24 if self.uncertain {
25 std::mem::forget(std::mem::take(&mut self.buffers));
27 }
28 self.core.host_changed.notify_all();
29 }
30}
31
32impl Runtime {
33 pub unsafe fn with_gpu_access<T>(
45 &self,
46 stream: &mut crate::gpu::Stream,
47 accesses: &[GpuAccess],
48 callback: impl FnOnce(&mut crate::gpu::Stream, &[crate::gpu::View<'_>]) -> Result<T>,
49 ) -> Result<T> {
50 let mut roots = BTreeMap::new();
51 for usage in accesses {
52 let storage = &usage.view.buffer.storage;
53 if !Arc::ptr_eq(&storage.runtime, &self.inner) {
54 return Err(Error::Message("buffer belongs to another runtime".into()));
55 }
56 if usage.view.gpu()?.owner().device_id() != stream.device_id() {
57 return Err(Error::Message("stream belongs to another device".into()));
58 }
59 roots.insert(Arc::as_ptr(storage) as usize, usage.view.buffer.clone());
60 }
61 let buffers: Vec<Buffer> = roots.into_values().collect();
62 let core = &self.inner.core;
63 let mut scheduler = core.state.lock().unwrap_or_else(|e| e.into_inner());
64 loop {
65 for buffer in &buffers {
66 buffer.storage.host_conflict(Access::ReadWrite)?;
67 }
68 if !buffers
69 .iter()
70 .any(|b| scheduler.conflicts(&b.view(), Access::ReadWrite))
71 {
72 break;
73 }
74 scheduler = core
75 .host_changed
76 .wait(scheduler)
77 .unwrap_or_else(|e| e.into_inner());
78 }
79 for buffer in &buffers {
80 buffer
81 .storage
82 .host
83 .lock()
84 .unwrap_or_else(|e| e.into_inner())
85 .external = true;
86 }
87 let mut lease = Lease {
88 buffers,
89 core: core.clone(),
90 uncertain: false,
91 };
92 drop(scheduler);
93 for buffer in &lease.buffers {
94 buffer.storage.make_visible(Engine::Gpu)?;
95 }
96 let views = accesses
97 .iter()
98 .map(|a| a.view.gpu())
99 .collect::<Result<Vec<_>>>()?;
100 let outcome =
101 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| callback(stream, &views)));
102 let completion = stream.synchronize();
103 if completion.is_err() {
104 lease.uncertain = true;
105 } else {
106 for usage in accesses.iter().filter(|a| a.access.writes()) {
107 usage
108 .view
109 .buffer
110 .storage
111 .visibility
112 .lock()
113 .unwrap_or_else(|e| e.into_inner())
114 .wrote(Engine::Gpu);
115 }
116 }
117 drop(lease);
118 match outcome {
119 Err(panic) => std::panic::resume_unwind(panic),
120 Ok(result) => {
121 completion?;
122 result
123 }
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 #[test]
132 fn leases_release_reservations_on_unwind() {
133 let runtime = Runtime::new().unwrap();
134 let buffer = super::super::tests::buffer(&runtime);
135 buffer.storage.host.lock().unwrap().external = true;
136 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
137 let _lease = Lease {
138 buffers: vec![buffer.clone()],
139 core: runtime.inner.core.clone(),
140 uncertain: false,
141 };
142 assert!(buffer.try_map_read().is_err());
143 panic!("unwind");
144 }));
145 assert!(result.is_err());
146 assert!(buffer.try_map_write().is_ok());
147 }
148 #[test]
149 #[cfg_attr(miri, ignore = "deliberate quarantine leak")]
150 fn uncertain_handoff_poison_reaches_every_alias() {
151 let runtime = Runtime::new().unwrap();
152 let buffer = super::super::tests::buffer(&runtime);
153 let alias = buffer.clone();
154 drop(Lease {
155 buffers: vec![buffer],
156 core: runtime.inner.core.clone(),
157 uncertain: true,
158 });
159 assert!(matches!(alias.try_map_read(), Err(Error::DeviceLost(_))));
160 assert!(matches!(alias.try_map_write(), Err(Error::DeviceLost(_))));
161 }
162}