@@ -56,18 +56,52 @@ extension Progress {
5656 /// - reporter: A `ProgressReporter` instance.
5757 /// - count: Number of units delegated from `self`'s `totalCount`.
5858 public func addChild( _ reporter: ProgressReporter , withPendingUnitCount count: Int ) {
59-
59+
60+ // Need to detect cycle here
61+ precondition ( self . isCycle ( reporter: reporter) == false , " Creating a cycle is not allowed. " )
62+
6063 // Make intermediary & add it to NSProgress parent's children list
6164 let ghostProgressParent = Progress ( totalUnitCount: Int64 ( reporter. manager. totalCount ?? 0 ) )
6265 ghostProgressParent. completedUnitCount = Int64 ( reporter. manager. completedCount)
6366 self . addChild ( ghostProgressParent, withPendingUnitCount: Int64 ( count) )
6467
6568 // Make observation instance
6669 let observation = _ProgressParentProgressReporterChild ( intermediary: ghostProgressParent, reporter: reporter)
67-
70+
6871 reporter. manager. setInteropObservationForMonitor ( observation: observation)
6972 reporter. manager. setMonitorInterop ( to: true )
7073 }
74+
75+ // MARK: Cycle detection
76+ func isCycle( reporter: ProgressReporter , visited: Set < ProgressManager > = [ ] ) -> Bool {
77+ if self . _parent ( ) == nil {
78+ return false
79+ }
80+
81+ if !( self . _parent ( ) is _NSProgressParentBridge ) {
82+ return self . _parent ( ) . isCycle ( reporter: reporter)
83+ }
84+
85+ // then check against ProgressManager
86+ let unwrappedParent = ( self . _parent ( ) as? _NSProgressParentBridge ) ? . actualParent
87+ if let unwrappedParent = unwrappedParent {
88+ if unwrappedParent === reporter. manager {
89+ return true
90+ }
91+ let updatedVisited = visited. union ( [ unwrappedParent] )
92+ return unwrappedParent. parents. withLock { parents in
93+ for (parent, _) in parents {
94+ if !updatedVisited. contains ( parent) {
95+ if parent. isCycle ( reporter: reporter, visited: updatedVisited) {
96+ return true
97+ }
98+ }
99+ }
100+ return false
101+ }
102+ }
103+ return false
104+ }
71105}
72106
73107private final class _ProgressParentProgressManagerChild : Sendable {
@@ -152,7 +186,7 @@ extension ProgressManager {
152186// Subclass of Foundation.Progress
153187internal final class _NSProgressParentBridge : Progress , @unchecked Sendable {
154188
155- let actualParent : ProgressManager
189+ internal let actualParent : ProgressManager
156190
157191 init ( managerParent: ProgressManager ) {
158192 self . actualParent = managerParent
0 commit comments