6
6
///
7
7
8
8
import Foundation
9
+ import os
9
10
10
11
/// The footprint manages snapshots of app memory limits and state,
11
12
/// and notifies your app when these change.
@@ -82,7 +83,7 @@ public final class Footprint: @unchecked Sendable {
82
83
83
84
/// The high watermark of memory bytes your app can use before being terminated.
84
85
public let limit : Int64
85
-
86
+
86
87
/// The state describing where your app sits within the scope of its memory limit.
87
88
public let state : State
88
89
@@ -93,7 +94,12 @@ public final class Footprint: @unchecked Sendable {
93
94
public let timestamp : UInt64
94
95
95
96
/// Initialize for the `Memory` structure.
96
- init ( used: Int64 , remaining: Int64 , compressed: Int64 = 0 , pressure: State = . normal) {
97
+ init (
98
+ used: Int64 ,
99
+ remaining: Int64 ,
100
+ compressed: Int64 = 0 ,
101
+ pressure: State = . normal
102
+ ) {
97
103
98
104
self . used = used
99
105
self . remaining = remaining
@@ -108,13 +114,8 @@ public final class Footprint: @unchecked Sendable {
108
114
usedRatio < 0.90 ? . critical : . terminal
109
115
110
116
self . timestamp = {
111
- let time = mach_absolute_time ( )
112
- var timebaseInfo = mach_timebase_info_data_t ( )
113
- guard mach_timebase_info ( & timebaseInfo) == KERN_SUCCESS else {
114
- return 0
115
- }
116
- let timeInNanoseconds = time * UInt64( timebaseInfo. numer) / UInt64( timebaseInfo. denom)
117
- return timeInNanoseconds / 1_000_000
117
+ let timeInNanoseconds = clock_gettime_nsec_np ( CLOCK_UPTIME_RAW)
118
+ return timeInNanoseconds / NSEC_PER_MSEC
118
119
} ( )
119
120
}
120
121
@@ -158,7 +159,14 @@ public final class Footprint: @unchecked Sendable {
158
159
public var memory : Memory {
159
160
_memoryLock. withLock { _memory }
160
161
}
161
-
162
+
163
+ /// Returns an AsyncStream that pushes a _Memory_ as it changes.
164
+ public var memoryStream : AsyncStream < Memory > {
165
+ AsyncStream { continuation in
166
+ _memoryStreamContinuations. append ( continuation)
167
+ }
168
+ }
169
+
162
170
/// Based on the current memory footprint, tells you if you should be able to allocate
163
171
/// a certain amount of memory.
164
172
///
@@ -205,6 +213,8 @@ public final class Footprint: @unchecked Sendable {
205
213
206
214
_memoryPressureSource. suspend ( )
207
215
_memoryPressureSource. cancel ( )
216
+
217
+ _memoryStreamContinuations. forEach { $0. finish ( ) }
208
218
}
209
219
210
220
private func heartbeat( ) {
@@ -308,8 +318,11 @@ public final class Footprint: @unchecked Sendable {
308
318
if changeSet. contains ( . footprint) {
309
319
// copy behind the lock
310
320
// deploy outside the lock
311
- let observers = _memoryLock. withLock { _observers }
321
+ let ( observers, continuations) = _memoryLock. withLock {
322
+ ( _observers, _memoryStreamContinuations)
323
+ }
312
324
observers. forEach { $0 ( memory) }
325
+ continuations. forEach { $0. yield ( memory) }
313
326
}
314
327
}
315
328
@@ -322,6 +335,7 @@ public final class Footprint: @unchecked Sendable {
322
335
private var _observers : [ ( Memory ) -> Void ] = [ ]
323
336
private let _memoryLock : NSLock = NSLock ( )
324
337
private var _memory : Memory
338
+ private var _memoryStreamContinuations : [ AsyncStream < Memory > . Continuation ] = [ ]
325
339
}
326
340
327
341
@available ( iOS 13 . 0 , macOS 10 . 15 , tvOS 13 . 0 , watchOS 6 . 0 , visionOS 1 . 0 , * )
0 commit comments