mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-24 16:11:43 +00:00
macos: synchronize cached value access (#13646)
Fixes #13276 Make CachedValue safe for concurrent terminal content reads and expiry. The expiry task could previously release cached Swift String storage while another thread retained it, aborting the process during otherwise normal terminal use. Protect cached values and task handles with an NSLock, and exercise concurrent reads across repeated expiration in a regression test.
This commit is contained in:
@@ -2370,6 +2370,7 @@ extension Ghostty.SurfaceView {
|
||||
/// We use this to cache our surface content. This probably should be extracted some day
|
||||
/// to a more generic helper.
|
||||
class CachedValue<T> {
|
||||
private let lock = NSLock()
|
||||
private var value: T?
|
||||
private let fetch: () -> T
|
||||
private let duration: Duration
|
||||
@@ -2381,10 +2382,15 @@ class CachedValue<T> {
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.lock()
|
||||
expiryTask?.cancel()
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func get() -> T {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
|
||||
if let value {
|
||||
return value
|
||||
}
|
||||
@@ -2399,8 +2405,7 @@ class CachedValue<T> {
|
||||
expiryTask = Task { [weak self] in
|
||||
do {
|
||||
try await Task.sleep(until: expires)
|
||||
self?.value = nil
|
||||
self?.expiryTask = nil
|
||||
self?.expire()
|
||||
} catch {
|
||||
// Task was cancelled, do nothing
|
||||
}
|
||||
@@ -2408,4 +2413,12 @@ class CachedValue<T> {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private func expire() {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
|
||||
value = nil
|
||||
expiryTask = nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user