I hate to blame an OS bug but I see no other explanation, so here we go.

I have a program which uses NSOperationQueue heavily. It uses lots of
different queues each of which has a max concurrent operation count
set to 1. In this way, the NSOperationQueue functions as a
serialization mechanism, like a lock but easier to use. This is
extremely handy.

One unusual thing it does, which I think may be the thing that kills
it, is that it often enqueues a new operation from inside of an
existing one being run on the same queue. This is to provide more
granularity for cancellation or priorities. For example, when there is
a lot of data to process, the NSOperation I enqueue just processes the
first piece of it. Then when it's done processing, it enqueues a
second NSOperation which will continue the processing. This way if I
want to cancel processing or do some higher-priority operation, it
doesn't have to wait for everything.

Anyway, I very rarely get this exception:

*** -[NSInvocationOperation start]: receiver has already started or finished

And this then crashes the app, because it's happening on an internal
NSOperationQueue thread which doesn't have an exception handler. The
rarity made this really difficult to debug, but I finally twigged to
the problem and wrote a test case which reproduces the exception
easily... on some hardware. This is that test case:

#import <Foundation/Foundation.h>


@interface Tester : NSObject
{
    NSOperationQueue *_queue;
}

- (void)test;

@end

@implementation Tester

- (id)init
{
    if((self = [super init]))
    {
        _queue = [[NSOperationQueue alloc] init];
        [_queue setMaxConcurrentOperationCount:1];
    }
    return self;
}

- (void)test
{
    NSInvocationOperation *op = [[NSInvocationOperation alloc]
initWithTarget:self selector:_cmd object:nil];
    [_queue addOperation:op];
    [op release];
}

@end

int main(int argc, char **argv)
{
    [NSAutoreleasePool new];

    NSMutableArray *testers = [NSMutableArray array];
    int i;
    for(i = 0; i < 10; i++)
        [testers addObject:[[[Tester alloc] init] autorelease]];

    for(Tester *tester in testers)
        [tester test];

    while(1) sleep(1000);
}

Compile and run and wait. On my Mac Pro it throws that exception
crashes inside of ten seconds with this backtrace:

#0  0x96480ff4 in ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ ()
#1  0x9207ee3b in objc_exception_throw ()
#2  0x92db74de in -[NSOperation start] ()
#3  0x92db7112 in __runop ()
#4  0x902ae1f7 in _pthread_wqthread ()
#5  0x902ae0aa in start_wqthread ()

I've had a few other people test as well. Most of them said that they
experienced the same crash. Some said they saw no crash. This is not a
big surprise for a threading bug but it's a bit odd.

Anyway, I would really love for this to be my bug, because then I
could fix it and stop having this problem. If anyone sees anything I'm
doing wrong then please tell me what it is. Failing that, if anyone
happens to know of a workaround for this problem, I would love to hear
it. Otherwise it's off to write an NSOperationQueue replacement, a
task I'm steeling myself for but not looking forward to.

For the Apple types among us, I have filed this as rdar://6332143 .

Any tips, tricks, corrections, or criticisms are most welcome!

Mike
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to