@@ -2,7 +2,7 @@ pub mod scheduler;
22pub mod types;
33
44use std:: { collections:: HashMap , sync:: atomic:: AtomicUsize } ;
5- use tracing:: error;
5+ use tracing:: { error, warn } ;
66
77use crate :: dfg:: types:: * ;
88use anyhow:: Result ;
@@ -305,7 +305,12 @@ impl DFComponentGraph {
305305 for ( consumer, tx) in self . graph . node_references ( ) {
306306 for i in tx. inputs . keys ( ) {
307307 if let Some ( producer) = self . allowed_map . get ( i) {
308- dependence_pairs. push ( ( producer, consumer) ) ;
308+ if * producer == consumer {
309+ warn ! ( target: "scheduler" , { } ,
310+ "Self-dependence on node" ) ;
311+ } else {
312+ dependence_pairs. push ( ( * producer, consumer) ) ;
313+ }
309314 } else {
310315 self . needed_map
311316 . entry ( i. clone ( ) )
@@ -314,14 +319,72 @@ impl DFComponentGraph {
314319 }
315320 }
316321 }
322+
323+ // We build a replica of the graph and map it to the
324+ // underlying DiGraph so we can identify cycles.
325+ let mut digraph = self . graph . map ( |idx, _| idx, |_, _| ( ) ) . graph ( ) . clone ( ) ;
317326 // Add transaction dependence edges
318- for ( producer, consumer) in dependence_pairs {
319- // Error only occurs in case of cyclic dependence which
320- // shoud not be possible between transactions. In that
321- // case, the whole cycle should be put in an error state.
322- self . graph
323- . add_edge ( * producer, consumer, ( ) )
324- . map_err ( |_| SchedulerError :: CyclicDependence ) ?;
327+ for ( producer, consumer) in dependence_pairs. iter ( ) {
328+ digraph. add_edge ( * producer, * consumer, ( ) ) ;
329+ }
330+ let mut tarjan = daggy:: petgraph:: algo:: TarjanScc :: new ( ) ;
331+ let mut sccs = Vec :: new ( ) ;
332+ tarjan. run ( & digraph, |scc| {
333+ if scc. len ( ) > 1 {
334+ // All non-singleton SCCs in a directed graph are
335+ // dependence cycles
336+ sccs. push ( scc. to_vec ( ) ) ;
337+ }
338+ } ) ;
339+ if !sccs. is_empty ( ) {
340+ for scc in sccs {
341+ error ! ( target: "scheduler" , { cycle_size = ?scc. len( ) } ,
342+ "Dependence cycle detected" ) ;
343+ for idx in scc {
344+ let idx = digraph
345+ . node_weight ( idx)
346+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
347+ let tx = self
348+ . graph
349+ . node_weight_mut ( * idx)
350+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
351+ // Mark the node as uncomputable so we don't go
352+ // and mark as completed operations that are in
353+ // error.
354+ tx. is_uncomputable = true ;
355+ error ! ( target: "scheduler" , { transaction_id = ?hex:: encode( tx. transaction_id. clone( ) ) } ,
356+ "Transaction is part of a dependence cycle" ) ;
357+ for ( _, op) in tx. graph . graph . node_references ( ) {
358+ self . results . push ( DFGTxResult {
359+ transaction_id : tx. transaction_id . clone ( ) ,
360+ handle : op. result_handle . to_vec ( ) ,
361+ compressed_ct : Err ( SchedulerError :: CyclicDependence . into ( ) ) ,
362+ } ) ;
363+ }
364+ }
365+ }
366+ return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
367+ } else {
368+ // If no dependence cycles were found, then we can
369+ // complete the graph and proceed to execution
370+ for ( producer, consumer) in dependence_pairs. iter ( ) {
371+ // The error case here should not happen as we've
372+ // already covered it by testing for SCCs in the graph
373+ // first
374+ if self . graph . add_edge ( * producer, * consumer, ( ) ) . is_err ( ) {
375+ let prod = self
376+ . graph
377+ . node_weight ( * producer)
378+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
379+ let cons = self
380+ . graph
381+ . node_weight ( * consumer)
382+ . ok_or ( SchedulerError :: DataflowGraphError ) ?;
383+ error ! ( target: "scheduler" , { producer_id = ?hex:: encode( prod. transaction_id. clone( ) ) , consumer_id = ?hex:: encode( cons. transaction_id. clone( ) ) } ,
384+ "Dependence cycle when adding dependence - initial cycle detection failed" ) ;
385+ return Err ( SchedulerError :: CyclicDependence . into ( ) ) ;
386+ }
387+ }
325388 }
326389 Ok ( ( ) )
327390 }
@@ -402,7 +465,17 @@ impl DFComponentGraph {
402465 . graph
403466 . node_weight_mut ( tx_node_index)
404467 . ok_or ( SchedulerError :: DataflowGraphError ) ?;
468+ if tx_node. is_uncomputable {
469+ return Ok ( ( ) ) ;
470+ }
405471 tx_node. is_uncomputable = true ;
472+ for ( _idx, op) in tx_node. graph . graph . node_references ( ) {
473+ self . results . push ( DFGTxResult {
474+ transaction_id : tx_node. transaction_id . clone ( ) ,
475+ handle : op. result_handle . to_vec ( ) ,
476+ compressed_ct : Err ( SchedulerError :: MissingInputs . into ( ) ) ,
477+ } ) ;
478+ }
406479 for edge in edges. edges_directed ( tx_node_index, Direction :: Outgoing ) {
407480 let dependent_tx_index = edge. target ( ) ;
408481 self . set_uncomputable ( dependent_tx_index, edges) ?;
0 commit comments