Skip to content

Commit eb87d7a

Browse files
committed
fix(ios): un-latch memory pressure monitor and gate editor truncation
1 parent 4fa38f8 commit eb87d7a

3 files changed

Lines changed: 19 additions & 16 deletions

File tree

TableProMobile/TableProMobile/Platform/MemoryPressureMonitor.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,21 @@ final class MemoryPressureMonitor {
2323
guard source == nil else { return }
2424

2525
let newSource = DispatchSource.makeMemoryPressureSource(
26-
eventMask: [.warning, .critical],
26+
eventMask: [.normal, .warning, .critical],
2727
queue: .global(qos: .utility)
2828
)
2929

3030
newSource.setEventHandler { [weak self] in
3131
let event = newSource.data
32-
let level: Level = event.contains(.critical) ? .critical : .warning
33-
Self.logger.warning("Memory pressure event: \(String(describing: level), privacy: .public)")
32+
let level: Level
33+
if event.contains(.critical) {
34+
level = .critical
35+
} else if event.contains(.warning) {
36+
level = .warning
37+
} else {
38+
level = .normal
39+
}
40+
Self.logger.log("Memory pressure level: \(String(describing: level), privacy: .public)")
3441
Task { @MainActor in
3542
self?.currentLevel = level
3643
}
@@ -40,10 +47,6 @@ final class MemoryPressureMonitor {
4047
source = newSource
4148
}
4249

43-
func reset() {
44-
currentLevel = .normal
45-
}
46-
4750
nonisolated func availableMemoryBytes() -> Int {
4851
Int(os_proc_available_memory())
4952
}

TableProMobile/TableProMobile/ViewModels/QueryEditorViewModel.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,18 @@ final class QueryEditorViewModel {
121121
case .normal:
122122
return
123123
case .warning, .critical:
124+
guard case .running = self.phase else { return }
124125
Self.logger.warning("Memory pressure: stopping query stream to stay within limits")
125126
self.fetchTask?.cancel()
126127
guard !self.buffer.isEmpty else { return }
127128
self.buffer.markTruncated(.memoryPressure)
128-
if case .running = self.phase {
129-
self.phase = .truncated(reason: .memoryPressure)
130-
}
129+
self.phase = .truncated(reason: .memoryPressure)
131130
}
132131
}
133132
}
134133

135134
private func isMemoryConstrained() -> Bool {
136-
if MemoryPressureMonitor.shared.currentLevel != .normal { return true }
137-
return !MemoryPressureMonitor.shared.hasHeadroom(forBytes: Self.memorySafetyMarginBytes)
135+
!MemoryPressureMonitor.shared.hasHeadroom(forBytes: Self.memorySafetyMarginBytes)
138136
}
139137

140138
private func resolvePhase() {

TableProMobile/TableProMobileTests/QueryEditorViewModelTests.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ struct QueryEditorViewModelTests {
5353
}
5454
}
5555

56-
@Test("handlePressure marks results truncated when rows are present")
57-
func pressureMarksTruncated() async {
56+
@Test("memory pressure after a clean finish does not relabel the result")
57+
func pressureDoesNotRelabelFinishedResult() async {
5858
let driver = MockDatabaseDriver()
5959
driver.scriptedExecuteResults = [
6060
.success(QueryResult(columns: makeColumns(), rows: [["1"], ["2"]], rowsAffected: 0, executionTime: 0))
@@ -67,8 +67,10 @@ struct QueryEditorViewModelTests {
6767
await vm.handlePressure(.warning)
6868

6969
#expect(vm.legacyRows.count == 2)
70-
if case .memoryPressure = vm.truncationReason {} else {
71-
Issue.record("expected memoryPressure truncation")
70+
#expect(vm.truncationReason == nil)
71+
#expect(vm.truncationMessage == nil)
72+
if case .finished = vm.phase {} else {
73+
Issue.record("a completed result must stay finished after a memory warning")
7274
}
7375
}
7476

0 commit comments

Comments
 (0)