diff --git a/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xcscmblueprint b/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xcscmblueprint index 4cf66a2..ddaa0b0 100644 --- a/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xcscmblueprint +++ b/CoreStore.xcodeproj/project.xcworkspace/xcshareddata/CoreStore.xcscmblueprint @@ -10,10 +10,10 @@ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD", "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { "8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit", - "4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore" + "4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore\/" }, "DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore", - "DVTSourceControlWorkspaceBlueprintVersion" : 203, + "DVTSourceControlWorkspaceBlueprintVersion" : 204, "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj", "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ { diff --git a/Sources/Internal/NSManagedObjectContext+Transaction.swift b/Sources/Internal/NSManagedObjectContext+Transaction.swift index d1dc257..3d6b4b7 100644 --- a/Sources/Internal/NSManagedObjectContext+Transaction.swift +++ b/Sources/Internal/NSManagedObjectContext+Transaction.swift @@ -101,8 +101,8 @@ internal extension NSManagedObjectContext { } @nonobjc - internal func saveSynchronously() -> SaveResult { - + internal func saveSynchronously(mergeSynchronously: Bool = true) -> SaveResult { + var result = SaveResult(hasChanges: false) self.performBlockAndWait { @@ -114,7 +114,7 @@ internal extension NSManagedObjectContext { do { - self.isSavingSynchronously = true + self.isSavingSynchronously = mergeSynchronously try self.save() self.isSavingSynchronously = nil } @@ -131,7 +131,7 @@ internal extension NSManagedObjectContext { if let parentContext = self.parentContext where self.shouldCascadeSavesToParent { - switch parentContext.saveSynchronously() { + switch parentContext.saveSynchronously(mergeSynchronously) { case .Success: result = SaveResult(hasChanges: true) diff --git a/Sources/Transactions/SynchronousDataTransaction.swift b/Sources/Transactions/SynchronousDataTransaction.swift index 76b337f..3a3341b 100644 --- a/Sources/Transactions/SynchronousDataTransaction.swift +++ b/Sources/Transactions/SynchronousDataTransaction.swift @@ -59,7 +59,33 @@ public final class SynchronousDataTransaction: BaseDataTransaction { self.result = result return result } - + + /** + Saves the transaction changes and waits for completion synchronously, but merges into the main context asynchronously. This method should not be used after the `commit()` method was already called once. + + This method can be used to avoid potential deadlocks that can arise when a background thread attempts to merge changes into the main context while the main queue is querying from that context. Note that this + introduces a possibility that the main thread can attempt to query for the changes before the asynchronous merge operation has happened. + + - returns: a `SaveResult` containing the success or failure information + */ + public func commit() -> SaveResult { + + CoreStore.assert( + self.transactionQueue.isCurrentExecutionContext(), + "Attempted to commit a \(cs_typeName(self)) outside its designated queue." + ) + CoreStore.assert( + !self.isCommitted, + "Attempted to commit a \(cs_typeName(self)) more than once." + ) + + self.isCommitted = true + + let result = self.context.saveSynchronously(false) + self.result = result + return result + } + /** Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.