Fixes #98 (deadlock when merge happens while main queue is querying)

This commit is contained in:
Colin Morelli
2016-09-29 14:47:51 -04:00
parent ed8c7b35e8
commit 8be20370d5
3 changed files with 33 additions and 7 deletions

View File

@@ -10,10 +10,10 @@
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD", "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit", "8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit",
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore" "4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore\/"
}, },
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore", "DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore",
"DVTSourceControlWorkspaceBlueprintVersion" : 203, "DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj", "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{ {

View File

@@ -101,7 +101,7 @@ internal extension NSManagedObjectContext {
} }
@nonobjc @nonobjc
internal func saveSynchronously() -> SaveResult { internal func saveSynchronously(mergeSynchronously: Bool = true) -> SaveResult {
var result = SaveResult(hasChanges: false) var result = SaveResult(hasChanges: false)
@@ -114,7 +114,7 @@ internal extension NSManagedObjectContext {
do { do {
self.isSavingSynchronously = true self.isSavingSynchronously = mergeSynchronously
try self.save() try self.save()
self.isSavingSynchronously = nil self.isSavingSynchronously = nil
} }
@@ -131,7 +131,7 @@ internal extension NSManagedObjectContext {
if let parentContext = self.parentContext where self.shouldCascadeSavesToParent { if let parentContext = self.parentContext where self.shouldCascadeSavesToParent {
switch parentContext.saveSynchronously() { switch parentContext.saveSynchronously(mergeSynchronously) {
case .Success: case .Success:
result = SaveResult(hasChanges: true) result = SaveResult(hasChanges: true)

View File

@@ -60,6 +60,32 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
return 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. 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.