ソースを参照

Synchronization for concurrent initialization

Sam King 1 年間 前
コミット
12a1e38655

+ 36 - 0
Model/CoreDataInitializationCoordinator.swift

@@ -0,0 +1,36 @@
+/// This actor provides us with logic to handle cases when a caller
+/// tries to initialize a coreDataStack that is already initialized.
+actor CoreDataInitializationCoordinator {
+    private var isInitialized = false
+    private var initializationTask: Task<Void, Error>?
+
+    func ensureInitialized(perform initialization: @escaping () async throws -> Void) async throws {
+        // If already initialized, return immediately
+        if isInitialized {
+            return
+        }
+
+        // If initialization is in progress, await the existing task
+        if let existingTask = initializationTask {
+            try await existingTask.value
+            return
+        }
+
+        // Start a new initialization task
+        let newTask = Task {
+            do {
+                try await initialization()
+                isInitialized = true
+            } catch {
+                // Clear task reference on failure
+                initializationTask = nil
+                throw error
+            }
+            // Clear task reference on success
+            initializationTask = nil
+        }
+
+        initializationTask = newTask
+        try await newTask.value
+    }
+}

+ 8 - 1
Model/CoreDataStack.swift

@@ -12,6 +12,7 @@ class CoreDataStack: ObservableObject {
     let persistentContainer: NSPersistentContainer
 
     private let maxRetries = 3
+    private let initializationCoordinator = CoreDataInitializationCoordinator()
 
     private init(inMemory: Bool = false) {
         self.inMemory = inMemory
@@ -196,7 +197,13 @@ class CoreDataStack: ObservableObject {
         }
     }
 
-    func initializeStack(retryCount: Int = 0) async throws {
+    func initializeStack() async throws {
+        try await initializationCoordinator.ensureInitialized {
+            try await self.initializeStack(retryCount: 0)
+        }
+    }
+
+    private func initializeStack(retryCount: Int) async throws {
         do {
             // Load stores asynchronously
             try await loadPersistentStores()

+ 5 - 0
Trio.xcodeproj/project.pbxproj

@@ -203,6 +203,7 @@
 		38FEF3FE2738083E00574A46 /* CGMSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */; };
 		38FEF413273B317A00574A46 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF412273B317A00574A46 /* HKUnit.swift */; };
 		3BAD36B22D7CDC1A00CC298D /* MainLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAD36B12D7CDC1400CC298D /* MainLoadingView.swift */; };
+		3BAD36CC2D7D420E00CC298D /* CoreDataInitializationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAD36CB2D7D420500CC298D /* CoreDataInitializationCoordinator.swift */; };
 		45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8A87AA037BD079BA3528BA /* ConfigEditorDataFlow.swift */; };
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
 		491D6FBD2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */; };
@@ -921,6 +922,7 @@
 		38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMSettingsProvider.swift; sourceTree = "<group>"; };
 		38FEF412273B317A00574A46 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
 		3BAD36B12D7CDC1400CC298D /* MainLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainLoadingView.swift; sourceTree = "<group>"; };
+		3BAD36CB2D7D420500CC298D /* CoreDataInitializationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataInitializationCoordinator.swift; sourceTree = "<group>"; };
 		3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorProvider.swift; sourceTree = "<group>"; };
 		3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigProvider.swift; sourceTree = "<group>"; };
 		3F60E97100041040446F44E7 /* PumpConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigStateModel.swift; sourceTree = "<group>"; };
@@ -2433,6 +2435,7 @@
 		587A54C82BCDCE0F009D38E2 /* Model */ = {
 			isa = PBXGroup;
 			children = (
+				3BAD36CB2D7D420500CC298D /* CoreDataInitializationCoordinator.swift */,
 				BDF34F8F2C10CF8C00D51995 /* CoreDataStack.swift */,
 				BD4064D02C4ED26900582F43 /* CoreDataObserver.swift */,
 				DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */,
@@ -3754,6 +3757,8 @@
 				3811DE4125C9D4A100A708ED /* SettingsRootView.swift in Sources */,
 				38192E04261B82FA0094D973 /* ReachabilityManager.swift in Sources */,
 				38E44539274E411700EC9A94 /* Disk+UIImage.swift in Sources */,
+				3BAD36CC2D7D420E00CC298D /* CoreDataInitializationCoordinator.swift in Sources */,
+				DD6B7CB62C7B748B00B75029 /* TotalInsulinDisplayType.swift in Sources */,
 				388E595C25AD948C0019842D /* TrioApp.swift in Sources */,
 				38FEF3FC2737E53800574A46 /* MainStateModel.swift in Sources */,
 				DD1745352C55AE7E00211FAC /* TargetBehavoirRootView.swift in Sources */,