Skip to content

Commit 20c03d4

Browse files
committed
fix fractionCompleted - deinit deadlock
1 parent 3c2ffc9 commit 20c03d4

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

Sources/FoundationEssentials/ProgressManager/ProgressManager+State.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,10 @@ extension ProgressManager {
106106
var overallFraction: ProgressFraction {
107107
var overallFraction = selfFraction
108108
for childState in children {
109-
if let _ = childState.child {
110-
if !childState.childFraction.isFinished {
111-
let multiplier = ProgressFraction(completed: childState.portionOfTotal, total: selfFraction.total)
112-
if let additionalFraction = multiplier * childState.childFraction {
113-
overallFraction = overallFraction + additionalFraction
114-
}
109+
if !childState.childFraction.isFinished {
110+
let multiplier = ProgressFraction(completed: childState.portionOfTotal, total: selfFraction.total)
111+
if let additionalFraction = multiplier * childState.childFraction {
112+
overallFraction = overallFraction + additionalFraction
115113
}
116114
}
117115
}

Tests/FoundationEssentialsTests/ProgressManager/ProgressManagerTests.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,29 @@ extension Tag {
407407
subprogress = nil
408408
#expect(manager.fractionCompleted == 1.0)
409409
}
410+
411+
@Test func deallocatedChild() async throws {
412+
let manager = ProgressManager(totalCount: 100)
413+
414+
var child: ProgressManager? = manager.subprogress(assigningCount: 50).start(totalCount: 10)
415+
child!.complete(count: 5)
416+
417+
let fractionBeforeDeallocation = manager.fractionCompleted
418+
#expect(fractionBeforeDeallocation == 0.25)
419+
420+
child = nil
421+
422+
for _ in 1...10 {
423+
_ = manager.fractionCompleted
424+
}
425+
426+
let fractionAfterDeallocation = manager.fractionCompleted
427+
428+
#expect(fractionAfterDeallocation == 0.5, "Deallocated child should be assumed completed.")
429+
430+
manager.complete(count: 50)
431+
#expect(manager.fractionCompleted == 1.0)
432+
}
410433
}
411434

412435
// MARK: - Thread Safety and Concurrent Access Tests
@@ -747,4 +770,28 @@ extension Tag {
747770
}
748771
}
749772
}
773+
774+
@Test func concurrentSubprogressCreation() async throws {
775+
let manager = ProgressManager(totalCount: 1000)
776+
777+
await withThrowingTaskGroup(of: Void.self) { group in
778+
// Create 20 concurrent tasks, each creating multiple subprogresses
779+
for _ in 1...20 {
780+
group.addTask {
781+
for i in 1...10 {
782+
let child = manager.subprogress(assigningCount: 5).start(totalCount: 4)
783+
child.complete(count: 4)
784+
785+
// Immediately access properties
786+
let _ = child.fractionCompleted
787+
let _ = manager.fractionCompleted
788+
789+
try? await Task.sleep(nanoseconds: 100_000 * UInt64(i))
790+
}
791+
}
792+
}
793+
}
794+
795+
#expect(manager.completedCount == 1000)
796+
}
750797
}

0 commit comments

Comments
 (0)