Skip to content

Commit 363f678

Browse files
committed
refactor interop-related stuff
1 parent d318e4d commit 363f678

File tree

4 files changed

+202
-327
lines changed

4 files changed

+202
-327
lines changed

Sources/FoundationEssentials/ProgressManager/ProgressManager+Interop.swift

Lines changed: 159 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
// Keep all interop logiv here
12+
1313
#if FOUNDATION_FRAMEWORK
1414
internal import _ForSwiftFoundation
15+
internal import Synchronization
16+
1517
@available(FoundationPreview 6.2, *)
16-
//MARK: Progress Parent - ProgressManager Child Interop
17-
// Actual Progress Parent
18-
// Ghost Progress Parent
19-
// Ghost ProgressManager Child
20-
// Actual ProgressManager Child
18+
//MARK: Progress Parent - Subprogress / ProgressReporter Child Interop
2119
extension Progress {
2220

2321
/// Returns a Subprogress which can be passed to any method that reports progress
@@ -30,28 +28,27 @@ extension Progress {
3028
/// - Returns: A `Subprogress` instance.
3129
public func makeChild(withPendingUnitCount count: Int) -> Subprogress {
3230

33-
// Make ghost parent & add it to actual parent's children list
34-
let topProgress = Progress(totalUnitCount: 1)
35-
self.addChild(topProgress, withPendingUnitCount: Int64(count))
31+
// Make a ProgressManager
32+
let manager = ProgressManager(totalCount: 1)
3633

37-
// Make ghost child
38-
let bottomProgress = ProgressManager(totalCount: 1)
34+
// Create a NSProgress - ProgressManager bridge for mirroring
35+
let subprogressBridge = SubprogressBridge(
36+
parent: self,
37+
portion: Int64(count),
38+
manager: manager
39+
)
3940

40-
// Make observation instance
41-
let managerObservation = _NSProgressParentSubprogressChild(
42-
ghostParent: topProgress,
43-
ghostChild: bottomProgress
41+
// Instantiate a Subprogress with ProgressManager as parent
42+
// Store bridge
43+
let subprogress = Subprogress(
44+
parent: manager,
45+
portionOfParent: 1,
46+
subprogressBridge: subprogressBridge
4447
)
4548

46-
// Make actual child with ghost child being parent
47-
var subprogress = Subprogress(parent: bottomProgress, portionOfParent: 1)
48-
bottomProgress.subprogress(assigningCount: 1)
49-
subprogress.managerObservation = managerObservation
50-
subprogress.progressParentProgressManagerChildMessenger = bottomProgress
5149
return subprogress
5250
}
5351

54-
5552
/// Adds a ProgressReporter as a child to a Progress, which constitutes a portion of Progress's totalUnitCount.
5653
///
5754
/// - Parameters:
@@ -61,17 +58,15 @@ extension Progress {
6158

6259
precondition(self.isCycle(reporter: reporter) == false, "Creating a cycle is not allowed.")
6360

64-
// Make intermediary & add it to NSProgress parent's children list
65-
let ghostProgressParent = Progress(totalUnitCount: Int64(reporter.manager.totalCount ?? 0))
66-
ghostProgressParent.completedUnitCount = Int64(reporter.manager.completedCount)
67-
self.addChild(ghostProgressParent, withPendingUnitCount: Int64(count))
68-
69-
// Make observation instance
70-
let reporterObservation = _NSProgressParentProgressReporterChild(
71-
intermediary: ghostProgressParent,
72-
reporter: reporter
61+
// Create a NSProgress - ProgressReporter bridge
62+
let reporterBridge = ProgressReporterBridge(
63+
parent: self,
64+
portion: Int64(count),
65+
reporterBridge: reporter
7366
)
74-
reporter.manager.setInteropObservationReporter(observation: reporterObservation)
67+
68+
// Store bridge
69+
reporter.manager.addBridge(reporterBridge: reporterBridge)
7570
}
7671

7772
// MARK: Cycle detection
@@ -80,11 +75,11 @@ extension Progress {
8075
return false
8176
}
8277

83-
if !(self._parent() is _NSProgressParentBridge) {
78+
if !(self._parent() is NSProgressBridge) {
8479
return self._parent().isCycle(reporter: reporter)
8580
}
8681

87-
let unwrappedParent = (self._parent() as? _NSProgressParentBridge)?.actualParent
82+
let unwrappedParent = (self._parent() as? NSProgressBridge)?.manager
8883
if let unwrappedParent = unwrappedParent {
8984
if unwrappedParent === reporter.manager {
9085
return true
@@ -96,105 +91,178 @@ extension Progress {
9691
}
9792
}
9893

99-
internal final class _NSProgressParentSubprogressChild: Sendable {
100-
private let ghostParent: Progress
101-
private let ghostChild: ProgressManager
102-
103-
fileprivate init(ghostParent: Progress, ghostChild: ProgressManager) {
104-
self.ghostParent = ghostParent
105-
self.ghostChild = ghostChild
94+
@available(FoundationPreview 6.2, *)
95+
//MARK: ProgressManager Parent - Progress Child Interop
96+
extension ProgressManager {
97+
98+
/// Adds a Foundation's `Progress` instance as a child which constitutes a certain `count` of `self`'s `totalCount`.
99+
/// - Parameters:
100+
/// - count: Number of units delegated from `self`'s `totalCount`.
101+
/// - progress: `Progress` which receives the delegated `count`.
102+
public func subprogress(assigningCount count: Int, to progress: Foundation.Progress) {
103+
precondition(progress._parent() == nil, "Cannot assign a progress to more than one parent.")
104+
105+
// Create a ProgressManager - NSProgress bridge
106+
let progressBridge = NSProgressBridge(
107+
manager: self,
108+
progress: progress,
109+
portion: count
110+
)
106111

107-
// Set up mirroring observation relationship between ghostChild and ghostParent
108-
// - Ghost Parent should mirror values from Ghost Child, and Ghost Child just mirrors values of Actual Child
109-
ghostChild.addObserver { [weak self] observerState in
112+
// Add bridge as a parent
113+
progress._setParent(progressBridge, portion: Int64(count))
114+
115+
// Store bridge
116+
self.addBridge(nsProgressBridge: progressBridge)
117+
}
118+
}
119+
120+
internal final class SubprogressBridge: Sendable {
121+
122+
internal let progressBridge: Progress
123+
internal let manager: ProgressManager
124+
125+
init(parent: Progress, portion: Int64, manager: ProgressManager) {
126+
self.manager = manager
127+
self.progressBridge = Progress(totalUnitCount: 1, parent: parent, pendingUnitCount: portion)
128+
129+
manager.addObserver { [weak self] observerState in
110130
guard let self else {
111131
return
112132
}
113133

114134
switch observerState {
115135
case .fractionUpdated(let totalCount, let completedCount):
116-
self.ghostParent.completedUnitCount = Int64(completedCount)
117-
self.ghostParent.totalUnitCount = Int64(totalCount)
136+
self.progressBridge.completedUnitCount = Int64(completedCount)
137+
self.progressBridge.totalUnitCount = Int64(totalCount)
118138
}
119139
}
120140
}
121141
}
122142

123-
internal final class _NSProgressParentProgressReporterChild: Sendable {
124-
private let intermediary: Progress
125-
private let reporter: ProgressReporter
143+
internal final class ProgressReporterBridge: Sendable {
144+
145+
internal let progressBridge: Progress
146+
internal let reporterBridge: ProgressReporter
126147

127-
fileprivate init(intermediary: Progress, reporter: ProgressReporter) {
128-
self.intermediary = intermediary
129-
self.reporter = reporter
148+
init(parent: Progress, portion: Int64, reporterBridge: ProgressReporter) {
149+
self.progressBridge = Progress(
150+
totalUnitCount: Int64(reporterBridge.manager.totalCount ?? 0),
151+
parent: parent,
152+
pendingUnitCount: portion
153+
)
154+
self.progressBridge.completedUnitCount = Int64(reporterBridge.manager.completedCount)
155+
self.reporterBridge = reporterBridge
156+
157+
let manager = reporterBridge.manager
130158

131-
reporter.manager.addObserver { [weak self] observerState in
159+
manager.addObserver { [weak self] observerState in
132160
guard let self else {
133161
return
134162
}
135163

136164
switch observerState {
137165
case .fractionUpdated(let totalCount, let completedCount):
138-
self.intermediary.completedUnitCount = Int64(completedCount)
139-
self.intermediary.totalUnitCount = Int64(totalCount)
166+
self.progressBridge.completedUnitCount = Int64(completedCount)
167+
self.progressBridge.totalUnitCount = Int64(totalCount)
140168
}
141169
}
142170
}
143171

144172
}
145173

146-
@available(FoundationPreview 6.2, *)
147-
//MARK: ProgressManager Parent - Progress Child Interop
148-
extension ProgressManager {
149-
150-
/// Adds a Foundation's `Progress` instance as a child which constitutes a certain `count` of `self`'s `totalCount`.
151-
/// - Parameters:
152-
/// - count: Number of units delegated from `self`'s `totalCount`.
153-
/// - progress: `Progress` which receives the delegated `count`.
154-
public func subprogress(assigningCount count: Int, to progress: Foundation.Progress) {
155-
precondition(progress._parent() == nil, "Cannot assign a progress to more than one parent.")
156-
157-
let parentBridge = _NSProgressParentBridge(managerParent: self, progressChild: progress, portion: count)
158-
progress._setParent(parentBridge, portion: Int64(count))
159-
160-
// Save ghost parent in ProgressManager so it doesn't go out of scope after assign method ends
161-
// So that when NSProgress increases completedUnitCount and queries for parent there is still a reference to ghostParent and parent doesn't show 0x0 (portion: 5)
162-
self.setParentBridge(parentBridge: parentBridge)
163-
}
164-
}
165-
166-
// Subclass of Foundation.Progress
167-
internal final class _NSProgressParentBridge: Progress, @unchecked Sendable {
174+
internal final class NSProgressBridge: Progress, @unchecked Sendable {
168175

169-
internal let actualParent: ProgressManager
170-
internal let actualChild: Progress
171-
internal let ghostChild: ProgressManager
176+
internal let manager: ProgressManager
177+
internal let managerBridge: ProgressManager
178+
internal let progress: Progress
172179

173-
init(managerParent: ProgressManager, progressChild: Progress, portion: Int) {
174-
self.actualParent = managerParent
175-
self.actualChild = progressChild
176-
self.ghostChild = ProgressManager(totalCount: Int(progressChild.totalUnitCount))
180+
init(manager: ProgressManager, progress: Progress, portion: Int) {
181+
self.manager = manager
182+
self.managerBridge = ProgressManager(totalCount: Int(progress.totalUnitCount))
183+
self.progress = progress
177184
super.init(parent: nil, userInfo: nil)
178185

179-
// Make ghostChild mirror progressChild, ghostChild is added as a child to managerParent
180-
ghostChild.withProperties { properties in
181-
properties.completedCount = Int(progressChild.completedUnitCount)
186+
managerBridge.withProperties { properties in
187+
properties.completedCount = Int(progress.completedUnitCount)
182188
}
183189

184-
let position = managerParent.addChild(child: ghostChild, portion: portion, childFraction: ProgressFraction(completed: Int(completedUnitCount), total: Int(totalUnitCount)))
185-
186-
ghostChild.addParent(parent: managerParent, positionInParent: position)
190+
let position = manager.addChild(
191+
child: managerBridge,
192+
portion: portion,
193+
childFraction: ProgressFraction(completed: Int(completedUnitCount), total: Int(totalUnitCount))
194+
)
195+
managerBridge.addParent(parent: manager, positionInParent: position)
187196
}
188197

189198
// Overrides the _updateChild func that Foundation.Progress calls to update parent
190199
// so that the parent that gets updated is the ProgressManager parent
191200
override func _updateChild(_ child: Foundation.Progress, fraction: _NSProgressFractionTuple, portion: Int64) {
192-
ghostChild.withProperties { properties in
201+
managerBridge.withProperties { properties in
193202
properties.totalCount = Int(fraction.next.total)
194203
properties.completedCount = Int(fraction.next.completed)
195204
}
196205

197-
ghostChild.markSelfDirty()
206+
managerBridge.markSelfDirty()
207+
}
208+
}
209+
210+
@available(FoundationPreview 6.2, *)
211+
extension ProgressManager {
212+
internal enum ObserverState {
213+
case fractionUpdated(totalCount: Int, completedCount: Int)
214+
}
215+
216+
internal struct InteropObservation {
217+
let subprogressBridge: SubprogressBridge?
218+
var reporterBridge: ProgressReporterBridge?
219+
var nsProgressBridge: Foundation.Progress?
220+
}
221+
}
222+
223+
extension ProgressManager.State {
224+
internal func notifyObservers(with observerState: ProgressManager.ObserverState) {
225+
for observer in observers {
226+
observer(observerState)
227+
}
228+
}
229+
}
230+
231+
@available(FoundationPreview 6.2, *)
232+
extension ProgressManager {
233+
//MARK: Interop Methods
234+
/// Adds `observer` to list of `_observers` in `self`.
235+
internal func addObserver(observer: @escaping @Sendable (ObserverState) -> Void) {
236+
state.withLock { state in
237+
state.observers.append(observer)
238+
}
239+
}
240+
241+
/// Notifies all `_observers` of `self` when `state` changes.
242+
internal func notifyObservers(with observedState: ObserverState) {
243+
state.withLock { state in
244+
for observer in state.observers {
245+
observer(observedState)
246+
}
247+
}
248+
}
249+
250+
internal func addBridge(reporterBridge: ProgressReporterBridge? = nil, nsProgressBridge: Foundation.Progress? = nil) {
251+
state.withLock { state in
252+
if let reporterBridge {
253+
state.interopObservation.reporterBridge = reporterBridge
254+
}
255+
256+
if let nsProgressBridge {
257+
state.interopObservation.nsProgressBridge = nsProgressBridge
258+
}
259+
}
260+
}
261+
262+
internal func setInteropChild(interopChild: ProgressManager) {
263+
state.withLock { state in
264+
state.interopChild = interopChild
265+
}
198266
}
199267
}
200268
#endif

0 commit comments

Comments
 (0)