From e8925b53883c10a7cd158ada50d9341a087005da Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Thu, 23 Jul 2026 10:08:50 -0400 Subject: [PATCH 1/3] Gate the ModelStorage protocol on _Concurrency availability --- Sources/CoreModel/Store.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/CoreModel/Store.swift b/Sources/CoreModel/Store.swift index 11d54ff..45ea7f7 100644 --- a/Sources/CoreModel/Store.swift +++ b/Sources/CoreModel/Store.swift @@ -6,6 +6,11 @@ // Copyright © 2015 PureSwift. All rights reserved. // +// - Note: Requires the `_Concurrency` runtime (`async` requirements). Embedded +// targets whose stdlib ships no `_Concurrency` slice (e.g. bare-metal ARM) +// have no asynchronous storage abstraction — they use a concrete synchronous +// store such as ``InMemoryStorage`` directly. +#if canImport(_Concurrency) /// CoreModel Store Protocol public protocol ModelStorage: AnyObject, Sendable { @@ -41,6 +46,7 @@ public protocol ModelStorage: AnyObject, Sendable { /// in memory against fetched values. func register(function: DatabaseFunction) async throws } +#endif // canImport(_Concurrency) #if !hasFeature(Embedded) // - Note: Unavailable under Embedded Swift (a compiler bug in SILGen crashes on From e7d0afabbd13f24938543ea07f6e082be6841569 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Thu, 23 Jul 2026 10:08:50 -0400 Subject: [PATCH 2/3] Gate the InMemoryModelStorage actor on _Concurrency availability --- Sources/CoreModel/InMemoryStore.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Sources/CoreModel/InMemoryStore.swift b/Sources/CoreModel/InMemoryStore.swift index 409105a..bda223e 100644 --- a/Sources/CoreModel/InMemoryStore.swift +++ b/Sources/CoreModel/InMemoryStore.swift @@ -6,6 +6,11 @@ // Copyright © 2026 PureSwift. All rights reserved. // +// - Note: Requires the `_Concurrency` runtime (an `actor`). Embedded targets +// without a concurrency runtime (e.g. bare-metal ARM) use the synchronous +// ``InMemoryStorage`` backing directly instead. +#if canImport(_Concurrency) + /// CoreModel In-Memory Store /// /// A ``ModelStorage`` backend that keeps all objects in memory, @@ -113,3 +118,5 @@ public extension InMemoryModelStorage { // same API with typed throws. extension InMemoryModelStorage: ModelStorage {} #endif + +#endif // canImport(_Concurrency) From 8a567e41261fa091a505754adb76608fa1beaf79 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Thu, 23 Jul 2026 10:08:50 -0400 Subject: [PATCH 3/3] Make InMemoryStorage public for concurrency-less Embedded targets --- Sources/CoreModel/InMemoryStorage.swift | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Sources/CoreModel/InMemoryStorage.swift b/Sources/CoreModel/InMemoryStorage.swift index ddc03d4..328f2f0 100644 --- a/Sources/CoreModel/InMemoryStorage.swift +++ b/Sources/CoreModel/InMemoryStorage.swift @@ -19,13 +19,15 @@ import Foundation /// /// The type is thread-safe: all access is serialized so the actor-isolated /// ``InMemoryModelStorage`` and the main-actor ``InMemoryViewContext`` can operate -/// on the same instance concurrently. Under Embedded Swift, where the view context is -/// unavailable, the backing is only ever touched from within its owning actor and the -/// lock is elided. -final class InMemoryStorage { +/// on the same instance concurrently. Under Embedded Swift the lock is elided: +/// with a concurrency runtime the backing is only ever touched from within its +/// owning actor, and without one (e.g. bare-metal ARM, where ``ModelStorage`` +/// itself is unavailable) this synchronous store is the storage API, used +/// directly from the single-threaded main loop. +public final class InMemoryStorage { /// The schema entities are validated against. - let model: Model + public let model: Model private var objects = [EntityName: [ObjectID: ModelData]]() @@ -35,7 +37,7 @@ final class InMemoryStorage { private let lock = NSLock() #endif - init(model: Model) { + public init(model: Model) { self.model = model } @@ -47,14 +49,14 @@ final class InMemoryStorage { return try body() } - func fetch(_ entity: EntityName, for id: ObjectID) throws(CoreModelError) -> ModelData? { + public func fetch(_ entity: EntityName, for id: ObjectID) throws(CoreModelError) -> ModelData? { try withLock { () throws(CoreModelError) in try validate(entity) return objects[entity]?[id] } } - func fetch(_ fetchRequest: FetchRequest) throws(CoreModelError) -> [ModelData] { + public func fetch(_ fetchRequest: FetchRequest) throws(CoreModelError) -> [ModelData] { try withLock { () throws(CoreModelError) in try validate(fetchRequest.entity) let values = objects[fetchRequest.entity].map { Array($0.values) } ?? [] @@ -62,35 +64,35 @@ final class InMemoryStorage { } } - func fetchID(_ fetchRequest: FetchRequest) throws(CoreModelError) -> [ObjectID] { + public func fetchID(_ fetchRequest: FetchRequest) throws(CoreModelError) -> [ObjectID] { try fetch(fetchRequest).map { $0.id } } - func count(_ fetchRequest: FetchRequest) throws(CoreModelError) -> UInt { + public func count(_ fetchRequest: FetchRequest) throws(CoreModelError) -> UInt { try UInt(fetch(fetchRequest).count) } - func insert(_ value: ModelData) throws(CoreModelError) { + public func insert(_ value: ModelData) throws(CoreModelError) { try withLock { () throws(CoreModelError) in try validate(value.entity) objects[value.entity, default: [:]][value.id] = value } } - func insert(_ values: [ModelData]) throws(CoreModelError) { + public func insert(_ values: [ModelData]) throws(CoreModelError) { for value in values { try insert(value) } } - func delete(_ entity: EntityName, for id: ObjectID) throws(CoreModelError) { + public func delete(_ entity: EntityName, for id: ObjectID) throws(CoreModelError) { try withLock { () throws(CoreModelError) in try validate(entity) objects[entity]?[id] = nil } } - func delete(_ entity: EntityName, for ids: [ObjectID]) throws(CoreModelError) { + public func delete(_ entity: EntityName, for ids: [ObjectID]) throws(CoreModelError) { try withLock { () throws(CoreModelError) in try validate(entity) for id in ids { @@ -99,7 +101,7 @@ final class InMemoryStorage { } } - func register(function: DatabaseFunction) { + public func register(function: DatabaseFunction) { withLock { functions[function.name] = function }