Skip to content

Commit 19a20e5

Browse files
committed
debugging: using dirty bit for values update
1 parent 8240116 commit 19a20e5

File tree

1 file changed

+163
-78
lines changed

1 file changed

+163
-78
lines changed

Sources/FoundationEssentials/ProgressManager/ProgressManager.swift

Lines changed: 163 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)