1use std::{
2 fmt::{Debug, Display},
3 future::Future,
4 pin::Pin,
5 task::Poll,
6};
7
8use anyhow::Result;
9use auto_hash_map::AutoSet;
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13use crate::{
14 CollectiblesSource, ReadCellOptions, ReadConsistency, ReadOutputOptions, ResolvedVc, TaskId,
15 TaskPersistence, TraitTypeId, ValueType, ValueTypeId, VcValueTrait,
16 backend::{CellContent, TypedCellContent},
17 event::EventListener,
18 id::{ExecutionId, LocalTaskId},
19 manager::{
20 ReadTracking, read_local_output, read_task_cell, read_task_output, with_turbo_tasks,
21 },
22 registry::{self, get_value_type},
23 turbo_tasks,
24};
25
26#[derive(Error, Debug)]
27pub enum ResolveTypeError {
28 #[error("no content in the cell")]
29 NoContent,
30 #[error("the content in the cell has no type")]
31 UntypedContent,
32 #[error("content is not available as task execution failed")]
33 TaskError { source: anyhow::Error },
34 #[error("reading the cell content failed")]
35 ReadError { source: anyhow::Error },
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct CellId {
40 pub type_id: ValueTypeId,
41 pub index: u32,
42}
43
44impl Display for CellId {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(
47 f,
48 "{}#{}",
49 registry::get_value_type(self.type_id).name,
50 self.index
51 )
52 }
53}
54
55#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
65pub enum RawVc {
66 TaskOutput(TaskId),
69 TaskCell(TaskId, CellId),
74 LocalOutput(ExecutionId, LocalTaskId, TaskPersistence),
82}
83
84impl Debug for RawVc {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 match self {
87 RawVc::TaskOutput(task_id) => f
88 .debug_tuple("RawVc::TaskOutput")
89 .field(&**task_id)
90 .finish(),
91 RawVc::TaskCell(task_id, cell_id) => f
92 .debug_tuple("RawVc::TaskCell")
93 .field(&**task_id)
94 .field(&cell_id.to_string())
95 .finish(),
96 RawVc::LocalOutput(execution_id, local_task_id, task_persistence) => f
97 .debug_tuple("RawVc::LocalOutput")
98 .field(&**execution_id)
99 .field(&**local_task_id)
100 .field(task_persistence)
101 .finish(),
102 }
103 }
104}
105
106impl Display for RawVc {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 match self {
109 RawVc::TaskOutput(task_id) => write!(f, "output of task {}", **task_id),
110 RawVc::TaskCell(task_id, cell_id) => {
111 write!(f, "{} of task {}", cell_id, **task_id)
112 }
113 RawVc::LocalOutput(execution_id, local_task_id, task_persistence) => write!(
114 f,
115 "output of local task {} ({}, {})",
116 **local_task_id, **execution_id, task_persistence
117 ),
118 }
119 }
120}
121
122impl RawVc {
123 pub fn is_resolved(&self) -> bool {
124 match self {
125 RawVc::TaskOutput(..) => false,
126 RawVc::TaskCell(..) => true,
127 RawVc::LocalOutput(..) => false,
128 }
129 }
130
131 pub fn is_local(&self) -> bool {
132 match self {
133 RawVc::TaskOutput(..) => false,
134 RawVc::TaskCell(..) => false,
135 RawVc::LocalOutput(..) => true,
136 }
137 }
138
139 pub fn is_transient(&self) -> bool {
144 match self {
145 RawVc::TaskOutput(task) | RawVc::TaskCell(task, ..) => task.is_transient(),
146 RawVc::LocalOutput(_, _, persistence) => *persistence == TaskPersistence::Transient,
147 }
148 }
149
150 pub(crate) fn into_read(self) -> ReadRawVcFuture {
151 ReadRawVcFuture::new(self)
154 }
155
156 pub(crate) async fn resolve_trait(
157 self,
158 trait_type: TraitTypeId,
159 ) -> Result<Option<RawVc>, ResolveTypeError> {
160 self.resolve_type_inner(|value_type_id| {
161 let value_type = get_value_type(value_type_id);
162 (value_type.has_trait(&trait_type), Some(value_type))
163 })
164 .await
165 }
166
167 pub(crate) async fn resolve_value(
168 self,
169 value_type: ValueTypeId,
170 ) -> Result<Option<RawVc>, ResolveTypeError> {
171 self.resolve_type_inner(|cell_value_type| (cell_value_type == value_type, None))
172 .await
173 }
174
175 async fn resolve_type_inner(
183 self,
184 conditional: impl FnOnce(ValueTypeId) -> (bool, Option<&'static ValueType>),
185 ) -> Result<Option<RawVc>, ResolveTypeError> {
186 let tt = turbo_tasks();
187 let mut current = self;
188 loop {
189 match current {
190 RawVc::TaskOutput(task) => {
191 current = read_task_output(&*tt, task, ReadOutputOptions::default())
192 .await
193 .map_err(|source| ResolveTypeError::TaskError { source })?;
194 }
195 RawVc::TaskCell(task, index) => {
196 let content = read_task_cell(&*tt, task, index, ReadCellOptions::default())
197 .await
198 .map_err(|source| ResolveTypeError::ReadError { source })?;
199 if let TypedCellContent(value_type, CellContent(Some(_))) = content {
200 return Ok(if conditional(value_type).0 {
201 Some(RawVc::TaskCell(task, index))
202 } else {
203 None
204 });
205 } else {
206 return Err(ResolveTypeError::NoContent);
207 }
208 }
209 RawVc::LocalOutput(execution_id, local_task_id, ..) => {
210 current = read_local_output(&*tt, execution_id, local_task_id)
211 .await
212 .map_err(|source| ResolveTypeError::TaskError { source })?;
213 }
214 }
215 }
216 }
217
218 pub(crate) async fn resolve(self) -> Result<RawVc> {
220 self.resolve_inner(ReadOutputOptions {
221 tracking: ReadTracking::default(),
222 consistency: ReadConsistency::Eventual,
223 })
224 .await
225 }
226
227 pub(crate) async fn resolve_strongly_consistent(self) -> Result<RawVc> {
229 self.resolve_inner(ReadOutputOptions {
230 tracking: ReadTracking::default(),
231 consistency: ReadConsistency::Strong,
232 })
233 .await
234 }
235
236 async fn resolve_inner(self, mut options: ReadOutputOptions) -> Result<RawVc> {
237 let tt = turbo_tasks();
238 let mut current = self;
239 loop {
240 match current {
241 RawVc::TaskOutput(task) => {
242 current = read_task_output(&*tt, task, options).await?;
243 options.consistency = ReadConsistency::Eventual;
247 }
248 RawVc::TaskCell(_, _) => return Ok(current),
249 RawVc::LocalOutput(execution_id, local_task_id, ..) => {
250 debug_assert_eq!(options.consistency, ReadConsistency::Eventual);
251 current = read_local_output(&*tt, execution_id, local_task_id).await?;
252 }
253 }
254 }
255 }
256
257 pub(crate) async fn to_non_local(self) -> Result<RawVc> {
260 Ok(match self {
261 RawVc::LocalOutput(execution_id, local_task_id, ..) => {
262 let tt = turbo_tasks();
263 let local_output = read_local_output(&*tt, execution_id, local_task_id).await?;
264 debug_assert!(
265 !matches!(local_output, RawVc::LocalOutput(_, _, _)),
266 "a LocalOutput cannot point at other LocalOutputs"
267 );
268 local_output
269 }
270 non_local => non_local,
271 })
272 }
273
274 pub(crate) fn connect(&self) {
275 let RawVc::TaskOutput(task_id) = self else {
276 panic!("RawVc::connect() must only be called on a RawVc::TaskOutput");
277 };
278 let tt = turbo_tasks();
279 tt.connect_task(*task_id);
280 }
281
282 pub fn try_get_task_id(&self) -> Option<TaskId> {
283 match self {
284 RawVc::TaskOutput(t) | RawVc::TaskCell(t, ..) => Some(*t),
285 RawVc::LocalOutput(..) => None,
286 }
287 }
288
289 pub fn try_get_type_id(&self) -> Option<ValueTypeId> {
290 match self {
291 RawVc::TaskCell(_, CellId { type_id, .. }) => Some(*type_id),
292 RawVc::TaskOutput(..) | RawVc::LocalOutput(..) => None,
293 }
294 }
295
296 pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool {
299 match self {
300 RawVc::TaskCell(_task_id, cell_id) => {
301 get_value_type(cell_id.type_id).has_trait(&trait_id)
302 }
303 _ => unreachable!("resolved_has_trait must be called with a RawVc::TaskCell"),
304 }
305 }
306
307 pub(crate) fn resolved_is_type(&self, type_id: ValueTypeId) -> bool {
310 match self {
311 RawVc::TaskCell(_task_id, cell_id) => cell_id.type_id == type_id,
312 _ => unreachable!("resolved_is_type must be called with a RawVc::TaskCell"),
313 }
314 }
315}
316
317impl CollectiblesSource for RawVc {
319 fn peek_collectibles<T: VcValueTrait + ?Sized>(self) -> AutoSet<ResolvedVc<T>> {
320 let RawVc::TaskOutput(task_id) = self else {
321 panic!(
322 "<RawVc as CollectiblesSource>::peek_collectibles() must only be called on a \
323 RawVc::TaskOutput"
324 );
325 };
326 let tt = turbo_tasks();
327 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
328 map.into_iter()
329 .filter_map(|(raw, count)| (count > 0).then_some(raw.try_into().unwrap()))
330 .collect()
331 }
332
333 fn take_collectibles<T: VcValueTrait + ?Sized>(self) -> AutoSet<ResolvedVc<T>> {
334 let RawVc::TaskOutput(task_id) = self else {
335 panic!(
336 "<RawVc as CollectiblesSource>::take_collectibles() must only be called on a \
337 RawVc::TaskOutput"
338 );
339 };
340 let tt = turbo_tasks();
341 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
342 tt.unemit_collectibles(T::get_trait_type_id(), &map);
343 map.into_iter()
344 .filter_map(|(raw, count)| (count > 0).then_some(raw.try_into().unwrap()))
345 .collect()
346 }
347
348 fn drop_collectibles<T: VcValueTrait + ?Sized>(self) {
349 let RawVc::TaskOutput(task_id) = self else {
350 panic!(
351 "<RawVc as CollectiblesSource>::drop_collectibles() must only be called on a \
352 RawVc::TaskOutput"
353 );
354 };
355 let tt = turbo_tasks();
356 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
357 tt.unemit_collectibles(T::get_trait_type_id(), &map);
358 }
359}
360
361pub struct ReadRawVcFuture {
362 current: RawVc,
363 read_output_options: ReadOutputOptions,
364 read_cell_options: ReadCellOptions,
365 listener: Option<EventListener>,
366}
367
368impl ReadRawVcFuture {
369 pub(crate) fn new(vc: RawVc) -> Self {
370 ReadRawVcFuture {
371 current: vc,
372 read_output_options: ReadOutputOptions::default(),
373 read_cell_options: ReadCellOptions::default(),
374 listener: None,
375 }
376 }
377
378 pub fn strongly_consistent(mut self) -> Self {
379 self.read_output_options.consistency = ReadConsistency::Strong;
380 self
381 }
382
383 pub fn untracked(mut self) -> Self {
389 self.read_output_options.tracking = ReadTracking::TrackOnlyError;
390 self.read_cell_options.tracking = ReadTracking::TrackOnlyError;
391 self
392 }
393
394 pub fn untracked_including_errors(mut self) -> Self {
400 self.read_output_options.tracking = ReadTracking::Untracked;
401 self.read_cell_options.tracking = ReadTracking::Untracked;
402 self
403 }
404
405 pub fn final_read_hint(mut self) -> Self {
406 self.read_cell_options.final_read_hint = true;
407 self
408 }
409}
410
411impl Future for ReadRawVcFuture {
412 type Output = Result<TypedCellContent>;
413
414 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
415 with_turbo_tasks(|tt| {
416 let this = unsafe { self.get_unchecked_mut() };
418 'outer: loop {
419 if let Some(listener) = &mut this.listener {
420 let listener = unsafe { Pin::new_unchecked(listener) };
422 if listener.poll(cx).is_pending() {
423 return Poll::Pending;
424 }
425 this.listener = None;
426 }
427 let mut listener = match this.current {
428 RawVc::TaskOutput(task) => {
429 let read_result = tt.try_read_task_output(task, this.read_output_options);
430 match read_result {
431 Ok(Ok(vc)) => {
432 this.read_output_options.consistency = ReadConsistency::Eventual;
436 this.current = vc;
437 continue 'outer;
438 }
439 Ok(Err(listener)) => listener,
440 Err(err) => return Poll::Ready(Err(err)),
441 }
442 }
443 RawVc::TaskCell(task, index) => {
444 let read_result =
445 tt.try_read_task_cell(task, index, this.read_cell_options);
446 match read_result {
447 Ok(Ok(content)) => {
448 return Poll::Ready(Ok(content));
450 }
451 Ok(Err(listener)) => listener,
452 Err(err) => return Poll::Ready(Err(err)),
453 }
454 }
455 RawVc::LocalOutput(execution_id, local_output_id, ..) => {
456 debug_assert_eq!(
457 this.read_output_options.consistency,
458 ReadConsistency::Eventual
459 );
460 let read_result = tt.try_read_local_output(execution_id, local_output_id);
461 match read_result {
462 Ok(Ok(vc)) => {
463 this.current = vc;
464 continue 'outer;
465 }
466 Ok(Err(listener)) => listener,
467 Err(err) => return Poll::Ready(Err(err)),
468 }
469 }
470 };
471 match unsafe { Pin::new_unchecked(&mut listener) }.poll(cx) {
473 Poll::Ready(_) => continue,
474 Poll::Pending => {
475 this.listener = Some(listener);
476 return Poll::Pending;
477 }
478 };
479 }
480 })
481 }
482}
483
484impl Unpin for ReadRawVcFuture {}