generated from StanfordBDHG/SwiftPackageTemplate
-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Maintain Shadow state for observable state on MainActor
- Loading branch information
Showing
10 changed files
with
223 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
Sources/SpeziBluetooth/CoreBluetooth/Model/MainActorBuffered.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
import Foundation | ||
import SpeziFoundation | ||
|
||
|
||
final class MainActorBuffered<Value: Sendable>: Sendable { | ||
private nonisolated(unsafe) var unsafeValue: Value | ||
@MainActor private(set) var mainActorValue: Value? | ||
|
||
init(_ value: Value) { | ||
self.unsafeValue = value | ||
self.mainActorValue = value | ||
} | ||
|
||
func loadUnsafe() -> Value { | ||
loadIfMainActor() ?? unsafeValue | ||
} | ||
|
||
func load(using lock: NSLock) -> Value { | ||
loadIfMainActor() ?? lock.withLock { | ||
unsafeValue | ||
} | ||
} | ||
|
||
func load(using lock: RWLock) -> Value { | ||
loadIfMainActor() ?? lock.withReadLock { | ||
unsafeValue | ||
} | ||
} | ||
|
||
private func loadIfMainActor() -> Value? { | ||
if Thread.isMainThread { | ||
MainActor.assumeIsolated { | ||
mainActorValue | ||
} | ||
} else { | ||
nil | ||
} | ||
} | ||
|
||
private func _store(_ newValue: Value, mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void) { | ||
Task { @MainActor in | ||
let valueMutation = { @MainActor in | ||
self.mainActorValue = newValue | ||
} | ||
|
||
mutation(valueMutation) | ||
} | ||
} | ||
|
||
func store(_ newValue: Value, using lock: NSLock, mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void) { | ||
lock.withLock { | ||
unsafeValue = newValue | ||
} | ||
_store(newValue, mutation: mutation) | ||
} | ||
|
||
func store(_ newValue: Value, using lock: RWLock, mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void) { | ||
lock.withWriteLock { | ||
unsafeValue = newValue | ||
} | ||
_store(newValue, mutation: mutation) | ||
} | ||
} | ||
|
||
|
||
extension MainActorBuffered where Value: Equatable { | ||
func storeAndCompare(_ newValue: Value, using lock: RWLock, mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void) -> Bool { | ||
let didChange = lock.withWriteLock { | ||
let didChange = unsafeValue != newValue | ||
unsafeValue = newValue | ||
return didChange | ||
} | ||
_store(newValue, mutation: mutation) | ||
|
||
return didChange | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
Sources/SpeziBluetooth/CoreBluetooth/Model/ManagedAtomicMainActorBuffered.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// | ||
// This source file is part of the Stanford Spezi open-source project | ||
// | ||
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
|
||
import Atomics | ||
import Foundation | ||
|
||
|
||
final class ManagedAtomicMainActorBuffered<Value: AtomicValue & Sendable>: Sendable where Value.AtomicRepresentation.Value == Value { | ||
private let managedValue: ManagedAtomic<Value> | ||
@MainActor private var mainActorValue: Value? | ||
|
||
init(_ value: Value) { | ||
self.managedValue = ManagedAtomic(value) | ||
self.mainActorValue = value | ||
} | ||
|
||
@_semantics("atomics.requires_constant_orderings") | ||
@inlinable | ||
func load(ordering: AtomicLoadOrdering = .relaxed) -> Value { | ||
if Thread.isMainThread { | ||
MainActor.assumeIsolated { | ||
mainActorValue | ||
} ?? managedValue.load(ordering: ordering) | ||
} else { | ||
managedValue.load(ordering: ordering) | ||
} | ||
} | ||
|
||
@_semantics("atomics.requires_constant_orderings") | ||
private func mutateMainActorBuffer( | ||
_ newValue: Value, | ||
mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void | ||
) { | ||
Task { @MainActor in | ||
let valueMutation = { @MainActor in | ||
self.mainActorValue = newValue | ||
} | ||
|
||
mutation(valueMutation) | ||
} | ||
} | ||
|
||
@_semantics("atomics.requires_constant_orderings") | ||
@inlinable | ||
func store( | ||
_ newValue: Value, | ||
ordering: AtomicStoreOrdering = .relaxed, | ||
mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void | ||
) { | ||
managedValue.store(newValue, ordering: ordering) | ||
mutateMainActorBuffer(newValue, mutation: mutation) | ||
} | ||
} | ||
|
||
|
||
extension ManagedAtomicMainActorBuffered where Value: Equatable { | ||
@_semantics("atomics.requires_constant_orderings") | ||
@inlinable | ||
func storeAndCompare( | ||
_ newValue: Value, | ||
ordering: AtomicUpdateOrdering = .relaxed, | ||
mutation: sending @MainActor @escaping (@MainActor () -> Void) -> Void | ||
) -> Bool { | ||
let previousValue = managedValue.exchange(newValue, ordering: ordering) | ||
mutateMainActorBuffer(newValue, mutation: mutation) | ||
|
||
return previousValue != newValue | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.