@@ -5,6 +5,7 @@ mod storage;
5
5
6
6
use std:: {
7
7
borrow:: Cow ,
8
+ fmt:: { self , Write } ,
8
9
future:: Future ,
9
10
hash:: BuildHasherDefault ,
10
11
mem:: take,
@@ -18,6 +19,7 @@ use std::{
18
19
19
20
use anyhow:: { bail, Result } ;
20
21
use auto_hash_map:: { AutoMap , AutoSet } ;
22
+ use indexmap:: IndexSet ;
21
23
use parking_lot:: { Condvar , Mutex } ;
22
24
use rustc_hash:: { FxHashMap , FxHashSet , FxHasher } ;
23
25
use smallvec:: smallvec;
@@ -28,8 +30,9 @@ use turbo_tasks::{
28
30
TransientTaskType , TypedCellContent ,
29
31
} ,
30
32
event:: { Event , EventListener } ,
31
- registry,
33
+ registry:: { self , get_value_type_global_name } ,
32
34
task_statistics:: TaskStatisticsApi ,
35
+ trace:: TraceRawVcs ,
33
36
util:: IdFactoryWithReuse ,
34
37
CellId , FunctionId , FxDashMap , RawVc , ReadCellOptions , ReadConsistency , SessionId , TaskId ,
35
38
TraitTypeId , TurboTasksBackendApi , ValueTypeId , TRANSIENT_TASK_BIT ,
@@ -425,7 +428,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
425
428
turbo_tasks : & dyn TurboTasksBackendApi < TurboTasksBackend < B > > ,
426
429
) -> Result < Result < RawVc , EventListener > > {
427
430
if let Some ( reader) = reader {
428
- self . assert_not_persistent_calling_transient ( reader, task_id) ;
431
+ self . assert_not_persistent_calling_transient ( reader, task_id, /* cell_id */ None ) ;
429
432
}
430
433
431
434
let mut ctx = self . execute_context ( turbo_tasks) ;
@@ -625,7 +628,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
625
628
turbo_tasks : & dyn TurboTasksBackendApi < TurboTasksBackend < B > > ,
626
629
) -> Result < Result < TypedCellContent , EventListener > > {
627
630
if let Some ( reader) = reader {
628
- self . assert_not_persistent_calling_transient ( reader, task_id) ;
631
+ self . assert_not_persistent_calling_transient ( reader, task_id, Some ( cell ) ) ;
629
632
}
630
633
631
634
fn add_cell_dependency < B : BackingStorage > (
@@ -985,9 +988,10 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
985
988
turbo_tasks : & dyn TurboTasksBackendApi < TurboTasksBackend < B > > ,
986
989
) -> TaskId {
987
990
if !parent_task. is_transient ( ) {
988
- panic_persistent_calling_transient (
991
+ self . panic_persistent_calling_transient (
989
992
self . lookup_task_type ( parent_task) . as_deref ( ) ,
990
993
Some ( & task_type) ,
994
+ /* cell_id */ None ,
991
995
) ;
992
996
}
993
997
if let Some ( task_id) = self . task_cache . lookup_forward ( & task_type) {
@@ -1013,6 +1017,84 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
1013
1017
task_id
1014
1018
}
1015
1019
1020
+ /// Generate an object that implements [`fmt::Display`] explaining why the given
1021
+ /// [`CachedTaskType`] is transient.
1022
+ fn debug_trace_transient_task (
1023
+ & self ,
1024
+ task_type : & CachedTaskType ,
1025
+ cell_id : Option < CellId > ,
1026
+ ) -> DebugTraceTransientTask {
1027
+ // it shouldn't be possible to have cycles in tasks, but we could have an exponential blowup
1028
+ // from tracing the same task many times, so use a visited_set
1029
+ fn inner_id (
1030
+ backend : & TurboTasksBackendInner < impl BackingStorage > ,
1031
+ task_id : TaskId ,
1032
+ cell_type_id : Option < ValueTypeId > ,
1033
+ visited_set : & mut FxHashSet < TaskId > ,
1034
+ ) -> DebugTraceTransientTask {
1035
+ if let Some ( task_type) = backend. lookup_task_type ( task_id) {
1036
+ if visited_set. contains ( & task_id) {
1037
+ let task_name = task_type. get_name ( ) ;
1038
+ DebugTraceTransientTask :: Collapsed {
1039
+ task_name,
1040
+ cell_type_id,
1041
+ }
1042
+ } else {
1043
+ inner_cached ( backend, & task_type, cell_type_id, visited_set)
1044
+ }
1045
+ } else {
1046
+ DebugTraceTransientTask :: Uncached { cell_type_id }
1047
+ }
1048
+ }
1049
+ fn inner_cached (
1050
+ backend : & TurboTasksBackendInner < impl BackingStorage > ,
1051
+ task_type : & CachedTaskType ,
1052
+ cell_type_id : Option < ValueTypeId > ,
1053
+ visited_set : & mut FxHashSet < TaskId > ,
1054
+ ) -> DebugTraceTransientTask {
1055
+ let task_name = task_type. get_name ( ) ;
1056
+
1057
+ let cause_self = task_type. this . and_then ( |cause_self_raw_vc| {
1058
+ let task_id = cause_self_raw_vc. get_task_id ( ) ;
1059
+ if task_id. is_transient ( ) {
1060
+ Some ( Box :: new ( inner_id (
1061
+ backend,
1062
+ cause_self_raw_vc. get_task_id ( ) ,
1063
+ cause_self_raw_vc. try_get_type_id ( ) ,
1064
+ visited_set,
1065
+ ) ) )
1066
+ } else {
1067
+ None
1068
+ }
1069
+ } ) ;
1070
+ let cause_args = task_type
1071
+ . arg
1072
+ . get_raw_vcs ( )
1073
+ . into_iter ( )
1074
+ . map ( |raw_vc| ( raw_vc. get_task_id ( ) , raw_vc. try_get_type_id ( ) ) )
1075
+ . filter ( |( task_id, _) | task_id. is_transient ( ) )
1076
+ . collect :: < IndexSet < _ > > ( ) // dedupe
1077
+ . into_iter ( )
1078
+ . map ( |( task_id, cell_type_id) | {
1079
+ inner_id ( backend, task_id, cell_type_id, visited_set)
1080
+ } )
1081
+ . collect ( ) ;
1082
+
1083
+ DebugTraceTransientTask :: Cached {
1084
+ task_name,
1085
+ cell_type_id,
1086
+ cause_self,
1087
+ cause_args,
1088
+ }
1089
+ }
1090
+ inner_cached (
1091
+ self ,
1092
+ task_type,
1093
+ cell_id. map ( |c| c. type_id ) ,
1094
+ & mut FxHashSet :: default ( ) ,
1095
+ )
1096
+ }
1097
+
1016
1098
fn invalidate_task (
1017
1099
& self ,
1018
1100
task_id : TaskId ,
@@ -1987,14 +2069,42 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
1987
2069
}
1988
2070
}
1989
2071
1990
- fn assert_not_persistent_calling_transient ( & self , parent_id : TaskId , child_id : TaskId ) {
2072
+ fn assert_not_persistent_calling_transient (
2073
+ & self ,
2074
+ parent_id : TaskId ,
2075
+ child_id : TaskId ,
2076
+ cell_id : Option < CellId > ,
2077
+ ) {
1991
2078
if !parent_id. is_transient ( ) && child_id. is_transient ( ) {
1992
- panic_persistent_calling_transient (
2079
+ self . panic_persistent_calling_transient (
1993
2080
self . lookup_task_type ( parent_id) . as_deref ( ) ,
1994
2081
self . lookup_task_type ( child_id) . as_deref ( ) ,
2082
+ cell_id,
1995
2083
) ;
1996
2084
}
1997
2085
}
2086
+
2087
+ fn panic_persistent_calling_transient (
2088
+ & self ,
2089
+ parent : Option < & CachedTaskType > ,
2090
+ child : Option < & CachedTaskType > ,
2091
+ cell_id : Option < CellId > ,
2092
+ ) {
2093
+ let transient_reason = if let Some ( child) = child {
2094
+ format ! (
2095
+ " The callee is transient because it depends on:\n {}" ,
2096
+ self . debug_trace_transient_task( child, cell_id) ,
2097
+ )
2098
+ } else {
2099
+ String :: new ( )
2100
+ } ;
2101
+ panic ! (
2102
+ "Persistent task {} is not allowed to call or read transient tasks {}.{}" ,
2103
+ parent. map_or( "unknown" , |t| t. get_name( ) ) ,
2104
+ child. map_or( "unknown" , |t| t. get_name( ) ) ,
2105
+ transient_reason,
2106
+ ) ;
2107
+ }
1998
2108
}
1999
2109
2000
2110
impl < B : BackingStorage > Backend for TurboTasksBackend < B > {
@@ -2272,15 +2382,90 @@ impl<B: BackingStorage> Backend for TurboTasksBackend<B> {
2272
2382
}
2273
2383
}
2274
2384
2275
- fn panic_persistent_calling_transient (
2276
- parent : Option < & CachedTaskType > ,
2277
- child : Option < & CachedTaskType > ,
2278
- ) {
2279
- panic ! (
2280
- "Persistent task {} is not allowed to call or read transient tasks {}" ,
2281
- parent. map_or( "unknown" , |t| t. get_name( ) ) ,
2282
- child. map_or( "unknown" , |t| t. get_name( ) ) ,
2283
- ) ;
2385
+ enum DebugTraceTransientTask {
2386
+ Cached {
2387
+ task_name : & ' static str ,
2388
+ cell_type_id : Option < ValueTypeId > ,
2389
+ cause_self : Option < Box < DebugTraceTransientTask > > ,
2390
+ cause_args : Vec < DebugTraceTransientTask > ,
2391
+ } ,
2392
+ /// This representation is used when this task is a duplicate of one previously shown
2393
+ Collapsed {
2394
+ task_name : & ' static str ,
2395
+ cell_type_id : Option < ValueTypeId > ,
2396
+ } ,
2397
+ Uncached {
2398
+ cell_type_id : Option < ValueTypeId > ,
2399
+ } ,
2400
+ }
2401
+
2402
+ impl DebugTraceTransientTask {
2403
+ fn fmt_indented ( & self , f : & mut fmt:: Formatter < ' _ > , level : usize ) -> fmt:: Result {
2404
+ let indent = " " . repeat ( level) ;
2405
+ f. write_str ( & indent) ?;
2406
+
2407
+ fn fmt_cell_type_id (
2408
+ f : & mut fmt:: Formatter < ' _ > ,
2409
+ cell_type_id : Option < ValueTypeId > ,
2410
+ ) -> fmt:: Result {
2411
+ if let Some ( ty) = cell_type_id {
2412
+ write ! ( f, " (read cell of type {})" , get_value_type_global_name( ty) )
2413
+ } else {
2414
+ Ok ( ( ) )
2415
+ }
2416
+ }
2417
+
2418
+ // write the name and type
2419
+ match self {
2420
+ Self :: Cached {
2421
+ task_name,
2422
+ cell_type_id,
2423
+ ..
2424
+ }
2425
+ | Self :: Collapsed {
2426
+ task_name,
2427
+ cell_type_id,
2428
+ ..
2429
+ } => {
2430
+ f. write_str ( task_name) ?;
2431
+ fmt_cell_type_id ( f, * cell_type_id) ?;
2432
+ if matches ! ( self , Self :: Collapsed { .. } ) {
2433
+ f. write_str ( " (collapsed)" ) ?;
2434
+ }
2435
+ }
2436
+ Self :: Uncached { cell_type_id } => {
2437
+ f. write_str ( "unknown transient task" ) ?;
2438
+ fmt_cell_type_id ( f, * cell_type_id) ?;
2439
+ }
2440
+ }
2441
+ f. write_char ( '\n' ) ?;
2442
+
2443
+ // write any extra "cause" information we might have
2444
+ if let Self :: Cached {
2445
+ cause_self,
2446
+ cause_args,
2447
+ ..
2448
+ } = self
2449
+ {
2450
+ if let Some ( c) = cause_self {
2451
+ writeln ! ( f, "{indent} self:" ) ?;
2452
+ c. fmt_indented ( f, level + 1 ) ?;
2453
+ }
2454
+ if !cause_args. is_empty ( ) {
2455
+ writeln ! ( f, "{indent} args:" ) ?;
2456
+ for c in cause_args {
2457
+ c. fmt_indented ( f, level + 1 ) ?;
2458
+ }
2459
+ }
2460
+ }
2461
+ Ok ( ( ) )
2462
+ }
2463
+ }
2464
+
2465
+ impl fmt:: Display for DebugTraceTransientTask {
2466
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
2467
+ self . fmt_indented ( f, 0 )
2468
+ }
2284
2469
}
2285
2470
2286
2471
// from https://github.com/tokio-rs/tokio/blob/29cd6ec1ec6f90a7ee1ad641c03e0e00badbcb0e/tokio/src/time/instant.rs#L57-L63
0 commit comments