looking for a memory problem

  • Dear list,

    I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is

    * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
        frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
        frame #1: 0x00000001098147d0
        frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
        frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
        frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
        frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
        frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
        frame #7: 0x0000000100001574 TeXnicle`start + 52

    I've tried disabling various parts of the app trying to isolate where the issue might be.

    Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?

    Cheers,

    Martin
  • Hi Martin,

    The issue exists in "[pool drain]" part of code.

    Hope these links might help you:
    http://developer.apple.com/library/mac/#technotes/tn2124/_index.html
    http://stackoverflow.com/questions/7194586/help-with-crash-log

    Regards,
    Lakshmi

    On 17-Jul-2012, at 4:00 PM, Martin Hewitson wrote:

    > Dear list,
    >
    > I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is
    >
    > * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    > frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
    > frame #1: 0x00000001098147d0
    > frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
    > frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
    > frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
    > frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
    > frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
    > frame #7: 0x0000000100001574 TeXnicle`start + 52
    >
    >
    > I've tried disabling various parts of the app trying to isolate where the issue might be.
    >
    > Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?
    >
    > Cheers,
    >
    > Martin
  • Have you tried using the zombies instrument?

    On 17 Jul 2012, at 11:30, Martin Hewitson <martin.hewitson...> wrote:

    > Dear list,
    >
    > I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is
    >
    > * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    > frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
    > frame #1: 0x00000001098147d0
    > frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
    > frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
    > frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
    > frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
    > frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
    > frame #7: 0x0000000100001574 TeXnicle`start + 52
    >
    >
    > I've tried disabling various parts of the app trying to isolate where the issue might be.
    >
    > Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?
    >
    > Cheers,
    >
    > Martin
  • I've seen this kind of thing before when I have released an object that was allocated in the autorelease pool. Have a go over of the objects you release and make sure that you own them when you release them.

    On Jul 17, 2012, at 6:30 AM, Martin Hewitson wrote:

    > Dear list,
    >
    > I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is
    >
    > * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    > frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
    > frame #1: 0x00000001098147d0
    > frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
    > frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
    > frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
    > frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
    > frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
    > frame #7: 0x0000000100001574 TeXnicle`start + 52
    >
    >
    > I've tried disabling various parts of the app trying to isolate where the issue might be.
    >
    > Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?
    >
    > Cheers,
    >
    > Martin

    Charlie Dickman
    <3tothe4th...>
  • Thanks to all. Due to the various clues and tips I finally tracked down the problem. I was creating a bunch of autoreleased objects within an autorelease pool of an NSOperation then storing them. But of course, when the pool is drained they all get released and boom!

    Thanks,

    Martin

    On 17, Jul, 2012, at 03:29 PM, Charlie Dickman <3tothe4th...> wrote:

    > I've seen this kind of thing before when I have released an object that was allocated in the autorelease pool. Have a go over of the objects you release and make sure that you own them when you release them.
    >
    > On Jul 17, 2012, at 6:30 AM, Martin Hewitson wrote:
    >
    >> Dear list,
    >>
    >> I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is
    >>
    >> * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    >> frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
    >> frame #1: 0x00000001098147d0
    >> frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
    >> frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
    >> frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
    >> frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
    >> frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
    >> frame #7: 0x0000000100001574 TeXnicle`start + 52
    >>
    >>
    >> I've tried disabling various parts of the app trying to isolate where the issue might be.
    >>
    >> Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?
    >>
    >> Cheers,
    >>
    >> Martin
    >
    > Charlie Dickman
    > <3tothe4th...>
    >
    >
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • Actually, to follow up a little. This is what I was doing:

    Within the operation's main I create a bunch of autoreleased objects and put this in an autoreleased array. In (very) rough code I do:

    @property (retain) NSArray *words;

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NSArray *someWords = [self generateABunchOfAutoReleasedObjectsInAnAutoreleasedArray];
    self.words = myAutoreleasedArray;

    // inform delegate that we have a list of words ready for collection
    [self.delegate performSelectorOnMainThread:  withObject:self waitUntilDone:NO];

    [pool drain];

    So I must not be understanding something (likely) because I thought that the autoreleased objects get retained when I add them to the autoreleased array. Then when I set the array to the property, it gets retained and so draining the pool shouldn't result in releasing the objects, should it?

    And, in fact, with some judicious log statements I see the objects don't get released when the pool drains, only much later when the document is closed.

    And yet, if I don't assign the autoreleased array to the property then I never see the crash and I see the objects being deallocated when the pool drains.

    I'd be grateful if somebody could point out the (stupid) mistake. Currently I'm stumped.

    Just occasionally I see a different backtrace like this:
    * thread #8: tid = 0x3703, 0x00007fff98d4afde libobjc.A.dylib`objc_release + 14, stop reason = EXC_BAD_ACCESS (code, address=0x0)
        frame #0: 0x00007fff98d4afde libobjc.A.dylib`objc_release + 14
        frame #1: 0x00007fff98d4a2b0 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 464
        frame #2: 0x00007fff97624a62 CoreFoundation`_CFAutoreleasePoolPop + 34
        frame #3: 0x00007fff92fdbb70 AppKit`-[NSSpellChecker _checkSpellingOfString:startingAt:language:wrap:inSpellDocumentWithTag:wordCount:reconnectOnError:] + 1379
        frame #4: 0x00007fff92fdbbd0 AppKit`-[NSSpellChecker checkSpellingOfString:startingAt:language:wrap:inSpellDocumentWithTag:wordCount:reconnectOnError:] + 75
        frame #5: 0x00007fff92fdbd06 AppKit`-[NSSpellChecker checkSpellingOfString:startingAt:] + 55
        frame #6: 0x00000001000b6925 TeXnicle`-[NSString(Spelling) listOfMisspelledWords] + 342 at NSString+Spelling.m:51
        frame #7: 0x00000001000b6b2e TeXnicle`-[TPSpellCheckFileOperation main] + 227 at TPSpellCheckFileOperation.m:68
        frame #8: 0x00007fff9586fbb6 Foundation`-[__NSOperationInternal start] + 684
        frame #9: 0x00007fff958773d1 Foundation`__block_global_6 + 129
        frame #10: 0x00007fff99ecbf3d libdispatch.dylib`_dispatch_call_block_and_release + 15
        frame #11: 0x00007fff99ec80fa libdispatch.dylib`_dispatch_client_callout + 8
        frame #12: 0x00007fff99ec923e libdispatch.dylib`_dispatch_worker_thread2 + 304
        frame #13: 0x00007fff93784ceb libsystem_c.dylib`_pthread_wqthread + 404
        frame #14: 0x00007fff9376f1b1 libsystem_c.dylib`start_wqthread + 13

    Could the problem be that I'm using the shared spell checker on multiple threads?

    So after some googling I came across this post:

    http://www.cocoabuilder.com/archive/cocoa/132829-nsspellchecker-crashes-aft
    er-64k-words.html


    It seems to hint at similar problems, but not exactly the same.

    Oh well, more digging to do.

    Cheers,

    Martin

    On 17, Jul, 2012, at 04:09 PM, Martin Hewitson <martin.hewitson...> wrote:

    > Thanks to all. Due to the various clues and tips I finally tracked down the problem. I was creating a bunch of autoreleased objects within an autorelease pool of an NSOperation then storing them. But of course, when the pool is drained they all get released and boom!
    >
    > Thanks,
    >
    > Martin
    >
    > On 17, Jul, 2012, at 03:29 PM, Charlie Dickman <3tothe4th...> wrote:
    >
    >> I've seen this kind of thing before when I have released an object that was allocated in the autorelease pool. Have a go over of the objects you release and make sure that you own them when you release them.
    >>
    >> On Jul 17, 2012, at 6:30 AM, Martin Hewitson wrote:
    >>
    >>> Dear list,
    >>>
    >>> I've been trying to track down a crash that happens sometimes when a document is closed in my NSPersistentDocument based app. This started to appear during the process of going from GC to non-GC. But it only happens after an undetermined number of document closes and reopens. The error message I get in the debugger is
    >>>
    >>> * thread #1: tid = 0x2103, 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16, stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
    >>> frame #0: 0x00007fff8f614e90 libobjc.A.dylib`objc_msgSend + 16
    >>> frame #1: 0x00000001098147d0
    >>> frame #2: 0x00007fff8f61a03c libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*) + 434
    >>> frame #3: 0x00007fff938c8915 CoreFoundation`_CFAutoreleasePoolPop + 37
    >>> frame #4: 0x00007fff8b8bb6cf Foundation`-[NSAutoreleasePool drain] + 154
    >>> frame #5: 0x00007fff90146115 AppKit`-[NSApplication run] + 634
    >>> frame #6: 0x00007fff903c2244 AppKit`NSApplicationMain + 867
    >>> frame #7: 0x0000000100001574 TeXnicle`start + 52
    >>>
    >>>
    >>> I've tried disabling various parts of the app trying to isolate where the issue might be.
    >>>
    >>> Could anyone offer any advice on how I might better track this down? Perhaps using instruments in some way?
    >>>
    >>> Cheers,
    >>>
    >>> Martin
    >>
    >> Charlie Dickman
    >> <3tothe4th...>
    >>
    >>
  • On Jul 17, 2012, at 9:39 AM, Martin Hewitson wrote:

    > @property (retain) NSArray *words;
    >
    > NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    >
    > NSArray *someWords = [self generateABunchOfAutoReleasedObjectsInAnAutoreleasedArray];

    You may be doing something wrong in -generateABunchOfAutoReleasedObjectsInAnAutoreleasedArray.

    > self.words = myAutoreleasedArray;

    I assume you meant someWords here.

    > So I must not be understanding something (likely) because I thought that the autoreleased objects get retained when I add them to the autoreleased array. Then when I set the array to the property, it gets retained and so draining the pool shouldn't result in releasing the objects, should it?

    Well, it should result in sending -release messages to the objects.  That's what the autorelease pool does, it sends release to the objects it contains when it's drained.  However, those release messages are for the original ownership from when the objects were created.  You have put them in a collection, which establishes a separate, additional ownership, and you've retained the collection.  So, you're correct that the objects should survive the draining of the autorelease pool.

    > And, in fact, with some judicious log statements I see the objects don't get released when the pool drains, only much later when the document is closed.
    >
    > And yet, if I don't assign the autoreleased array to the property then I never see the crash and I see the objects being deallocated when the pool drains.

    Don't guess.  Don't try to intuit the behavior.  Use the Zombies instrument.  When it tells you a deallocated object has been messaged, review the recorded retain and release history of that object and see which code is not following the memory management rules – that is, which has under-retained or over-released the object.

    Regards,
    Ken
  • On 17 Jul 2012, at 9:39 AM, Martin Hewitson wrote:

    > So I must not be understanding something (likely) because I thought that the autoreleased objects get retained when I add them to the autoreleased array. Then when I set the array to the property, it gets retained and so draining the pool shouldn't result in releasing the objects, should it?

    Porting code into and out of garbage collection is Hard.

    Stop thinking about "autoreleased" and think in terms of "ownership" instead. How many claims (retain, alloc, container insertions, I won't go through the list here) on an object does _your_ code make, net of relinquishments (release, autorelease) of _your_ claims?

    Better yet, use ARC if you possibly can. Even if you have to run on Snow Leopard, and live with unsafe_unretained, most of your worries are taken care of. Bear in mind, though, that while ARC makes reference-counted memory management easier, it doesn't excuse you from understanding it.

    > And, in fact, with some judicious log statements I see the objects don't get released when the pool drains, only much later when the document is closed.

    Do not log. You'll never catch all of the events. Learn Instruments (even if you have to pause your development) and set "Record reference counts" and "Enable NSZombie detection" in the Allocations instrument. Conduct your audit there.

    > And yet, if I don't assign the autoreleased array to the property then I never see the crash and I see the objects being deallocated when the pool drains.

    Which won't really tell you anything, even if you've lucked into silencing your crash. Cocoa will retain and release your objects for reasons of its own. If you got the retain count low enough that a particular drain deallocates an object, you may simply have balanced Cocoa-initiated retains that the framework itself will attempt to balance later.

    > I'd be grateful if somebody could point out the (stupid) mistake. Currently I'm stumped.

    Except for passing through NSSpellChecker, the trace doesn't tell you much. As I just said, it looks as though Cocoa, within its own code, is trying to balance a retain it did itself.

    > Could the problem be that I'm using the shared spell checker on multiple threads?

    YES. At least it's a very big item on your list of problems. I haven't found any documentation that affirmatively says NSSpellChecker is thread-safe. Therefore it isn't. It's in AppKit, which counts against it, and it manages a UI facility, which excludes it.

    Try executing your checks on the main thread. Communicating the results to the calling thread will require some care.

    — F

    --
    Fritz Anderson -- Xcode 4 Unleashed: Now in stores! -- <http://x4u.manoverboard.org/>
  • On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:

    > This started to appear during the process of going from GC to non-GC.

    What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.

    --
    Sean
  • What is ARC and where can I read learn about it?

    On Jul 17, 2012, at 11:42 AM, Sean McBride wrote:

    > On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >
    >> This started to appear during the process of going from GC to non-GC.
    >
    > What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.
    >
    > --
    > Sean

    Charlie Dickman
    <3tothe4th...>
  • On 17 Jul 2012, at 10:26 AM, Fritz Anderson wrote:

    > How many claims (… container insertions …) on an object does _your_ code make…?

    And here's an example of the kind of thinking I was advising you against. That containers almost always retain their contents is a detail in the container classes' code, not in yours. Don't count insertions as ownership increments.

    — F
  • Never mind. It originates in Xcode 4 and I don't use Xcode 4.

    On Jul 17, 2012, at 11:52 AM, Charlie Dickman wrote:

    > What is ARC and where can I read learn about it?
    >
    > On Jul 17, 2012, at 11:42 AM, Sean McBride wrote:
    >
    >> On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >>
    >>> This started to appear during the process of going from GC to non-GC.
    >>
    >> What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.
    >>
    >> --
    >> Sean
    >
    > Charlie Dickman
    > <3tothe4th...>

    Charlie Dickman
    <3tothe4th...>
  • On Tue, 17 Jul 2012 11:52:56 -0400, Charlie Dickman said:

    > What is ARC and where can I read learn about it?

    Googling "ARC Cocoa" should find you your answer.  Here's a starting point:

    <http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-Transiti
    oningToARC/Introduction/Introduction.html
    >

    --
    Sean
  • On Tue, 17 Jul 2012 12:03:49 -0400, Charlie Dickman said:

    > Never mind. It originates in Xcode 4 and I don't use Xcode 4.

    Then I suggest staying with GC.  Why do you want to switch away from GC?  Yes, it's deprecated, but so is Xcode 3, so....

    --
    Sean
  • On 17, Jul, 2012, at 05:42 PM, Sean McBride <sean...> wrote:

    > On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >
    >> This started to appear during the process of going from GC to non-GC.
    >
    > What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.

    That was my original plan, but ARC is 64-bit only, right? I'm not sure I'm ready to drop 32-bit support yet, at least not without canvasing opinion from the users.

    Martin

    >
    > --
    > Sean
    >
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On 17, Jul, 2012, at 05:26 PM, Fritz Anderson <fritza...> wrote:

    >
    >> Could the problem be that I'm using the shared spell checker on multiple threads?
    >
    > YES. At least it's a very big item on your list of problems. I haven't found any documentation that affirmatively says NSSpellChecker is thread-safe. Therefore it isn't. It's in AppKit, which counts against it, and it manages a UI facility, which excludes it.

    Oh, that's not good. I wanted to be spell checking multiple files in the background. I will need to think carefully if there's a way to call -checkSpellingOfString:startingAt on the main thread.

    Martin

    >
    > Try executing your checks on the main thread. Communicating the results to the calling thread will require some care.
    >
    > — F
    >
    > --
    > Fritz Anderson -- Xcode 4 Unleashed: Now in stores! -- <http://x4u.manoverboard.org/>
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • So my potential solution for this is:

    NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
    __block NSRange range = NSMakeRange(0, 0);
    while (range.location < [aString length]) {

        dispatch_sync(dispatch_get_main_queue(), ^{
          range = [checker checkSpellingOfString:aString startingAt:range.location];
        });

      // update range
    }

    Where this piece of code will run on multiple threads.

    Does this look like a reasonable approach to ensuring thread safety?

    Martin

    On 17, Jul, 2012, at 06:33 PM, Martin Hewitson <martin.hewitson...> wrote:

    >
    > On 17, Jul, 2012, at 05:26 PM, Fritz Anderson <fritza...> wrote:
    >
    >>
    >>> Could the problem be that I'm using the shared spell checker on multiple threads?
    >>
    >> YES. At least it's a very big item on your list of problems. I haven't found any documentation that affirmatively says NSSpellChecker is thread-safe. Therefore it isn't. It's in AppKit, which counts against it, and it manages a UI facility, which excludes it.
    >
    > Oh, that's not good. I wanted to be spell checking multiple files in the background. I will need to think carefully if there's a way to call -checkSpellingOfString:startingAt on the main thread.
    >
    > Martin
    >
    >>
    >> Try executing your checks on the main thread. Communicating the results to the calling thread will require some care.
    >>
    >> — F
    >>
    >> --
    >> Fritz Anderson -- Xcode 4 Unleashed: Now in stores! -- <http://x4u.manoverboard.org/>
    >>
    >
    > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    > Martin Hewitson
    > Albert-Einstein-Institut
    > Max-Planck-Institut fuer
    > Gravitationsphysik und Universitaet Hannover
    > Callinstr. 38, 30167 Hannover, Germany
    > Tel: +49-511-762-17121, Fax: +49-511-762-5861
    > E-Mail: <martin.hewitson...>
    > WWW: http://www.aei.mpg.de/~hewitson
    > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >
    >
    >
    >
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On 17 Jul 2012, at 18:10, Martin Hewitson wrote:

    > So my potential solution for this is:
    >
    > NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
    > __block NSRange range = NSMakeRange(0, 0);
    > while (range.location < [aString length]) {
    >
    > dispatch_sync(dispatch_get_main_queue(), ^{
    > range = [checker checkSpellingOfString:aString startingAt:range.location];
    > });
    >
    > // update range
    > }
    >
    > Where this piece of code will run on multiple threads.
    >
    > Does this look like a reasonable approach to ensuring thread safety?

    Two potential problems:

    - If the main thread is waiting on your thread/queue to do its work, then you’ve got a deadlock. Hopefully your code never does that

    - If you run this code on the main thread, it’ll deadlock also. You might find something like KSThreadProxy a neater solution to this problem.
  • On 18/07/2012, at 1:42 AM, Sean McBride wrote:

    > writing for GC and ARC is not so different, and you can even switch over slowly

    Could you elaborate on what you mean by "switch over slowly"? I think I understand the idea if one is coming from manual reference counting, but I thought GC was essentially all or nothing. Or am I reading something into your words that you didn't mean?

    --
    Shane Stanley <sstanley...>
    'AppleScriptObjC Explored' <www.macosxautomation.com/applescript/apps/>
  • On Jul 17, 2012, at 11:31 AM, Martin Hewitson wrote:

    > On 17, Jul, 2012, at 05:42 PM, Sean McBride <sean...> wrote:
    >
    >> On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >>
    >>> This started to appear during the process of going from GC to non-GC.
    >>
    >> What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.
    >
    > That was my original plan, but ARC is 64-bit only, right? I'm not sure I'm ready to drop 32-bit support yet, at least not without canvasing opinion from the users.
    >
    > Martin

    I’ve got an app that’s backward-compatible all the way to 10.4/PPC, to which I added Sparkle support about half a year ago. In that half year, according to the Sparkle stats, 93.99% of my users have been on 64-bit Intel CPUs. Of the 6% who are not on 64-bit Intel, only  2.72% are using 32-bit Intel, followed by 1.91% on 32-bit PPC and 1.38% on 64-bit PPC (i.e. the G5).

    Needless to say, the next major release of my app will not be backward-compatible all the way to 10.4/PPC. I’d caution against putting too much effort into supporting what may well be a statistically insignificant portion of your user base. If you’re porting from GC, port it to ARC, and you’ll save yourself a great deal of time and headaches.

    Charles
  • > So my potential solution for this is:
    >
    > NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
    > __block NSRange range = NSMakeRange(0, 0);
    > while (range.location < [aString length]) {
    >
    > dispatch_sync(dispatch_get_main_queue(), ^{
    > range = [checker checkSpellingOfString:aString startingAt:range.location];
    > });
    >
    > // update range
    > }
    >
    > Where this piece of code will run on multiple threads.
    >
    > Does this look like a reasonable approach to ensuring thread safety?

    In addition to the potential for deadlocking that Mike warned about, it's probably best to be paranoid about +sharedSpellChecker. Hopefully that method uses a thread-safe singleton allocation (eg: via dispatch_once), but you never know. I'd move that method call to the main thread too.

    However, by using this approach you're not really doing the spell checking work on a background thread. The work is going to be done synchronously on the main thread, blocking your GUI (and whatever else). Maybe you should investigate using -requestCheckingOfString:etc: instead.

    ~Martin
  • On 18, Jul, 2012, at 04:17 AM, Martin Wierschin <martin...> wrote:

    >> So my potential solution for this is:
    >>
    >> NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
    >> __block NSRange range = NSMakeRange(0, 0);
    >> while (range.location < [aString length]) {
    >>
    >> dispatch_sync(dispatch_get_main_queue(), ^{
    >> range = [checker checkSpellingOfString:aString startingAt:range.location];
    >> });
    >>
    >> // update range
    >> }
    >>
    >> Where this piece of code will run on multiple threads.
    >>
    >> Does this look like a reasonable approach to ensuring thread safety?
    >
    > In addition to the potential for deadlocking that Mike warned about, it's probably best to be paranoid about +sharedSpellChecker. Hopefully that method uses a thread-safe singleton allocation (eg: via dispatch_once), but you never know. I'd move that method call to the main thread too.
    >
    > However, by using this approach you're not really doing the spell checking work on a background thread. The work is going to be done synchronously on the main thread, blocking your GUI (and whatever else). Maybe you should investigate using -requestCheckingOfString:etc: instead.

    Oh, this looks perfect. Don't know how I missed that API!

    Thanks,

    Martin

    >
    > ~Martin
    >
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On 18, Jul, 2012, at 03:07 AM, Charles Srstka <cocoadev...> wrote:

    > On Jul 17, 2012, at 11:31 AM, Martin Hewitson wrote:
    >
    >> On 17, Jul, 2012, at 05:42 PM, Sean McBride <sean...> wrote:
    >>
    >>> On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >>>
    >>>> This started to appear during the process of going from GC to non-GC.
    >>>
    >>> What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.
    >>
    >> That was my original plan, but ARC is 64-bit only, right? I'm not sure I'm ready to drop 32-bit support yet, at least not without canvasing opinion from the users.
    >>
    >> Martin
    >
    > I’ve got an app that’s backward-compatible all the way to 10.4/PPC, to which I added Sparkle support about half a year ago. In that half year, according to the Sparkle stats, 93.99% of my users have been on 64-bit Intel CPUs. Of the 6% who are not on 64-bit Intel, only  2.72% are using 32-bit Intel, followed by 1.91% on 32-bit PPC and 1.38% on 64-bit PPC (i.e. the G5).

    That's very interesting. Not least of all that Sparkle can provide these stats. I didn't know that. I guess today I'm integrating Sparkle and preparing to gather some statistics. I'd very much like to drop 32-bit support in favour of ARC!

    Martin

    >
    > Needless to say, the next major release of my app will not be backward-compatible all the way to 10.4/PPC. I’d caution against putting too much effort into supporting what may well be a statistically insignificant portion of your user base. If you’re porting from GC, port it to ARC, and you’ll save yourself a great deal of time and headaches.
    >
    > Charles
    >

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Martin Hewitson
    Albert-Einstein-Institut
    Max-Planck-Institut fuer
        Gravitationsphysik und Universitaet Hannover
    Callinstr. 38, 30167 Hannover, Germany
    Tel: +49-511-762-17121, Fax: +49-511-762-5861
    E-Mail: <martin.hewitson...>
    WWW: http://www.aei.mpg.de/~hewitson
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • On Wed, 18 Jul 2012 11:03:31 +1000, Shane Stanley said:

    >> writing for GC and ARC is not so different, and you can even switch
    > over slowly
    >
    > Could you elaborate on what you mean by "switch over slowly"? I think I
    > understand the idea if one is coming from manual reference counting, but
    > I thought GC was essentially all or nothing. Or am I reading something
    > into your words that you didn't mean?

    Shane,

    Sure.  Well, it's all or nothing from the perspective of compiler flags.  When you actually throw the switch from GC to ARC, it's just a one-shot.

    But you can prepare your code slowly, in a way that supports both.  ex:
    - replace all use of NSAllocateCollectable with malloc/free
    - change 'retain' to 'strong' in property declarations
    - replace CFMakeCollectable with CFBridgingRelease
    - replace NSMakeCollectable with a nil check and CFBridgingRelease

    That's where I'm at anyway.  I'm looking forward to the performance benefits of ARC, but will miss the GC programming model.

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On Jul 18, 2012, at 12:45 AM, Martin Hewitson wrote:

    > On 18, Jul, 2012, at 03:07 AM, Charles Srstka <cocoadev...> wrote:
    >
    >> On Jul 17, 2012, at 11:31 AM, Martin Hewitson wrote:
    >>
    >>> On 17, Jul, 2012, at 05:42 PM, Sean McBride <sean...> wrote:
    >>>
    >>>> On Tue, 17 Jul 2012 12:30:39 +0200, Martin Hewitson said:
    >>>>
    >>>>> This started to appear during the process of going from GC to non-GC.
    >>>>
    >>>> What do you mean "non-GC"?  I strongly suggest going from GC to ARC, not from GC back to the stone-age retain-release.  Although quite different 'under the hood', writing for GC and ARC is not so different, and you can even switch over slowly.
    >>>
    >>> That was my original plan, but ARC is 64-bit only, right? I'm not sure I'm ready to drop 32-bit support yet, at least not without canvasing opinion from the users.
    >>>
    >>> Martin
    >>
    >> I’ve got an app that’s backward-compatible all the way to 10.4/PPC, to which I added Sparkle support about half a year ago. In that half year, according to the Sparkle stats, 93.99% of my users have been on 64-bit Intel CPUs. Of the 6% who are not on 64-bit Intel, only  2.72% are using 32-bit Intel, followed by 1.91% on 32-bit PPC and 1.38% on 64-bit PPC (i.e. the G5).
    >
    >
    > That's very interesting. Not least of all that Sparkle can provide these stats. I didn't know that. I guess today I'm integrating Sparkle and preparing to gather some statistics. I'd very much like to drop 32-bit support in favour of ARC!

    The thing about it is that you appear to be using block-based APIs already, which means you already require at least 10.6, which means that you’re already Intel-only and have no PPC users, so if your user base is like mine, you’ll lose only 2.72% of your userbase by dropping 32-bit.

    And honestly, your numbers probably *are* pretty close to mine. 32-bit Intel Macs are pretty rare. The most common are the original white/black MacBooks, which were only in production for about six months in 2006, since Apple switched to 64-bit processors almost immediately after they switched to Intel.

    Charles
  • On 19/07/2012, at 1:35 AM, Sean McBride wrote:

    > But you can prepare your code slowly, in a way that supports both.  ex:
    > - replace all use of NSAllocateCollectable with malloc/free
    > - change 'retain' to 'strong' in property declarations
    > - replace CFMakeCollectable with CFBridgingRelease
    > - replace NSMakeCollectable with a nil check and CFBridgingRelease

    Thanks -- I didn't realize the last two.

    So you are saying I can include things like __bridge and __bridge_retained in GC code, and they'll just be ignored until I flick the switch?

    --
    Shane Stanley <sstanley...>
    'AppleScriptObjC Explored' <www.macosxautomation.com/applescript/apps/>
  • On Thu, 19 Jul 2012 09:43:12 +1000, Shane Stanley said:

    >> But you can prepare your code slowly, in a way that supports both.  ex:
    >> - replace all use of NSAllocateCollectable with malloc/free
    >> - change 'retain' to 'strong' in property declarations
    >> - replace CFMakeCollectable with CFBridgingRelease
    >> - replace NSMakeCollectable with a nil check and CFBridgingRelease
    >
    > Thanks -- I didn't realize the last two.
    >
    > So you are saying I can include things like __bridge and
    > __bridge_retained in GC code, and they'll just be ignored until I flick
    > the switch?

    Actually, I wasn't saying that.  If you look in the header at how CFBridgingRelease is defined:

    #if __has_feature(objc_arc)

    NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
        return (__bridge_retained CFTypeRef)X;
    }

    #else

    NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
        return [(id)CFMakeCollectable(X) autorelease];
    }

    #endif

    you see that it does the expected thing in either GC or ARC.  Very useful.  Just beware passing nil, which is ok with NSMakeCollectable but not so at the CF layer.

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today