@@ -53,7 +53,16 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
5353 var otherProperties : [ AnyMetatypeWrapper : ( any Sendable ) ]
5454 // Type: Metatype maps to dictionary of child to value
5555 var childrenOtherProperties : [ AnyMetatypeWrapper : OrderedDictionary < ProgressManager , [ ( any Sendable ) ] > ]
56- var isDirty : Bool
56+
57+ // dirty bit for completed count
58+ var isDirtyCompleted : Bool
59+ var updatedCompletedCount : Int
60+
61+ // dirty bit for total count
62+ var isDirtyTotal : Bool
63+ var updatedTotalCount : Int ?
64+
65+ var children : Set < ProgressManager >
5766 }
5867
5968 // Interop states
@@ -78,7 +87,10 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
7887 public var totalCount : Int ? {
7988 _ $observationRegistrar. access ( self , keyPath: \. totalCount)
8089 return state. withLock { state in
81- getTotalCount ( fractionState: & state. fractionState)
90+ if state. isDirtyTotal {
91+ updateDirtiedValues ( state: & state)
92+ }
93+ return getTotalCount ( fractionState: & state. fractionState)
8294 }
8395 }
8496
@@ -87,7 +99,10 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
8799 public var completedCount : Int {
88100 _ $observationRegistrar. access ( self , keyPath: \. completedCount)
89101 return state. withLock { state in
90- getCompletedCount ( fractionState: & state. fractionState)
102+ if state. isDirtyCompleted {
103+ updateDirtiedValues ( state: & state)
104+ }
105+ return getCompletedCount ( fractionState: & state. fractionState)
91106 }
92107 }
93108
@@ -97,7 +112,10 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
97112 public var fractionCompleted : Double {
98113 _ $observationRegistrar. access ( self , keyPath: \. fractionCompleted)
99114 return state. withLock { state in
100- getFractionCompleted ( fractionState: & state. fractionState)
115+ if state. isDirtyTotal || state. isDirtyCompleted {
116+ updateDirtiedValues ( state: & state)
117+ }
118+ return getFractionCompleted ( fractionState: & state. fractionState)
101119 }
102120 }
103121
@@ -106,7 +124,12 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
106124 public var isIndeterminate : Bool {
107125 _ $observationRegistrar. access ( self , keyPath: \. isIndeterminate)
108126 return state. withLock { state in
109- getIsIndeterminate ( fractionState: & state. fractionState)
127+ if state. isDirtyTotal {
128+ if state. isDirtyTotal || state. isDirtyCompleted {
129+ updateDirtiedValues ( state: & state)
130+ }
131+ }
132+ return getIsIndeterminate ( fractionState: & state. fractionState)
110133 }
111134 }
112135
@@ -115,7 +138,47 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
115138 public var isFinished : Bool {
116139 _ $observationRegistrar. access ( self , keyPath: \. isFinished)
117140 return state. withLock { state in
118- getIsFinished ( fractionState: & state. fractionState)
141+ if state. isDirtyTotal || state. isDirtyCompleted {
142+ updateDirtiedValues ( state: & state)
143+ }
144+ return getIsFinished ( fractionState: & state. fractionState)
145+ }
146+ }
147+
148+ /// This is called if the total dirty bit is set
149+ private func updateDirtiedValues( state: inout State ) {
150+ if state. children. isEmpty {
151+ // If there are no children, update value directly and don't traverse down anymore
152+ let updateState = getPreviousAndCurrentState ( state: & state)
153+ updateFractionCompleted ( from: updateState. previous, to: updateState. current)
154+
155+ // mark dirty bit false
156+ state. isDirtyTotal = false
157+ state. isDirtyCompleted = false
158+ } else {
159+ // If there are children, traverse down
160+ for child in state. children {
161+ child. updateDirtiedValues ( )
162+ }
163+ }
164+ }
165+
166+ private func updateDirtiedValues( ) {
167+ state. withLock { state in
168+ if state. children. isEmpty {
169+ // If there are no children, update value directly and don't traverse down anymore
170+ let updateState = getPreviousAndCurrentState ( state: & state)
171+ updateFractionCompleted ( from: updateState. previous, to: updateState. current)
172+
173+ // mark dirty bit false
174+ state. isDirtyTotal = false
175+ state. isDirtyCompleted = false
176+ } else {
177+ // If there are children, traverse down
178+ for child in state. children {
179+ child. updateDirtiedValues ( )
180+ }
181+ }
119182 }
120183 }
121184
@@ -141,26 +204,16 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
141204 /// The total units of work.
142205 public var totalCount : Int ? {
143206 mutating get {
144- manager. getTotalCount ( fractionState: & state. fractionState)
207+ if state. isDirtyTotal {
208+ manager. updateDirtiedValues ( state: & state)
209+ }
210+ return manager. getTotalCount ( fractionState: & state. fractionState)
145211 }
146212
147213 set {
148- // let previous = state.fractionState.overallFraction
149- if state. fractionState. selfFraction. total != newValue && state. fractionState. selfFraction. total > 0 {
150- state. fractionState. childFraction = state. fractionState. childFraction * _ProgressFraction( completed: state. fractionState. selfFraction. total, total: newValue ?? 1 )
151- }
152- state. fractionState. selfFraction. total = newValue ?? 0
153-
154- // if newValue is nil, reset indeterminate to true
155- if newValue != nil {
156- state. fractionState. indeterminate = false
157- } else {
158- state. fractionState. indeterminate = true
159- }
160- //TODO: rdar://149015734 Check throttling
161- manager. markDirty ( )
162- // manager.updateFractionCompleted(from: previous, to: state.fractionState.overallFraction)
163-
214+ state. updatedTotalCount = newValue
215+ manager. markTotalDirty ( state: & state)
216+
164217 // Interop updates stuff
165218 manager. ghostReporter? . notifyObservers ( with: . totalCountUpdated)
166219 manager. monitorInterop. withLock { [ manager] interop in
@@ -175,16 +228,17 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
175228 /// The completed units of work.
176229 public var completedCount : Int {
177230 mutating get {
178- manager. getCompletedCount ( fractionState: & state. fractionState)
231+ if state. isDirtyCompleted {
232+ manager. updateDirtiedValues ( state: & state)
233+ }
234+ return manager. getCompletedCount ( fractionState: & state. fractionState)
179235 }
180236
181237 set {
182- // let prev = state.fractionState.overallFraction
183- state. fractionState. selfFraction. completed = newValue
184- manager. markDirty ( )
185- // manager.updateFractionCompleted(from: prev, to: state.fractionState.overallFraction)
238+ state. updatedCompletedCount = newValue
239+ manager. markCompletedDirty ( state: & state)
240+
186241 manager. ghostReporter? . notifyObservers ( with: . fractionUpdated)
187-
188242 manager. monitorInterop. withLock { [ manager] interop in
189243 if interop == true {
190244 manager. notifyObservers ( with: . fractionUpdated)
@@ -229,19 +283,24 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
229283 }
230284
231285 internal let parents : LockedState < [ ProgressManager : Int ] >
232- private let children : LockedState < Set < ProgressManager > >
233286 private let state : LockedState < State >
234287
235288 internal init ( total: Int ? , ghostReporter: ProgressManager ? , interopObservation: ( any Sendable ) ? ) {
236289 self . parents = . init( initialState: [ : ] )
237- self . children = . init( initialState: Set ( ) )
238290 let fractionState = FractionState (
239291 indeterminate: total == nil ? true : false ,
240292 selfFraction: _ProgressFraction ( completed: 0 , total: total ?? 0 ) ,
241293 childFraction: _ProgressFraction ( completed: 0 , total: 1 ) ,
242294 interopChild: nil
243295 )
244- let state = State ( fractionState: fractionState, otherProperties: [ : ] , childrenOtherProperties: [ : ] , isDirty: true )
296+ let state = State ( fractionState: fractionState,
297+ otherProperties: [ : ] ,
298+ childrenOtherProperties: [ : ] ,
299+ isDirtyCompleted: false ,
300+ updatedCompletedCount: 0 ,
301+ isDirtyTotal: false ,
302+ updatedTotalCount: 0 ,
303+ children: Set ( ) )
245304 self . state = LockedState ( initialState: state)
246305 self . interopObservation = interopObservation
247306 self . ghostReporter = ghostReporter
@@ -255,34 +314,6 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
255314 self . init ( total: totalCount, ghostReporter: nil , interopObservation: nil )
256315 }
257316
258- // /// Sets `totalCount`.
259- // /// - Parameter newTotal: Total units of work.
260- // public func setTotalCount(_ newTotal: Int?) {
261- // state.withLock { state in
262- // let previous = state.fractionState.overallFraction
263- // if state.fractionState.selfFraction.total != newTotal && state.fractionState.selfFraction.total > 0 {
264- // state.fractionState.childFraction = state.fractionState.childFraction * _ProgressFraction(completed: state.fractionState.selfFraction.total, total: newTotal ?? 1)
265- // }
266- // state.fractionState.selfFraction.total = newTotal ?? 0
267- //
268- // // if newValue is nil, reset indeterminate to true
269- // if newTotal != nil {
270- // state.fractionState.indeterminate = false
271- // } else {
272- // state.fractionState.indeterminate = true
273- // }
274- // updateFractionCompleted(from: previous, to: state.fractionState.overallFraction)
275- //
276- // ghostReporter?.notifyObservers(with: .totalCountUpdated)
277- //
278- // monitorInterop.withLock { [self] interop in
279- // if interop == true {
280- // notifyObservers(with: .totalCountUpdated)
281- // }
282- // }
283- // }
284- // }
285-
286317 /// Returns a `Subprogress` representing a portion of `self` which can be passed to any method that reports progress.
287318 ///
288319 /// - Parameter count: Units, which is a portion of `totalCount`delegated to an instance of `Subprogress`.
@@ -313,11 +344,9 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
313344 /// - Parameter count: Units of work.
314345 public func complete( count: Int ) {
315346 state. withLock { state in
316- state. fractionState. selfFraction. completed += count
347+ state. updatedCompletedCount = state. fractionState. selfFraction. completed + count
348+ markCompletedDirty ( state: & state)
317349 }
318- markDirty ( )
319- // let updateState = updateCompletedCount(count: count)
320- // updateFractionCompleted(from: updateState.previous, to: updateState.current)
321350
322351 // Interop updates stuff
323352 ghostReporter? . notifyObservers ( with: . fractionUpdated)
@@ -353,7 +382,16 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
353382 return try state. withLock { state in
354383 var values = Values ( manager: self , state: state)
355384 // This is done to avoid copy on write later
356- state = State ( fractionState: FractionState ( indeterminate: true , selfFraction: _ProgressFraction ( ) , childFraction: _ProgressFraction ( ) ) , otherProperties: [ : ] , childrenOtherProperties: [ : ] , isDirty: state. isDirty)
385+ state = State ( fractionState: FractionState ( indeterminate: true ,
386+ selfFraction: _ProgressFraction ( ) ,
387+ childFraction: _ProgressFraction ( ) ) ,
388+ otherProperties: [ : ] ,
389+ childrenOtherProperties: [ : ] ,
390+ isDirtyCompleted: false ,
391+ updatedCompletedCount: 0 ,
392+ isDirtyTotal: false ,
393+ updatedTotalCount: 0 ,
394+ children: Set ( ) )
357395 let result = try closure ( & values)
358396 state = values. state
359397 return result
@@ -439,14 +477,26 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
439477 let current : _ProgressFraction
440478 }
441479
442- private func updateCompletedCount( count: Int ) -> UpdateState {
443- // Acquire and release child's lock
444- let ( previous, current) = state. withLock { state in
445- let prev = state. fractionState. overallFraction
446- state. fractionState. selfFraction. completed += count
447- return ( prev, state. fractionState. overallFraction)
480+ private func getPreviousAndCurrentState( state: inout State ) -> UpdateState {
481+ let prev = state. fractionState. overallFraction
482+ if state. isDirtyTotal {
483+ if state. updatedTotalCount == nil {
484+ print ( " updated total count is nil " )
485+ state. fractionState. indeterminate = true
486+ } else {
487+ state. fractionState. indeterminate = false
488+ }
489+ // actually update totalCount
490+ if ( state. fractionState. selfFraction. total != ( state. updatedTotalCount ?? 0 ) ) && ( state. fractionState. selfFraction. total > 0 ) {
491+ state. fractionState. childFraction = state. fractionState. childFraction * _ProgressFraction( completed: state. fractionState. selfFraction. total, total: state. updatedTotalCount ?? 1 )
492+ }
493+ state. fractionState. selfFraction. total = state. updatedTotalCount ?? 0
494+ }
495+ if state. isDirtyCompleted {
496+ // actually update completedCount
497+ state. fractionState. selfFraction. completed = state. updatedCompletedCount
448498 }
449- return UpdateState ( previous: previous , current: current )
499+ return UpdateState ( previous: prev , current: state . fractionState . overallFraction )
450500 }
451501
452502 private func updateFractionCompleted( from: _ProgressFraction , to: _ProgressFraction ) {
@@ -488,21 +538,56 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
488538 state. fractionState. childFraction = state. fractionState. childFraction - ( multiple * next)
489539 }
490540 }
541+ state. isDirtyTotal = false
542+ state. isDirtyCompleted = false
491543 return UpdateState ( previous: previousOverallFraction, current: state. fractionState. overallFraction)
492544 }
493545 updateFractionCompleted ( from: updateState. previous, to: updateState. current)
494546 }
495547
496548
497549 /// Update completedCount and mark all ancestors as dirty.
498- private func markDirty( ) {
550+ private func markCompletedDirty( state: inout State ) {
551+ state. isDirtyCompleted = true
552+
553+ // recursively mark all ancestors as dirty
554+ parents. withLock { parents in
555+ for (parent, _) in parents {
556+ parent. markCompletedDirty ( )
557+ }
558+ }
559+ }
560+
561+ private func markCompletedDirty( ) {
499562 state. withLock { state in
500- state. isDirty = true
563+ state. isDirtyCompleted = true
501564 }
502- // recursively mark all ancestors as dirty
565+
566+ parents. withLock { parents in
567+ for (parent, _) in parents {
568+ parent. markCompletedDirty ( )
569+ }
570+ }
571+ }
572+
573+ private func markTotalDirty( state: inout State ) {
574+ state. isDirtyTotal = true
575+
576+ parents. withLock { parents in
577+ for (parent, _) in parents {
578+ parent. markTotalDirty ( )
579+ }
580+ }
581+ }
582+
583+ private func markTotalDirty( ) {
584+ state. withLock { state in
585+ state. isDirtyTotal = true
586+ }
587+
503588 parents. withLock { parents in
504589 for (parent, _) in parents {
505- parent. markDirty ( )
590+ parent. markTotalDirty ( )
506591 }
507592 }
508593 }
@@ -552,8 +637,8 @@ internal struct AnyMetatypeWrapper: Hashable, Equatable, Sendable {
552637 }
553638
554639 internal func addToChildren( childManager: ProgressManager ) {
555- _ = children . withLock { children in
556- children. insert ( childManager)
640+ _ = state . withLock { state in
641+ state . children. insert ( childManager)
557642 }
558643 }
559644
0 commit comments