1use std::{
2 fmt::{Debug, Display},
3 future::Future,
4 pin::Pin,
5 sync::Arc,
6 task::{Poll, ready},
7};
8
9use anyhow::Result;
10use auto_hash_map::AutoSet;
11use bincode::{Decode, Encode};
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 CollectiblesSource, ReadCellOptions, ReadConsistency, ReadOutputOptions, ResolvedVc, TaskId,
16 TaskPersistence, TraitTypeId, ValueTypeId, VcValueTrait,
17 backend::TypedCellContent,
18 event::EventListener,
19 id::{ExecutionId, LocalTaskId},
20 manager::{
21 ReadCellTracking, ReadTracking, SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK,
22 TurboTasksApi, read_local_output, with_turbo_tasks,
23 },
24 registry::get_value_type,
25 turbo_tasks,
26};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
29pub struct CellId {
30 pub type_id: ValueTypeId,
31 pub index: u32,
32}
33
34impl Display for CellId {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "{}#{}", get_value_type(self.type_id).ty.name, self.index)
37 }
38}
39
40#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
51pub enum RawVc {
52 TaskOutput(TaskId),
55 TaskCell(TaskId, CellId),
60 LocalOutput(ExecutionId, LocalTaskId, TaskPersistence),
70}
71
72impl Debug for RawVc {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 RawVc::TaskOutput(task_id) => f
76 .debug_tuple("RawVc::TaskOutput")
77 .field(&**task_id)
78 .finish(),
79 RawVc::TaskCell(task_id, cell_id) => f
80 .debug_tuple("RawVc::TaskCell")
81 .field(&**task_id)
82 .field(&cell_id.to_string())
83 .finish(),
84 RawVc::LocalOutput(execution_id, local_task_id, task_persistence) => f
85 .debug_tuple("RawVc::LocalOutput")
86 .field(&**execution_id)
87 .field(&**local_task_id)
88 .field(task_persistence)
89 .finish(),
90 }
91 }
92}
93
94impl Display for RawVc {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 RawVc::TaskOutput(task_id) => write!(f, "output of task {}", **task_id),
98 RawVc::TaskCell(task_id, cell_id) => {
99 write!(f, "{} of task {}", cell_id, **task_id)
100 }
101 RawVc::LocalOutput(execution_id, local_task_id, task_persistence) => write!(
102 f,
103 "output of local task {} ({}, {})",
104 **local_task_id, **execution_id, task_persistence
105 ),
106 }
107 }
108}
109
110impl RawVc {
111 pub fn is_resolved(&self) -> bool {
112 match self {
113 RawVc::TaskOutput(..) => false,
114 RawVc::TaskCell(..) => true,
115 RawVc::LocalOutput(..) => false,
116 }
117 }
118
119 pub fn is_local(&self) -> bool {
120 match self {
121 RawVc::TaskOutput(..) => false,
122 RawVc::TaskCell(..) => false,
123 RawVc::LocalOutput(..) => true,
124 }
125 }
126
127 pub fn is_transient(&self) -> bool {
132 match self {
133 RawVc::TaskOutput(task) | RawVc::TaskCell(task, ..) => task.is_transient(),
134 RawVc::LocalOutput(_, _, persistence) => *persistence == TaskPersistence::Transient,
135 }
136 }
137
138 pub(crate) fn into_read(self, is_serializable_cell_content: bool) -> ReadRawVcFuture {
139 ReadRawVcFuture::new(self, Some(is_serializable_cell_content))
142 }
143
144 pub(crate) fn into_read_with_unknown_is_serializable_cell_content(self) -> ReadRawVcFuture {
145 ReadRawVcFuture::new(self, None)
148 }
149
150 pub(crate) fn resolve(self) -> ResolveRawVcFuture {
152 ResolveRawVcFuture::new(self)
153 }
154
155 pub(crate) async fn to_non_local(self) -> Result<RawVc> {
158 Ok(match self {
159 RawVc::LocalOutput(execution_id, local_task_id, ..) => {
160 let tt = turbo_tasks();
161 let local_output = read_local_output(&*tt, execution_id, local_task_id).await?;
162 debug_assert!(
163 !matches!(local_output, RawVc::LocalOutput(_, _, _)),
164 "a LocalOutput cannot point at other LocalOutputs"
165 );
166 local_output
167 }
168 non_local => non_local,
169 })
170 }
171
172 pub(crate) fn connect(&self) {
173 let RawVc::TaskOutput(task_id) = self else {
174 panic!("RawVc::connect() must only be called on a RawVc::TaskOutput");
175 };
176 let tt = turbo_tasks();
177 tt.connect_task(*task_id);
178 }
179
180 pub fn try_get_task_id(&self) -> Option<TaskId> {
181 match self {
182 RawVc::TaskOutput(t) | RawVc::TaskCell(t, ..) => Some(*t),
183 RawVc::LocalOutput(..) => None,
184 }
185 }
186
187 pub fn try_get_type_id(&self) -> Option<ValueTypeId> {
188 match self {
189 RawVc::TaskCell(_, CellId { type_id, .. }) => Some(*type_id),
190 RawVc::TaskOutput(..) | RawVc::LocalOutput(..) => None,
191 }
192 }
193
194 pub(crate) fn resolved_has_trait(&self, trait_id: TraitTypeId) -> bool {
197 match self {
198 RawVc::TaskCell(_task_id, cell_id) => {
199 get_value_type(cell_id.type_id).has_trait(&trait_id)
200 }
201 _ => unreachable!("resolved_has_trait must be called with a RawVc::TaskCell"),
202 }
203 }
204
205 pub(crate) fn resolved_is_type(&self, type_id: ValueTypeId) -> bool {
208 match self {
209 RawVc::TaskCell(_task_id, cell_id) => cell_id.type_id == type_id,
210 _ => unreachable!("resolved_is_type must be called with a RawVc::TaskCell"),
211 }
212 }
213}
214
215impl CollectiblesSource for RawVc {
217 fn peek_collectibles<T: VcValueTrait + ?Sized>(self) -> AutoSet<ResolvedVc<T>> {
218 let RawVc::TaskOutput(task_id) = self else {
219 panic!(
220 "<RawVc as CollectiblesSource>::peek_collectibles() must only be called on a \
221 RawVc::TaskOutput"
222 );
223 };
224 let tt = turbo_tasks();
225 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
226 map.into_iter()
227 .filter_map(|(raw, count)| (count > 0).then_some(raw.try_into().unwrap()))
228 .collect()
229 }
230
231 fn take_collectibles<T: VcValueTrait + ?Sized>(self) -> AutoSet<ResolvedVc<T>> {
232 let RawVc::TaskOutput(task_id) = self else {
233 panic!(
234 "<RawVc as CollectiblesSource>::take_collectibles() must only be called on a \
235 RawVc::TaskOutput"
236 );
237 };
238 let tt = turbo_tasks();
239 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
240 tt.unemit_collectibles(T::get_trait_type_id(), &map);
241 map.into_iter()
242 .filter_map(|(raw, count)| (count > 0).then_some(raw.try_into().unwrap()))
243 .collect()
244 }
245
246 fn drop_collectibles<T: VcValueTrait + ?Sized>(self) {
247 let RawVc::TaskOutput(task_id) = self else {
248 panic!(
249 "<RawVc as CollectiblesSource>::drop_collectibles() must only be called on a \
250 RawVc::TaskOutput"
251 );
252 };
253 let tt = turbo_tasks();
254 let map = tt.read_task_collectibles(task_id, T::get_trait_type_id());
255 tt.unemit_collectibles(T::get_trait_type_id(), &map);
256 }
257}
258
259fn poll_listener(
262 listener: &mut Option<EventListener>,
263 cx: &mut std::task::Context<'_>,
264) -> Poll<()> {
265 if let Some(l) = listener {
266 ready!(Pin::new(l).poll(cx));
267 *listener = None;
268 }
269 Poll::Ready(())
270}
271
272fn suppress_top_level_task_check<R>(strongly_consistent: bool, f: impl FnOnce() -> R) -> R {
279 if cfg!(debug_assertions) && strongly_consistent {
280 SUPPRESS_EVENTUAL_CONSISTENCY_TOP_LEVEL_TASK_CHECK.sync_scope(true, f)
282 } else {
283 f()
284 }
285}
286
287#[must_use]
288pub struct ResolveRawVcFuture {
289 current: RawVc,
290 read_output_options: ReadOutputOptions,
291 strongly_consistent: bool,
294 listener: Option<EventListener>,
295}
296
297impl ResolveRawVcFuture {
298 fn new(vc: RawVc) -> Self {
299 ResolveRawVcFuture {
300 current: vc,
301 read_output_options: ReadOutputOptions::default(),
302 strongly_consistent: false,
303 listener: None,
304 }
305 }
306
307 pub fn strongly_consistent(mut self) -> Self {
308 self.strongly_consistent = true;
309 self.read_output_options.consistency = ReadConsistency::Strong;
310 self
311 }
312
313 pub(crate) fn track_with_key(mut self) -> Self {
316 self.read_output_options.tracking = ReadTracking::Tracked;
317 self
318 }
319
320 pub(crate) fn untracked(mut self) -> Self {
323 self.read_output_options.tracking = ReadTracking::TrackOnlyError;
324 self
325 }
326}
327
328impl Future for ResolveRawVcFuture {
329 type Output = Result<RawVc>;
330
331 #[inline(never)]
332 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
333 let this = unsafe { self.get_unchecked_mut() };
335
336 let poll_fn = |tt: &Arc<dyn TurboTasksApi>| -> Poll<Self::Output> {
337 'outer: loop {
338 ready!(poll_listener(&mut this.listener, cx));
339 let listener = match this.current {
340 RawVc::TaskOutput(task) => {
341 let read_result = tt.try_read_task_output(task, this.read_output_options);
342 match read_result {
343 Ok(Ok(vc)) => {
344 this.read_output_options.consistency = ReadConsistency::Eventual;
353 this.current = vc;
354 continue 'outer;
355 }
356 Ok(Err(listener)) => listener,
357 Err(err) => return Poll::Ready(Err(err)),
358 }
359 }
360 RawVc::TaskCell(_, _) => return Poll::Ready(Ok(this.current)),
361 RawVc::LocalOutput(execution_id, local_task_id, ..) => {
362 debug_assert_eq!(
363 this.read_output_options.consistency,
364 ReadConsistency::Eventual
365 );
366 let read_result = tt.try_read_local_output(execution_id, local_task_id);
367 match read_result {
368 Ok(Ok(vc)) => {
369 this.current = vc;
370 continue 'outer;
371 }
372 Ok(Err(listener)) => listener,
373 Err(err) => return Poll::Ready(Err(err)),
374 }
375 }
376 };
377 this.listener = Some(listener);
378 }
379 };
380
381 suppress_top_level_task_check(this.strongly_consistent, || with_turbo_tasks(poll_fn))
388 }
389}
390
391impl Unpin for ResolveRawVcFuture {}
392
393#[must_use]
394pub struct ReadRawVcFuture {
395 resolve: ResolveRawVcFuture,
397 read_cell_options: ReadCellOptions,
399 is_serializable_cell_content_unknown: bool,
403 resolved: Option<(TaskId, CellId)>,
405 listener: Option<EventListener>,
407}
408
409impl ReadRawVcFuture {
410 pub(crate) fn new(vc: RawVc, is_serializable_cell_content: Option<bool>) -> Self {
411 ReadRawVcFuture {
412 resolve: ResolveRawVcFuture::new(vc),
413 read_cell_options: ReadCellOptions {
414 is_serializable_cell_content: is_serializable_cell_content.unwrap_or(false),
415 ..Default::default()
416 },
417 is_serializable_cell_content_unknown: is_serializable_cell_content.is_none(),
418 resolved: None,
419 listener: None,
420 }
421 }
422
423 pub fn strongly_consistent(mut self) -> Self {
425 self.resolve = self.resolve.strongly_consistent();
426 self
427 }
428
429 pub fn track_with_key(mut self, key: u64) -> Self {
431 self.resolve = self.resolve.track_with_key();
432 self.read_cell_options.tracking = ReadCellTracking::Tracked { key: Some(key) };
433 self
434 }
435
436 pub fn untracked(mut self) -> Self {
442 self.resolve = self.resolve.untracked();
443 self.read_cell_options.tracking = ReadCellTracking::TrackOnlyError;
444 self
445 }
446
447 pub fn final_read_hint(mut self) -> Self {
449 self.read_cell_options.final_read_hint = true;
450 self
451 }
452}
453
454impl Future for ReadRawVcFuture {
455 type Output = Result<TypedCellContent>;
456
457 #[inline(never)]
458 fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
459 let this = unsafe { self.get_unchecked_mut() };
461
462 if this.resolved.is_none() {
467 match ready!(Pin::new(&mut this.resolve).poll(cx)) {
468 Err(err) => return Poll::Ready(Err(err)),
469 Ok(RawVc::TaskCell(task, index)) => {
470 this.resolved = Some((task, index));
471 }
472 Ok(_) => unreachable!("ResolveRawVcFuture always resolves to a TaskCell"),
473 }
474 }
475
476 let (task, index) = this.resolved.unwrap();
480
481 if this.is_serializable_cell_content_unknown {
484 this.read_cell_options.is_serializable_cell_content =
485 get_value_type(index.type_id).bincode.is_some();
486 this.is_serializable_cell_content_unknown = false;
487 }
488
489 let poll_fn = |tt: &Arc<dyn TurboTasksApi>| -> Poll<Self::Output> {
490 loop {
491 ready!(poll_listener(&mut this.listener, cx));
492 let listener = match tt.try_read_task_cell(task, index, this.read_cell_options) {
493 Ok(Ok(content)) => return Poll::Ready(Ok(content)),
494 Ok(Err(listener)) => listener,
495 Err(err) => return Poll::Ready(Err(err)),
496 };
497 this.listener = Some(listener);
498 }
499 };
500
501 suppress_top_level_task_check(this.resolve.strongly_consistent, || {
506 with_turbo_tasks(poll_fn)
507 })
508 }
509}
510
511impl Unpin for ReadRawVcFuture {}