Author: ericwa
Date: Sat Mar 29 23:04:46 2014
New Revision: 10660

URL: http://svn.gna.org/viewcvs/etoile?rev=10660&view=rev
Log:
COEditingContext: detect recursive calls to commit methods

Modified:
    trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.h
    trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.m
    trunk/Etoile/Frameworks/CoreObject/Tests/Core/TestEditingContext.m
    trunk/Etoile/Frameworks/CoreObject/Tests/TestCommon.m

Modified: trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.h
URL: 
http://svn.gna.org/viewcvs/etoile/trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.h?rev=10660&r1=10659&r2=10660&view=diff
==============================================================================
--- trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.h  (original)
+++ trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.h  Sat Mar 29 
23:04:46 2014
@@ -133,6 +133,8 @@
     BOOL _isRecordingUndo;
     COCommandGroup *_currentEditGroup;
        CORevisionCache *_revisionCache;
+       /** Detect illegal recursive calls to commit */
+       BOOL _inCommit;
 }
 
 

Modified: trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.m
URL: 
http://svn.gna.org/viewcvs/etoile/trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.m?rev=10660&r1=10659&r2=10660&view=diff
==============================================================================
--- trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.m  (original)
+++ trunk/Etoile/Frameworks/CoreObject/Core/COEditingContext.m  Sat Mar 29 
23:04:46 2014
@@ -524,81 +524,95 @@
                         withUndoTrack: (COUndoTrack *)track
                                         error: (NSError **)anError
 {
-       [self validateMetadata: metadata];
-
-       // TODO: We could organize validation errors by persistent root. Each
-       // persistent root might result in a validation error that contains a
-       // suberror per inner object, then each suberror could in turn contain
-       // a suberror per validation result. For now, we just aggregate errors 
per
-       // inner object.
-       if ([self validateChangedObjectsForContext: self error: anError] == NO)
-               return NO;
-
-       /* Commit persistent root changes (deleted persistent roots included) */
-
-    COStoreTransaction *transaction = [[COStoreTransaction alloc] init];
-    [self recordBeginUndoGroupWithMetadata: metadata];
-
-       for (COPersistentRoot *persistentRoot in persistentRoots)
-       {
-               [persistentRoot saveCommitWithMetadata: metadata transaction: 
transaction];
-       }
-       
-       /* Add persistent root deletions to the transaction */
-       
-       for (COPersistentRoot *persistentRoot in persistentRoots)
-       {
-        ETUUID *uuid = [persistentRoot UUID];
-        
-               if ([_persistentRootsPendingDeletion containsObject: 
persistentRoot])
-        {
-            [transaction deletePersistentRoot: uuid];
-            [self recordPersistentRootDeletion: persistentRoot];
-        }
-        else if ([_persistentRootsPendingUndeletion containsObject: 
persistentRoot])
-        {
-            [transaction undeletePersistentRoot: uuid];
-            [self recordPersistentRootUndeletion: persistentRoot];
-        }
-    }
-
-       /* Update transaction IDs (can't add to the transaction after this) */
-       
-       for (ETUUID *uuid in [transaction persistentRootUUIDs])
-       {
-               COPersistentRoot *persistentRoot = [self persistentRootForUUID: 
uuid];
-
-               persistentRoot.lastTransactionID = [transaction 
setOldTransactionID: persistentRoot.lastTransactionID
-                                                                 
forPersistentRoot: uuid];
-       }
-       
-       /* Update _persistentRootsPendingDeletion and 
_persistentRootsPendingUndeletion and unload
-          persistent roots. */
-       
-       for (COPersistentRoot *persistentRoot in persistentRoots)
-       {
-               if ([_persistentRootsPendingDeletion containsObject: 
persistentRoot])
-        {
-            [_persistentRootsPendingDeletion removeObject: persistentRoot];
-            
-            [self unloadPersistentRoot: persistentRoot];
-        }
-        else if ([_persistentRootsPendingUndeletion containsObject: 
persistentRoot])
-        {
-            [_persistentRootsPendingUndeletion removeObject: persistentRoot];
-        }
-    }
-                                                                               
        
+       if (_inCommit)
+       {
+               [NSException raise: NSGenericException
+                                       format: @"%@ called recursively", 
NSStringFromSelector(_cmd)];
+       }
+               
+       @try
+       {
+               _inCommit = YES;
+               [self validateMetadata: metadata];
+
+               // TODO: We could organize validation errors by persistent 
root. Each
+               // persistent root might result in a validation error that 
contains a
+               // suberror per inner object, then each suberror could in turn 
contain
+               // a suberror per validation result. For now, we just aggregate 
errors per
+               // inner object.
+               if ([self validateChangedObjectsForContext: self error: 
anError] == NO)
+                       return NO;
+
+               /* Commit persistent root changes (deleted persistent roots 
included) */
+
+               COStoreTransaction *transaction = [[COStoreTransaction alloc] 
init];
+               [self recordBeginUndoGroupWithMetadata: metadata];
+
+               for (COPersistentRoot *persistentRoot in persistentRoots)
+               {
+                       [persistentRoot saveCommitWithMetadata: metadata 
transaction: transaction];
+               }
+               
+               /* Add persistent root deletions to the transaction */
+               
+               for (COPersistentRoot *persistentRoot in persistentRoots)
+               {
+                       ETUUID *uuid = [persistentRoot UUID];
+                       
+                       if ([_persistentRootsPendingDeletion containsObject: 
persistentRoot])
+                       {
+                               [transaction deletePersistentRoot: uuid];
+                               [self recordPersistentRootDeletion: 
persistentRoot];
+                       }
+                       else if ([_persistentRootsPendingUndeletion 
containsObject: persistentRoot])
+                       {
+                               [transaction undeletePersistentRoot: uuid];
+                               [self recordPersistentRootUndeletion: 
persistentRoot];
+                       }
+               }
+
+               /* Update transaction IDs (can't add to the transaction after 
this) */
+               
+               for (ETUUID *uuid in [transaction persistentRootUUIDs])
+               {
+                       COPersistentRoot *persistentRoot = [self 
persistentRootForUUID: uuid];
+
+                       persistentRoot.lastTransactionID = [transaction 
setOldTransactionID: persistentRoot.lastTransactionID
+                                                                               
                                          forPersistentRoot: uuid];
+               }
+               
+               /* Update _persistentRootsPendingDeletion and 
_persistentRootsPendingUndeletion and unload
+                  persistent roots. */
+               
+               for (COPersistentRoot *persistentRoot in persistentRoots)
+               {
+                       if ([_persistentRootsPendingDeletion containsObject: 
persistentRoot])
+                       {
+                               [_persistentRootsPendingDeletion removeObject: 
persistentRoot];
                                
-    ETAssert([_store commitStoreTransaction: transaction]);
-       COCommandGroup *command = [self recordEndUndoGroupWithUndoTrack: track];
-    
-       /* For a commit triggered by undo/redo on a COUndoTrack, the command is 
nil */
-       [self didCommitWithCommand: command persistentRoots: persistentRoots];
-
-       if (anError != NULL)
-       {
-               *anError = nil;
+                               [self unloadPersistentRoot: persistentRoot];
+                       }
+                       else if ([_persistentRootsPendingUndeletion 
containsObject: persistentRoot])
+                       {
+                               [_persistentRootsPendingUndeletion 
removeObject: persistentRoot];
+                       }
+               }
+                                                                               
                
+                                       
+               ETAssert([_store commitStoreTransaction: transaction]);
+               COCommandGroup *command = [self 
recordEndUndoGroupWithUndoTrack: track];
+               
+               /* For a commit triggered by undo/redo on a COUndoTrack, the 
command is nil */
+               [self didCommitWithCommand: command persistentRoots: 
persistentRoots];
+
+               if (anError != NULL)
+               {
+                       *anError = nil;
+               }
+       }
+       @finally
+       {
+               _inCommit = NO;
        }
        return YES;
 }

Modified: trunk/Etoile/Frameworks/CoreObject/Tests/Core/TestEditingContext.m
URL: 
http://svn.gna.org/viewcvs/etoile/trunk/Etoile/Frameworks/CoreObject/Tests/Core/TestEditingContext.m?rev=10660&r1=10659&r2=10660&view=diff
==============================================================================
--- trunk/Etoile/Frameworks/CoreObject/Tests/Core/TestEditingContext.m  
(original)
+++ trunk/Etoile/Frameworks/CoreObject/Tests/Core/TestEditingContext.m  Sat Mar 
29 23:04:46 2014
@@ -280,4 +280,27 @@
        UKRaisesException([r2cxt2 parentRevision]);
 }
 
+- (void) testCommitWithinCommitNotificationIllegal
+{
+       __block BOOL receivedNotification = NO;
+       __block BOOL insideCommit = NO;
+       
+       [[NSNotificationCenter defaultCenter] addObserverForName: 
COEditingContextDidChangeNotification
+                                                                               
                          object: ctx
+                                                                               
                           queue: nil
+                                                                               
                  usingBlock: ^(NSNotification *notif) {
+                                                                               
                          receivedNotification = YES;
+                                                                               
                          UKTrue(insideCommit);
+                                                                               
                          UKRaisesException([ctx commit]);
+                                                                               
                  }];
+               
+       COPersistentRoot *persistentRoot = [ctx 
insertNewPersistentRootWithEntityName: @"Anonymous.OutlineItem"];
+
+       insideCommit = YES;
+       [ctx commit];
+       insideCommit = NO;
+       
+       UKTrue(receivedNotification);
+}
+
 @end

Modified: trunk/Etoile/Frameworks/CoreObject/Tests/TestCommon.m
URL: 
http://svn.gna.org/viewcvs/etoile/trunk/Etoile/Frameworks/CoreObject/Tests/TestCommon.m?rev=10660&r1=10659&r2=10660&view=diff
==============================================================================
--- trunk/Etoile/Frameworks/CoreObject/Tests/TestCommon.m       (original)
+++ trunk/Etoile/Frameworks/CoreObject/Tests/TestCommon.m       Sat Mar 29 
23:04:46 2014
@@ -227,7 +227,7 @@
        {
                [FMDatabase logOpenDatabases];
 
-               const int expectedOpenDatabases = 3;
+               const int expectedOpenDatabases = 4;
                if ([FMDatabase countOfOpenDatabases] > expectedOpenDatabases)
                {
                        NSLog(@"ERROR: Expected only %d SQLite database 
connections to still be open.", expectedOpenDatabases);


_______________________________________________
Etoile-cvs mailing list
[email protected]
https://mail.gna.org/listinfo/etoile-cvs

Reply via email to