• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Temporarily disabling autosave
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Temporarily disabling autosave


  • Subject: Re: Temporarily disabling autosave
  • From: Jerry Krinock <email@hidden>
  • Date: Fri, 19 Apr 2013 13:42:05 -0700

On 2013 Apr 19, at 12:37, Mike Abdullah <email@hidden> wrote:

> Why, what's wrong with cancelling a [auto]save?

That's a damned good question, Mike.  You're probably thinking that, hey, we lived without any autosaves from 1984 to 2011.  What's the big deal?  It turns out that you need to be really careful when playing around with an autosave that Cocoa has designated "not implicitly cancellable".  Search the internet for:

    deadlock in -[NSDocument performActivityWithSynchronousWaiting:usingBlock:]

to see some of the fun that people have had.

I have an app with a requirement similar to Steve's.  The app can do long-winded sequences of operations that take tens of seconds, and change the document.  So if I honored an autosave request during one these sequences, I'd have to interrupt the operations (which is tricky), save, and then save again at the end after the changes were done.

The solution: Upon receiving a non-cancellable autosave message while other operations are in progress, I stash the completion handler that Cocoa sends in the message, create an operation to "really autosave" later, and add it to my operation queue.

I had to do other stuff to deal with corner cases such as Revert, being in the Versions Browser, etc.  Below, I've snipped out a few of the relevant methods from my NSDocument (actually it's NSPersistentDocument, which adds even more to the mess) implementation.

Jerry

- (void)autosaveWithImplicitCancellability:(BOOL)autosavingIsImplicitlyCancellable
                         completionHandler:(void (^)(NSError *errorOrNil))completionHandler {
    if (autosavingIsImplicitlyCancellable) {
        // We can cancel this autosave if we want to.
        if (
            // If operations are currently in progress, cancel it.  This is
            // because we will save when our operations are complete.
            ([[[self operationQueue] operations] count] != 0)
            ||
            // Prevent unnecessary saves, in case the document is in a watched folder
            // that triggers a syncing mechanism.
            (![self isDocumentEdited])
            ) {
            // Cancel it.
            completionHandler([NSError errorWithDomain:NSCocoaErrorDomain
                                                  code:NSUserCancelledError
                                              userInfo:nil]) ;
            return ;
        }
    }

    NSMutableDictionary* info = [NSMutableDictionary dictionary] ;
    [info setValue:Block_copy(completionHandler) forKey:constKeyCompletionHandler] ;
    [info setObject:self forKey:constKeyDocument] ;
    // The following adds a task to my home-made main operation
    // queue which I wrote back in the Leopard days …
    NSArray* selectorNames = [NSArray arrayWithObject:@"reallyAutosave"] ;
    [[self operationQueue] queueGroup:@"Non-cancellable Autosave"
                                addon:NO
                        selectorNames:selectorNames
                                 info:info
                                block:NO
                                owner:self
                           doneThread:nil
                           doneTarget:nil
                         doneSelector:NULL
                         keepWithNext:NO] ;
    [self setSavingState:1] ;
}


// This is the method that runs (in the main thread) to fulfill "reqllyAutosave"

- (void)reallyAutosave_unsafe {
    NSDictionary* info = [self info] ;
    Bkmslf* bkmslf = [info objectForKey:constKeyDocument] ;

    void (^completionHandler)(NSError*) = [info objectForKey:constKeyCompletionHandler] ;

    [bkmslf reallyAutosaveWithCompletionHandler:completionHandler] ;
    // Note: completionHandler will be released by the receiver of the above message.
}

/*!
 @brief    Override of new asynchronous saving method invoked by Cocoa
 when running in Mac OS X 10.7 or later.
 */
- (void)saveToURL:(NSURL *)url
           ofType:(NSString *)typeName
 forSaveOperation:(NSSaveOperationType)saveOperation
completionHandler:(void (^)(NSError *errorOrNil))completionHandler {

    [self prepareForSaveOperation:saveOperation] ;

    // Despite my best efforts to play nice with autosave, it still occasionally
    // deadlocked on me, until I added this:
    if ([self savingState] > 1) {
        NSLog(@"Warning 089-0831  Cancelling save, now in state %ld, from %@ to avoid deadlock", (long)[self savingState], SSYDebugCaller()) ;
        completionHandler([NSError errorWithDomain:NSCocoaErrorDomain
                                              code:NSUserCancelledError
                                          userInfo:nil]) ;
        return ;
    }
    [self setSavingState:2] ;

    [super saveToURL:url
              ofType:typeName
    forSaveOperation:saveOperation
   completionHandler:completionHandler] ;
}

- (void)doHousekeepingAfterSaveNotification:(NSNotification*)note {
    [self setSavingState:0] ;

    ...
}


_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden


  • Follow-Ups:
    • Re: Temporarily disabling autosave
      • From: Mike Abdullah <email@hidden>
    • Re: Temporarily disabling autosave
      • From: Steve Mills <email@hidden>
    • Re: Temporarily disabling autosave
      • From: Mike Abdullah <email@hidden>
    • Re: Temporarily disabling autosave
      • From: Alex Zavatone <email@hidden>
References: 
 >Temporarily disabling autosave (From: Steve Mills <email@hidden>)
 >Re: Temporarily disabling autosave (From: Mike Abdullah <email@hidden>)
 >Re: Temporarily disabling autosave (From: Steve Mills <email@hidden>)
 >Re: Temporarily disabling autosave (From: Mike Abdullah <email@hidden>)

  • Prev by Date: Re: Temporarily disabling autosave
  • Next by Date: Re: Temporarily disabling autosave
  • Previous by thread: Re: Temporarily disabling autosave
  • Next by thread: Re: Temporarily disabling autosave
  • Index(es):
    • Date
    • Thread