@@ -52,6 +52,52 @@ impl Generation {
52
52
}
53
53
}
54
54
55
+ ///
56
+ /// A result from running a Node.
57
+ ///
58
+ /// If the value is Dirty, the consumer should check whether the dependencies of the Node have the
59
+ /// same values as they did when this Node was last run; if so, the value can be re-used
60
+ /// (and should be marked "Clean").
61
+ ///
62
+ /// If the value is Clean, the consumer can simply use the value as-is.
63
+ ///
64
+ #[ derive( Clone , Debug ) ]
65
+ pub ( crate ) enum EntryResult < N : Node > {
66
+ Clean ( Result < N :: Item , N :: Error > ) ,
67
+ Dirty ( Result < N :: Item , N :: Error > ) ,
68
+ }
69
+
70
+ impl < N : Node > EntryResult < N > {
71
+ fn is_dirty ( & self ) -> bool {
72
+ if let EntryResult :: Dirty ( ..) = self {
73
+ true
74
+ } else {
75
+ false
76
+ }
77
+ }
78
+
79
+ fn dirty ( & mut self ) {
80
+ if let EntryResult :: Clean ( value) = self {
81
+ * self = EntryResult :: Dirty ( value. clone ( ) )
82
+ }
83
+ }
84
+
85
+ fn clean ( & mut self ) {
86
+ if let EntryResult :: Dirty ( value) = self {
87
+ * self = EntryResult :: Clean ( value. clone ( ) )
88
+ }
89
+ }
90
+ }
91
+
92
+ impl < N : Node > AsRef < Result < N :: Item , N :: Error > > for EntryResult < N > {
93
+ fn as_ref ( & self ) -> & Result < N :: Item , N :: Error > {
94
+ match self {
95
+ EntryResult :: Clean ( v) => v,
96
+ EntryResult :: Dirty ( v) => v,
97
+ }
98
+ }
99
+ }
100
+
55
101
#[ allow( clippy:: type_complexity) ]
56
102
#[ derive( Debug ) ]
57
103
pub ( crate ) enum EntryState < N : Node > {
@@ -65,7 +111,7 @@ pub(crate) enum EntryState<N: Node> {
65
111
NotStarted {
66
112
run_token : RunToken ,
67
113
generation : Generation ,
68
- previous_result : Option < Result < N :: Item , N :: Error > > ,
114
+ previous_result : Option < EntryResult < N > > ,
69
115
} ,
70
116
// A node that is running. A running node that has been marked dirty re-runs rather than
71
117
// completing.
@@ -76,7 +122,7 @@ pub(crate) enum EntryState<N: Node> {
76
122
generation : Generation ,
77
123
start_time : Instant ,
78
124
waiters : Vec < oneshot:: Sender < Result < ( N :: Item , Generation ) , N :: Error > > > ,
79
- previous_result : Option < Result < N :: Item , N :: Error > > ,
125
+ previous_result : Option < EntryResult < N > > ,
80
126
dirty : bool ,
81
127
} ,
82
128
// A node that has completed, and then possibly been marked dirty. Because marking a node
@@ -85,9 +131,8 @@ pub(crate) enum EntryState<N: Node> {
85
131
Completed {
86
132
run_token : RunToken ,
87
133
generation : Generation ,
88
- result : Result < N :: Item , N :: Error > ,
134
+ result : EntryResult < N > ,
89
135
dep_generations : Vec < Generation > ,
90
- dirty : bool ,
91
136
} ,
92
137
}
93
138
@@ -158,8 +203,9 @@ impl<N: Node> Entry<N> {
158
203
let state = self . state . lock ( ) ;
159
204
match * state {
160
205
EntryState :: Completed {
161
- ref result, dirty, ..
162
- } if !dirty => Some ( result. clone ( ) ) ,
206
+ result : EntryResult :: Clean ( ref result) ,
207
+ ..
208
+ } => Some ( result. clone ( ) ) ,
163
209
_ => None ,
164
210
}
165
211
}
@@ -175,7 +221,7 @@ impl<N: Node> Entry<N> {
175
221
run_token : RunToken ,
176
222
generation : Generation ,
177
223
previous_dep_generations : Option < Vec < Generation > > ,
178
- previous_result : Option < Result < N :: Item , N :: Error > > ,
224
+ previous_result : Option < EntryResult < N > > ,
179
225
) -> EntryState < N >
180
226
where
181
227
C : NodeContext < Node = N > ,
@@ -243,16 +289,15 @@ impl<N: Node> Entry<N> {
243
289
start_time : Instant :: now ( ) ,
244
290
run_token,
245
291
generation,
246
- previous_result : previous_result ,
292
+ previous_result,
247
293
dirty : false ,
248
294
}
249
295
}
250
296
& EntryKey :: Cyclic ( _) => EntryState :: Completed {
251
- result : Err ( N :: Error :: cyclic ( ) ) ,
297
+ result : EntryResult :: Clean ( Err ( N :: Error :: cyclic ( ) ) ) ,
252
298
dep_generations : Vec :: new ( ) ,
253
299
run_token,
254
300
generation,
255
- dirty : false ,
256
301
} ,
257
302
}
258
303
}
@@ -293,10 +338,9 @@ impl<N: Node> Entry<N> {
293
338
& mut EntryState :: Completed {
294
339
ref result,
295
340
generation,
296
- dirty,
297
341
..
298
- } if !dirty && self . node . content ( ) . cacheable ( ) => {
299
- return future:: result ( result. clone ( ) )
342
+ } if self . node . content ( ) . cacheable ( ) && !result . is_dirty ( ) => {
343
+ return future:: result ( result. as_ref ( ) . clone ( ) )
300
344
. map ( move |res| ( res, generation) )
301
345
. to_boxed ( ) ;
302
346
}
@@ -323,21 +367,21 @@ impl<N: Node> Entry<N> {
323
367
EntryState :: Completed {
324
368
run_token,
325
369
generation,
326
- result,
370
+ mut result,
327
371
dep_generations,
328
- dirty,
329
372
} => {
330
- assert ! (
331
- dirty || !self . node. content( ) . cacheable( ) ,
332
- "A clean Node should not reach this point: {:?}" ,
333
- result
334
- ) ;
335
373
trace ! (
336
- "Re-starting node {:?}. It was: dirty={ }, cacheable={}" ,
374
+ "Re-starting node {:?}. It was: previous_result={:? }, cacheable={}" ,
337
375
self . node,
338
- dirty ,
376
+ result ,
339
377
self . node. content( ) . cacheable( )
340
378
) ;
379
+ assert ! (
380
+ result. is_dirty( ) || !self . node. content( ) . cacheable( ) ,
381
+ "A clean Node should not reach this point: {:?}" ,
382
+ result
383
+ ) ;
384
+ result. dirty ( ) ;
341
385
// The Node has already completed but is now marked dirty. This indicates that we are the
342
386
// first caller to request it since it was marked dirty. We attempt to clean it (which will
343
387
// cause it to re-run if the dep_generations mismatch).
@@ -416,7 +460,7 @@ impl<N: Node> Entry<N> {
416
460
waiters,
417
461
run_token,
418
462
generation,
419
- previous_result,
463
+ mut previous_result,
420
464
dirty,
421
465
..
422
466
} => {
@@ -428,6 +472,9 @@ impl<N: Node> Entry<N> {
428
472
"Not completing node {:?} because it was invalidated before completing." ,
429
473
self . node
430
474
) ;
475
+ if let Some ( previous_result) = previous_result. as_mut ( ) {
476
+ previous_result. dirty ( ) ;
477
+ }
431
478
EntryState :: NotStarted {
432
479
run_token : run_token. next ( ) ,
433
480
generation,
@@ -440,6 +487,9 @@ impl<N: Node> Entry<N> {
440
487
"Not completing node {:?} because it was dirtied before completing." ,
441
488
self . node
442
489
) ;
490
+ if let Some ( previous_result) = previous_result. as_mut ( ) {
491
+ previous_result. dirty ( ) ;
492
+ }
443
493
Self :: run (
444
494
context,
445
495
& self . node ,
@@ -452,19 +502,19 @@ impl<N: Node> Entry<N> {
452
502
} else {
453
503
// If the new result does not match the previous result, the generation increments.
454
504
let ( generation, next_result) = if let Some ( result) = result {
455
- if Some ( & result) == previous_result. as_ref ( ) {
505
+ if Some ( & result) == previous_result. as_ref ( ) . map ( EntryResult :: as_ref ) {
456
506
// Node was re-executed, but had the same result value.
457
- ( generation, result)
507
+ ( generation, EntryResult :: Clean ( result) )
458
508
} else {
459
- ( generation. next ( ) , result)
509
+ ( generation. next ( ) , EntryResult :: Clean ( result) )
460
510
}
461
511
} else {
462
512
// Node was marked clean.
463
513
// NB: The `expect` here avoids a clone and a comparison: see the method docs.
464
- (
465
- generation ,
466
- previous_result . expect ( "A Node cannot be marked clean without a previous result." ) ,
467
- )
514
+ let mut result =
515
+ previous_result . expect ( "A Node cannot be marked clean without a previous result." ) ;
516
+ result. clean ( ) ;
517
+ ( generation , result )
468
518
} ;
469
519
// Notify all waiters (ignoring any that have gone away), and then store the value.
470
520
// A waiter will go away whenever they drop the `Future` `Receiver` of the value, perhaps
@@ -476,14 +526,13 @@ impl<N: Node> Entry<N> {
476
526
waiters. len( )
477
527
) ;
478
528
for waiter in waiters {
479
- let _ = waiter. send ( next_result. clone ( ) . map ( |res| ( res, generation) ) ) ;
529
+ let _ = waiter. send ( next_result. as_ref ( ) . clone ( ) . map ( |res| ( res, generation) ) ) ;
480
530
}
481
531
EntryState :: Completed {
482
532
result : next_result,
483
533
dep_generations,
484
534
run_token,
485
535
generation,
486
- dirty : false ,
487
536
}
488
537
}
489
538
}
@@ -541,15 +590,23 @@ impl<N: Node> Entry<N> {
541
590
///
542
591
/// Clears the state of this Node, forcing it to be recomputed.
543
592
///
544
- pub ( crate ) fn clear ( & mut self ) {
593
+ /// # Arguments
594
+ ///
595
+ /// * `graph_still_contains_edges` - If the caller has guaranteed that all edges from this Node
596
+ /// have been removed from the graph, they should pass false here, else true. We may want to
597
+ /// remove this parameter, and force this method to remove the edges, but that would require
598
+ /// acquiring the graph lock here, which we currently don't do.
599
+ ///
600
+ pub ( crate ) fn clear ( & mut self , graph_still_contains_edges : bool ) {
545
601
let mut state = self . state . lock ( ) ;
546
602
547
- let ( run_token, generation, previous_result) =
603
+ let ( run_token, generation, mut previous_result) =
548
604
match mem:: replace ( & mut * state, EntryState :: initial ( ) ) {
549
605
EntryState :: NotStarted {
550
606
run_token,
551
607
generation,
552
608
previous_result,
609
+ ..
553
610
}
554
611
| EntryState :: Running {
555
612
run_token,
@@ -567,6 +624,12 @@ impl<N: Node> Entry<N> {
567
624
568
625
trace ! ( "Clearing node {:?}" , self . node) ;
569
626
627
+ if graph_still_contains_edges {
628
+ if let Some ( previous_result) = previous_result. as_mut ( ) {
629
+ previous_result. dirty ( ) ;
630
+ }
631
+ }
632
+
570
633
// Swap in a state with a new RunToken value, which invalidates any outstanding work.
571
634
* state = EntryState :: NotStarted {
572
635
run_token : run_token. next ( ) ,
@@ -585,15 +648,36 @@ impl<N: Node> Entry<N> {
585
648
let state = & mut * self . state . lock ( ) ;
586
649
trace ! ( "Dirtying node {:?}" , self . node) ;
587
650
match state {
588
- & mut EntryState :: Running { ref mut dirty, .. }
589
- | & mut EntryState :: Completed { ref mut dirty, .. } => {
590
- // Mark dirty.
651
+ & mut EntryState :: Running { ref mut dirty, .. } => {
591
652
* dirty = true ;
592
653
}
654
+ & mut EntryState :: Completed { ref mut result, .. } => {
655
+ result. dirty ( ) ;
656
+ }
593
657
& mut EntryState :: NotStarted { .. } => { }
594
658
}
595
659
}
596
660
661
+ pub fn may_have_dirty_edges ( & self ) -> bool {
662
+ match * self . state . lock ( ) {
663
+ EntryState :: NotStarted {
664
+ ref previous_result,
665
+ ..
666
+ }
667
+ | EntryState :: Running {
668
+ ref previous_result,
669
+ ..
670
+ } => {
671
+ if let Some ( EntryResult :: Dirty ( ..) ) = previous_result {
672
+ true
673
+ } else {
674
+ false
675
+ }
676
+ }
677
+ EntryState :: Completed { ref result, .. } => result. is_dirty ( ) ,
678
+ }
679
+ }
680
+
597
681
pub ( crate ) fn format ( & self ) -> String {
598
682
let state = match self . peek ( ) {
599
683
Some ( Ok ( ref nr) ) => format ! ( "{:?}" , nr) ,
0 commit comments