query_runtime/runtime/gpu_execution_pool/
lease.rs1use std::{
4 future::Future,
5 panic::AssertUnwindSafe,
6 sync::{Arc, mpsc},
7};
8
9use query_planning::error::{QueryError, QueryErrorCode, Result};
10
11use super::{
12 GpuExecutionJobClass, GpuExecutionJobKind, GpuExecutionPool, JobObservation, RUN_OPERATION, job_panic_error,
13};
14use crate::observability::{RuntimeObservationHandle, RuntimeStreamLeaseScope};
15
16#[derive(Clone)]
21pub struct GpuExecutorLease {
22 pool: GpuExecutionPool,
23 execution_class: GpuExecutionJobClass,
33}
34
35impl GpuExecutorLease {
36 pub(crate) fn from_pool(pool: GpuExecutionPool) -> Self {
37 Self { pool, execution_class: GpuExecutionJobClass::Lifecycle }
38 }
39
40 pub(crate) fn from_pool_for_auxiliary_fragment(pool: GpuExecutionPool) -> Self {
45 Self { pool, execution_class: GpuExecutionJobClass::Execution }
46 }
47
48 pub async fn run<F, T>(&self, kind: GpuExecutionJobKind, future: F) -> Result<T>
50 where
51 F: Future<Output = T> + Send + 'static,
52 T: Send + 'static,
53 {
54 self.pool.run(kind, future).await
55 }
56
57 pub fn run_observed_scoped<B, F, T>(
60 &self,
61 kind: GpuExecutionJobKind,
62 observation: RuntimeObservationHandle,
63 build_future: B,
64 ) -> impl Future<Output = Result<T>> + Send + 'static + use<B, F, T>
65 where
66 B: FnOnce(RuntimeStreamLeaseScope) -> F + Send + 'static,
67 F: Future<Output = T> + Send + 'static,
68 T: Send + 'static,
69 {
70 let class = match kind {
71 GpuExecutionJobKind::QueryExecution => self.execution_class,
72 _ => kind.job_class(),
73 };
74 self.pool.clone().run_observed_scoped_in_class(kind, class, observation, build_future)
75 }
76
77 pub fn submit_blocking<F, T>(
85 &self,
86 kind: GpuExecutionJobKind,
87 observation: RuntimeObservationHandle,
88 work: F,
89 ) -> Result<PoolBlockingWork<T>>
90 where
91 F: FnOnce() -> T + Send + 'static,
92 T: Send + 'static,
93 {
94 self.pool.submit_blocking(kind, observation, work)
95 }
96
97 #[doc(hidden)]
100 pub fn submit_blocking_with_completion<F, T, C>(
101 &self,
102 kind: GpuExecutionJobKind,
103 observation: RuntimeObservationHandle,
104 work: F,
105 completion: C,
106 ) -> Result<PoolBlockingWork<T>>
107 where
108 F: FnOnce() -> T + Send + 'static,
109 T: Send + 'static,
110 C: FnOnce() + Send + 'static,
111 {
112 self.pool.submit_blocking_with_completion(kind, observation, work, completion)
113 }
114}
115
116impl std::fmt::Debug for GpuExecutorLease {
117 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 formatter.debug_struct("GpuExecutorLease").finish_non_exhaustive()
119 }
120}
121
122pub struct PoolBlockingWork<T> {
128 pub(super) receiver: mpsc::Receiver<BlockingWorkOutcome<T>>,
129 pub(super) job: Arc<JobObservation>,
130 pub(super) device_ordinal: usize,
131 pub(super) kind: GpuExecutionJobKind,
132}
133
134impl<T> std::fmt::Debug for PoolBlockingWork<T> {
135 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 formatter.debug_struct("PoolBlockingWork").finish_non_exhaustive()
137 }
138}
139
140pub(super) enum BlockingWorkOutcome<T> {
141 Completed(T),
142 Panicked,
143}
144
145pub(super) struct BlockingCompletionNotify<C: FnOnce()> {
149 completion: Option<C>,
150}
151
152impl<C: FnOnce()> BlockingCompletionNotify<C> {
153 pub(super) fn new(completion: C) -> Self {
154 Self { completion: Some(completion) }
155 }
156
157 pub(super) fn notify(&mut self) {
158 if let Some(completion) = self.completion.take() {
159 completion();
160 }
161 }
162}
163
164impl<C: FnOnce()> Drop for BlockingCompletionNotify<C> {
165 fn drop(&mut self) {
166 if self.completion.is_some() {
167 let _ = std::panic::catch_unwind(AssertUnwindSafe(|| self.notify()));
170 }
171 }
172}
173
174impl<T> PoolBlockingWork<T> {
175 pub fn join(self) -> Result<T> {
184 match self.receiver.recv() {
185 Ok(BlockingWorkOutcome::Completed(value)) => Ok(value),
186 Ok(BlockingWorkOutcome::Panicked) => Err(job_panic_error(self.device_ordinal, self.kind)),
187 Err(_) => {
188 self.job.cancellation_requested();
189 Err(QueryError::new(
190 QueryErrorCode::Cancelled,
191 RUN_OPERATION,
192 "GPU execution pool blocking worker ended without delivering completion",
193 )
194 .with_fact("device_ordinal", self.device_ordinal)
195 .with_fact("job_kind", self.kind.as_str())
196 .with_fact("reason", "gpu_execution_pool_blocking_worker_lost"))
197 },
198 }
199 }
200}