Re: ... not sure if this is a KVO or a Core Audio problem with my code
Re: ... not sure if this is a KVO or a Core Audio problem with my code
- Subject: Re: ... not sure if this is a KVO or a Core Audio problem with my code
- From: Ken Thomases <email@hidden>
- Date: Fri, 28 Nov 2008 13:59:14 -0600
On Nov 28, 2008, at 8:48 AM, John Zorko wrote:
I'm experiencing a hang in my app when I do things too quickly.
When I pause the app and examine the callstack, I see this:
#0 0x31467b18 in semaphore_timedwait_signal_trap ()
#1 0x3145e984 in semaphore_timedwait_signal ()
#2 0x3145b104 in _pthread_cond_wait ()
#3 0x3145b260 in pthread_cond_timedwait_relative_np ()
#4 0x348cbd58 in CAGuard::WaitFor ()
#5 0x34909954 in ClientAudioQueue::ServicePendingCallbacks ()
#6 0x34909ab8 in AudioQueueStop ()
#7 0x00014334 in -[AudioStreamer stop] (self=0x895800,
_cmd=0x3018cc44) at /Users/jmzorko/work/root/Magnatune/Classes/
AudioStreamer.m:529
#8 0x00003566 in -[MagnatuneAppDelegate stopStream] (self=0x11cc40,
_cmd=0x18694) at /Users/jmzorko/work/root/Magnatune/Classes/
MagnatuneAppDelegate.m:237
#9 0x0000f8d2 in -[SongController
tableView:didSelectRowAtIndexPath:] (self=0x12e4c0, _cmd=0x300f1a90,
tableView=0x186080, indexPath=0x1491a0) at /Users/jmzorko/work/root/
Magnatune/Classes/SongController.m:212
This is happening inside my observeValueForKeyPath method ...
basically the background thread that does the Core Audio stuff has
been told to stop via AudioQueueStop(), and the IsRunning callback
set the value of a boolean called isPlaying to false. I've
registered as an observer to this boolean, and usually things work
well. However, when I select a new song to play too soon after a
previous song, observeValueForKeyPath seems to hang. Here is
observeValueForKeyPath:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"observeValueForKeyPath -- current thread ID: %x, keypath:
%s", [NSThread currentThread], [keyPath UTF8String]);
if ([keyPath isEqualToString:@"isPlaying"])
{
NSLog(@"calling handleIsPlaying");
[self performSelectorOnMainThread:@selector(handleIsPlaying:)
withObject:keyPath waitUntilDone:YES];
The "waitUntilDone:YES" is part of the problem.
[pool release];
return;
}
.
.
.
[pool release];
[super observeValueForKeyPath:keyPath ofObject:object change:change
context:context];
}
... and here is handleIsPlaying:
[...]
... I can't immediately see why handleIsPlaying() or
observeValueForKeyPath() would hang, but according to the callstack,
it's waiting for something. The last thing I see in my console log
is:
2008-11-28 06:29:44.459 Magnatune[1357:20b] calling AudioQueueStop()
from AudioStreamer stop method
2008-11-28 06:29:44.990 Magnatune[1357:ba0f] ---------------- audio
queue callback called
2008-11-28 06:29:45.001 Magnatune[1357:ba0f] observeValueForKeyPath
-- current thread ID: 1a3860, keypath: isPlaying
2008-11-28 06:29:45.007 Magnatune[1357:ba0f] calling handleIsPlaying
If anyone could illuminate me as to what the problem is (what is
CAGuard supposed to guard against, and why am I running into this
issue?), i'd be very appreciative.
What you're seeing is a classic deadlock. If, in the debugger, you
would look at the other threads' stack traces, you would see another
thread which is inside some Core Audio stuff but has done something
which needs to wait on your first thread (the one whose stack trace
you showed above).
CAGuard is guarding internal state of Core Audio. Exactly what isn't
for you to know. The issue is that any callback that Core Audio makes
to your code is likely to happen while such a guard is held (locked).
So, anything you do in such a callback can't block waiting on another
thread which might in turn be waiting for that same guard. Actually,
since Core Audio calls your callbacks on real time threads, you really
shouldn't do anything which might block if you can at all avoid it.
I would recommend that you reconsider your design. If you can avoid
it, don't update your isPlaying property from a Core Audio callback.
If you need to track that, you can set it in the thread which starts
the audio queue. Alternatively, you can query AudioQueueGetProperty
with kAudioQueueProperty_IsRunning.
If you do want to deal with properties, perhaps reconsider where (and
how) you're shunting the update to the main thread. Instead of
shunting the call of handleIsPlaying: to the main thread, try shunting
the actual setting of the isPlaying property. Remember, you might not
be the only one observing that property. And, of course, don't wait
until done.
Lastly, your implementation of -observeValueForKeyPath:... isn't done
correctly. It isn't sufficient to test the keyPath. You must provide
a unique context when you register as an observer and check that.
Search the archives for the reasons and the correct form, or type
"observe" and then ask Xcode to do code completion so it inserts a
template showing the correct form.
Cheers,
Ken
_______________________________________________
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