Skip navigation.
 
mlEXC_BAD_ACCESS with NSAutoReleasePool, NSThread and NSThreadWillExitNotification
FROM : John Zorko
DATE : Tue Oct 14 16:38:27 2008

Hello, all ...

I'm experiencing a crash after a thread exits.

Program received signal:  “EXC_BAD_ACCESS”.
(gdb) bt
#0  0x300c8c18 in objc_msgSend ()
#1  0x3067073a in NSPopAutoreleasePool ()
#2  0x306770ea in __NSFinalizeThreadData ()
#3  0x31446f6e in _pthread_tsd_cleanup ()
#4  0x31449ae4 in _pthread_exit ()
#5  0x3144b7e8 in pthread_exit ()
#6  0x30676e66 in +[NSThread exit] ()
#7  0x30673444 in __NSThread__main__ ()
#8  0x3144a824 in _pthread_body ()
#9  0x00000000 in ?? ()
(gdb)

The NSThread itself runs well enough.  It allocates an autorelease 
pool and releases it at the end, like i've seen many NSThreads do:

- (void)startInternal
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   .
   .
   .

   NSLog(@"- startInternal ending ... releasing pool");
   [pool release];
   
   exit;
}

I start this thread thusly:

- (void)start
{
   streamerThread = [[NSThread alloc] initWithTarget:self 
selector:@selector(startInternal) object:nil];
   
   [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(threadStopped)
                                         name:NSThreadWillExitNotification
                                         object:streamerThread];
   [streamerThread start];
   
   appDelegate.playbackThreadFinished = false;
}

... and the threadStopped method does this:

- (void)threadStopped
{
   NSLog(@"*** streamer thread has stopped ***");
   
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   
   appDelegate.playbackThreadFinished = true;
}

Now, in my main thread, I have an observer on playbackThreadFinished 
that I set up as soon as the app launches (before the other thread is 
created):

   [self addObserver:self forKeyPath:@"playbackThreadFinished" options:0 
context:nil];


... and when I see the "playbackThreadFinished" notification, I do 
things like see if there is a new song to play, etc.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                       change:(NSDictionary *)change context:(void *)context
{
   NSLog(@"observeValueForKeyPath -- current thread ID: %x, keypath: 
%s", [NSThread currentThread], [keyPath UTF8String]);

   if ([keyPath isEqualToString:@"isPlaying"])
   {
       .
       .
       .
       return;
   }
   else if ([keyPath isEqualToString:@"playbackThreadFinished"])
   {
       if (playbackThreadFinished)
       {
           // ... if we're in shuffle mode
           
           if (playShuffleFlag)
           {
               if ([self.shufflePlayedList count] < [self countOfSongList])
               {
                   NSUInteger randomSong;
                   
                   do
                   {
                       randomSong = (int)(rand() % [self countOfSongList]);
                   }
                   while ([self.shufflePlayedList indexOfObject:[NSNumber 
numberWithInt:randomSong]] != NSNotFound);
                   
                   NSLog(@"play shuffle advancing to song %i", randomSong);
                   NSNumber *num = [[NSNumber alloc] initWithInt:randomSong];
                   [self.shufflePlayedList addObject:num];
                   [num release];
                   
                   self.nextSong = randomSong;
               }
               else
               {
                   NSLog(@"shuffle play has played all songs in the list");
                   [artistViewController 
popToViewController:artistViewController.artistAlbumSongController 
animated:YES];
               }
           }
           
           // ... if we're in album play mode
           
           else if (playEntireAlbumFlag)
           {
               if (self.currentSong < [self countOfSongList] - 1)
               {
                   self.currentSong ++;
                   NSLog(@"play album advancing to song %d of %d", self.currentSong, 
[self countOfSongList]);
                   self.nextSong = currentSong;
               }
           }
           
           // ... if the user just picked a song from the list
           
           else if (self.currentSong > -1)
           {
               self.nextSong = self.currentSong;
               self.currentSong = -1;    // reset so the same song won't play again 
when it's finished :-)
           }
       }
       
       return;
   }
   else if ([keyPath isEqualToString:@"nextSong"])
   {
       .
       .
       .
       return;
   }
   
   [super observeValueForKeyPath:keyPath ofObject:object change:change 
context:context];
}

... the crash happens after observeValueForKeyPath finishes.  What 
confuses me is that the only NSAutoreleasePool allocated is the one 
the thread itself allocated, and I didn't autorelease anything in 
observeValueForKeyPath.  Yes i'm a Cocoa n00b ...

Regards,

John

Falling You - exploring the beauty of voice and sound
http://www.fallingyou.com

Related mailsAuthorDate
mlEXC_BAD_ACCESS with NSAutoReleasePool, NSThread and NSThreadWillExitNotification John Zorko Oct 14, 16:38
mlRe: EXC_BAD_ACCESS with NSAutoReleasePool, NSThread and NSThreadWillExitNotification Jonathan del Strot… Oct 14, 16:47
mlRe: EXC_BAD_ACCESS with NSAutoReleasePool, NSThread and NSThreadWillExitNotification John Zorko Oct 14, 21:02
mlRe: EXC_BAD_ACCESS with NSAutoReleasePool, NSThread and NSThreadWillExitNotification Ken Thomases Oct 15, 07:37