I have an application that makes heavy use of audio. I use AVAudioPlayers to simplify the task.
I've noticed crashes from time to time when using the players, usually when I call [player play]. The stack trace looks something like this:
EXC_BAD_ACCESS
#0 0x32d531e6 in memmove$VARIANT$CortexA8 ()
#1 0x351056dc in Cached_DataSource::ReadFromHeaderCache ()
#2 0x351052a4 in Cached_DataSource::ReadBytes ()
#3 0x350ba47c in AudioFileObject::ReadBytes ()
#4 0x35104562 in AudioFileObject::ReadPacketDataVBR ()
#5 0x35102c70 in AudioFileObject::ReadPacketData ()
#6 0x351020b2 in AudioFileReadPacketData ()
#7 0x36bad1b8 in AudioPlayerAQOutputCallbackCore ()
#8 0x36bad7a4 in prepareToPlayQueue ()
#9 0x36bad954 in playQueue ()
#10 0x36bac27e in -[AVAudioPlayer play] ()
I have breakpoints set up in every dealloc call, so I know that I'm not inadvertently dealloc-ing a player or delegate or similar.
The players are sometimes stopped before they play their full sound, so I always make a [player setCurrentTime:0] call before playing. I'm not sure if this could cause some negative side effects.
Clearly the audio queue is trying to access invalid memory, but from the looks of things that memory lives way down in the file cache code, so I can't see how anything I'm doing would affect it. All of the files I'm playing are included as resources in the main bundle.
I created a repro that reliably reproduces this in the 4.1 simulator and on my 5.0.1 device. It can take a little while and hundreds of calls to setCurrentTime, play, and stop before it crashes.
I need to get to the bottom of this. I can start porting all of my code to use audio queues or something else if required, but I want to make sure that there isn't something I'm missing or that I could do differently to make this more stable.
Related
See example below:
import 'dart:async';
Future<void> main(List<String> arguments) async {
try {
await someMethod();
} catch (e, st) {
print("error: ${e}");
print("st : ${st}");
}
}
Future<void> someMethod() async {
final completer = Completer.sync();
_scheduleCompletion(completer);
await completer.future;
}
Future<void> _scheduleCompletion(Completer completer) async {
Future.delayed(Duration(milliseconds: 1), () {
try {
[][0]; // simulating error
completer.complete();
} catch (e, st) {
completer.completeError("error occured", st);
}
});
}
I am receiving following output:
#0 List.[] (dart:core-patch/growable_array.dart:254:60)
#1 _scheduleCompletion.<anonymous closure> (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions.dart:26:9)
#2 new Future.delayed.<anonymous closure> (dart:async/future.dart:315:39)
#3 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#4 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#5 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#6 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
As you can see stacktrace does not allow you to see what steps led to error. What I expect is something like:
[anonymous closure, where error actually happens]
[someMethod]
[main]
while instead I see only last part.
I know there is package, called stack_trace, that allows you to "wrap your whole program in Chain.capture and you will be happy". Unfortunately my real-world case is not that simple and I can not simply "wrap my program" in "Chain.capture".
Another reason on not using "Chain.capture" thing is that in some cases it's onError handler gets even worse stacktrace vs. simple try/catch block .
Also I would like to understand why this is happening and what should I do to fix my program so part of valuable trace is not lost.
UPDATE (based on Irn's answer):
Let's re-write someMethod not to use completer, but rather call another async method:
Future<void> someMethod() async {
await someOtherMethod();
}
Future<void> someOtherMethod() async {
throw "another error";
}
In that case I will have following trace:
#0 someOtherMethod (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:24:3)
<asynchronous suspension>
#1 someMethod (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:19:3)
<asynchronous suspension>
#2 main (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:5:5)
<asynchronous suspension>
As you see it contains all invocations main -> someMethod -> someOtherMethod, that allows you to narrow down the issue. As I understand in that case we should also lose everything as we are scheduling calls on event loop. But we see everything works as expected. Why is that?
About stack_trace package: I would love to use it, but I made small experiment and looks like sometimes it provides worse trace then the one from try/catch. But that is topic for another question ;)
The short answer is that you can't trace a stack which doesn't exist.
Dart's asynchronous code is event loop based.
When you do await someFuture, your function really sets up a callback on that future, using Future.then, and then it returns. That unwinds the stack leading up to setting that callback, and eventually the stack all the way back to the event loop.
(The return only happens when you evaluate the await, the someFuture expression is invoked immediately, and if that manages to throw before doing something asynchronous, then you will get the stack trace containing the stack prior to the await returning).
At a later time, the event loop triggers and calls the callback, and the asynchronous computation continues from there.
The original stack no longer exists at that point, which is why you see a stack trace leading back to an event loop event (here, a timer, which the VM implements by sending port events at specific times). The stack you see is the only stack which exists at the point you ask for it.
You can use package:stack_trace to capture a stack trace at the point where you schedule a callback, and then the stack trace package will remember that stack trace until the callback is run, and attempt to combine it with the stack traces reported (or captured) during the callback.
Since you make lots of callbacks, but don't usually need to see that many stack traces, it has a significant performance impact to capture stacks at every callback.
I'm having trouble successfully using shareReplay from RxDart (0.24.1) in my application. I have a Stream and I would like to 'cache' the latest emitted value, so that any 'late' listeners get it immediately (the stream can be idle for longer periods). I'm failing to do this, so I started experimenting outside of my app and must admit I don't fully understand what is happening.
Here is some code:
import 'package:rxdart/rxdart.dart';
void main() async {
final s = Stream.fromIterable([1, 2, 3]).shareReplay(maxSize: 10);
print('First:');
await for (final i in s) {
print(i);
}
print('Second:');
await for (final i in s) {
print(i);
}
}
I would expect the code to print
First
1
2
3
Second
1
2
3
but it never gets to the second await for, it doesn't even print the string Second:; however, the program finishes successfully, so the first await for does finish (as the original Stream is finite). What is going on?
In reality, the Stream that I have is potentially endless with long periods of time between events), and I'm only interested in the last event, so here is some code simulating this:
import 'package:rxdart/rxdart.dart';
void main() async {
final s = Stream.periodic(Duration(seconds: 1), (e) => e).shareReplay(maxSize: 1);
// print(s.isBroadcast);
await Future.delayed(Duration(seconds: 3));
print('First');
await for (final i in s.take(1)) {
print(i);
}
print('Second');
await for (final i in s.take(1)) {
print(i);
}
}
To make sure the Stream can finish (and so await for can finish) I use take(1). I expected the output to be:
First
2
Second
2
(Maybe the twos would be threes?)
What I expected to happen is this:
The periodic stream starts emitting every second (it is broadcast/hot).
The application waits for 3 seconds due to Future.delayed.
The first late listener comes and gets the latest value, 2 (or maybe 3?) and finishes due to take(1).
The second late listener comes and gets the latest value as well because the stream didn't emit anything else in the meantime (even if it does, the value would be greater by 1, which is Ok) and finishes due to take(1).
The app finishes.
However, the output is:
First
0
Second
Unhandled exception:
type '_TakeStream<int>' is not a subtype of type 'Future<bool>'
#0 _StreamIterator._onData (dart:async/stream_impl.dart:1067:19)
#1 _RootZone.runUnaryGuarded (dart:async/zone.dart:1384:10)
#2 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:357:11)
#3 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:285:7)
#4 _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:127:11)
#5 _TakeStream._handleData (dart:async/stream_pipe.dart:318:12)
#6 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:157:13)
#7 _RootZone.runUnaryGuarded (dart:async/zone.dart:1384:10)
#8 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:357:11)
#9 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:285:7)
#10 _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:385:25)
#11 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:250:5)
#12 _StartWithStreamSink._safeAddFirstEvent (package:rxdart/src/transformers/start_with.dart:56:12)
#13 _StartWithStreamSink.onListen (package:rxdart/src/transformers/start_with.dart:37:11)
#14 forwardStream.<anonymous closure>.<anonymous closure> (package:rxdart/src/utils/forwarding_stream.dart:31:37)
#15 forwardStream.runCatching (package:rxdart/src/utils/forwarding_stream.dart:24:12)
#16 forwardStream.<anonymous closure> (package:rxdart/src/utils/forwarding_stream.dart:31:16)
#17 _runGuarded (dart:async/stream_controller.dart:847:24)
#18 _BroadcastStreamController._subscribe (dart:async/broadcast_stream_controller.dart:213:7)
#19 _ControllerStream._createSubscription (dart:async/stream_controller.dart:860:19)
#20 _StreamImpl.listen (dart:async/stream_impl.dart:493:9)
#21 DeferStream.listen (package:rxdart/src/streams/defer.dart:37:18)
#22 StreamView.listen (dart:async/stream.dart:1871:20)
#23 new _ForwardingStreamSubscription (dart:async/stream_pipe.dart:118:10)
#24 new _StateStreamSubscription (dart:async/stream_pipe.dart:341:9)
#25 _TakeStream._createSubscription (dart:async/stream_pipe.dart:310:16)
#26 _ForwardingStream.listen (dart:async/stream_pipe.dart:83:12)
#27 _StreamIterator._initializeOrDone (dart:async/stream_impl.dart:1041:30)
#28 _StreamIterator.moveNext (dart:async/stream_impl.dart:1028:12)
#29 main (package:shopping_list/main.dart)
<asynchronous suspension>
#30 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#31 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
The first await for with take(1) to actually make it finish works as I would expect it to, with the exception that it gets the the initial value, which means the stream didn't stat emitting anything before the listener came (is not 'hot'). The second fails with an exception. What am I missing?
EDIT: my understanding of shareReplay may have been wrong all along, I though it is hot because of https://pub.dev/documentation/rxdart/latest/rx/ReplaySubject-class.html says it is hot.
I changed the code a bit:
import 'package:rxdart/rxdart.dart';
void main() async {
final s = BehaviorSubject();
s.addStream(Stream.periodic(Duration(seconds: 1), (e) => e));
// print(s.isBroadcast);
await Future.delayed(Duration(seconds: 3));
print('First');
await for (final i in s.take(1)) {
print(i);
}
print('Second');
await for (final i in s.take(1)) {
print(i);
}
}
and now the output is:
First
2
Second
2
BUT the program never finishes...
Without take(1) the output never gets to the second listener, so it does have some effect. I don't get it.
EDIT 2: I think I might know why the first example with shareReplay doesn't work, here is an excerpt from the docs:
It will automatically begin emitting items when first listened to, and shut down when no listeners remain.
In my case, the first listener/await for subscribes, and the stream starts, and when the loop is done the last (and only) listener is finished, so the stream returned from shareReplay shuts down. Still doesn't explain why the line print('Second'); doesn't execute, though.
The reason your final program doesn't terminate is that your broadcast stream is still emitting events every second.
Nobody's listening, but it's a broadcast stream so it doesn't care. It has a timer ticking every second, and that timer keeps the program alive, even if nobody is ever going to listen again.
You can keep your program alive by doing Timer.periodic(Duration(seconds: 1), (_) {}); as well. As long as the timer is live, something might happen.
The crash is interesting, though. It suggests a problem with the internal logic of the StreamIterator class. I'll look into that.
Can't say anything guaranteed useful about shareReplay, I'm not familiar with RxDart.
My guess is that the first program gets into a problem because the replayShared stream doesn't terminate properly - that is, doesn't send a done event when it's done. That would explain the program terminating early. The first await for is waiting for the next event, so it doesn't end and go to the next loop, but there are no live timers or scheduled events of any kind left, so the isolate just shuts down.
A quick read of BeheaviorSubject suggests that it works like a stream controller, so you probably need to close it after the addStream has completed. Maybe:
s.addStream(...).whenComplete(s.close);
My iphone app get locked malloc. If I press pause button in Xcode it pauses in OSSpinLockLock$VARIANT$mp function.
#0 0x95dfbc2d in OSSpinLockLock$VARIANT$mp ()
#1 0x95dc2613 in szone_malloc_should_clear ()
#2 0x95dc366b in szone_malloc ()
#3 0x95df9962 in malloc_zone_malloc ()
#4 0x95dfa882 in malloc ()
#5 0x0219743a in operator new(unsigned long) ()
if I press continue and then pause again then it always shows same stack trace.
Look at your other threads. One of them is likely blocked inside of a malloc or free as well and the two are deadlocked against each other.
A somewhat common cause of this is allocating memory inside of a signal handler (which you should never do). You'll see this especially if you have some kind of "crash catcher" in your system, and inside of the handler you do complex operations.
I can't find an obvious way to detect deletion of a document that I'm monitoring (I cache a list of docs my app cares about).
On Device1:
When I delete a doc, I call
[fileCoordinator coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForDeleting
error:&err byAccessor:^(NSURL* writingURL) {
[fileManager removeItemAtURL:writingURL error:nil];
This works fine on Device1, everything stays in synch.
On Device2:
I was trying to rely on NSMetadataQuery notifications:
Initial file list is coming in fine on NSMetadataQueryDidFinishGatheringNotification
Document adds/changes are coming in fine via NSMetadataQueryDidUpdateNotification
When i delete a file on Device1, I get a strange result: an update comes in NSMetadataQueryDidUpdateNotification with all my docs (except the one deleted) listed
I'm not sure how I am supposed to detect that the missing file was deleted or that the update notification was for that purpose
Question 1: What should I be checking for?
I tried another route which was to register as a NSFilePresenter for the iCloud URL:
- (NSURL *)iCloudDocumentsURL
{
return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:#"Documents"];
}
I now get called via the NSFilePresenter protocol when files in that URL change, but it's punitively slow to run the logic to determine the missing doc since the callback is vague.
The only call I get back is - (void)presentedItemDidChange;
I was expecting to get a callback via - (void)accommodatePresentedSubitemDeletionAtURL:(NSURL *)url completionHandler:(void (^)(NSError *errorOrNil))completionHandler
But this method is never called.
Question 2: Any idea how to get that working?
Since you're keeping track of files previously reported from iCloud, the deleted file is absent from the current iCloud listing, and so you just compare the two lists to find which was deleted.
That's what I'm doing in my "file manager" view controller, because I keep a NSMutableDictionary of file entries, which includes among other things, keys for the positions of the file "icons" in my master view. When I get notified of an update, and that update results in either more or fewer files, I animate the icon changes based upon those file
changes.
tobinjim is correct in that the NSMetadataQuery returns the entire result set every time. I had expected that only changes to be sent to save bandwidth, but I did not RTFM correctly.
Once I figured that out, I ran into a bug in iOS libraries. This was the crash that was occurring when I deleted a document on one device and the iCloud query update came across to my other device:
2012-06-25 13:15:12.343 app[19779:707] *** -[NSMutableIndexSet indexGreaterThanOrEqualToIndex:]: message sent to deallocated instance 0xdaae2c0
(gdb) bt
#0 0x31937870 in ___forwarding___ ()
#1 0x31892650 in __forwarding_prep_0___ ()
#2 0x373cc676 in __NSIndexSetEnumerate ()
#3 0x373a1ee8 in -[NSIndexSet enumerateIndexesWithOptions:usingBlock:] ()
#4 0x371c1f08 in -[LBQuery _processUpdates] ()
#5 0x373571a6 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] ()
#6 0x3737ffa4 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] ()
#7 0x371c2274 in -[LBQuery processUpdates] ()
#8 0x373a88d6 in -[NSMetadataQuery _update] ()
I believe I have found the cause of this issue. I was reviewing the new IOS 6 sample code from the presentation by Luke the Hiesterman. I noticed that there were no calls to NSMetadataQuery disable/enableUpdates like there were in the previous iCloud sample apps. I removed all calls to these. I also changed my method which handled NSMetadataQueryDidUpdateNotification calls to run asynchronously but in a queued manner.
There seems to be a race condition occurring between threads for NSMetadataQuery. In some calls the results of the query are fine but at other times, they have been released and the results show up as NSZombie's (thanks to gdb which is way way better than the current lldb). So the enabling and disabling of the query across threads causes the LBQuery to crash by calling objects that have been released.
The removal of all the enabling and disabling for NSMetadataQuery seems to have sped up my app as well, and iCloud seems much more stable.
Since iOS 8 NSMetadataQuery extended NSMetadataQueryDidUpdateNotification userInfo with NSMetadataQueryUpdateRemovedItemsKey which you can use to detect which files were removed.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMetadataQuery_Class/#//apple_ref/doc/constant_group/Keys_for_Use_with_a_Notification_Info_Dictionary
We keep getting a random, weird crash with NSDateFormatter. The relevant stack trace is:
Program received signal: “EXC_BAD_ACCESS”.
#0 0x00000005 in ?? ()
#1 0x0213e3c3 in udat_parse ()
#2 0x01d4e1ca in CFDateFormatterGetAbsoluteTimeFromString ()
#3 0x01d4e225 in CFDateFormatterCreateDateFromString ()
#4 0x003e2608 in getObjectValue ()
#5 0x003e2921 in -[NSDateFormatter getObjectValue:forString:errorDescription:] ()
#6 0x003e21cd in -[NSDateFormatter dateFromString:] ()
The date formatter is still in memory (i.e. not released or corrupted). The only thing I can think of is the strings upon crash do not conform to the format, but i doubt that will make the formatter completely crash. (it is non trivial to check the format beforehand).
Any thoughts?
Thanks to the previous answerers.
This was not a memory problem. It turned out to be a synchronization issue. NSDateFormatters are not thread safe; there was a background thread attempting to use the same formatter at the same time (hence the randomness).
Hope this helps someone in the future!
Another solution would be to serialize the execution of the code that uses NSDateFormatters, or any other non-thread-safe objects. Using Grand Central Dispatch you can push the code on the main_queue:
dispatch_async(dispatch_get_main_queue(), ^(void){
[some_object some_message];
});
or use a private queue to achieve the same effect:
dispatch_queue_t dispatch_queue = dispatch_queue_create("com.MyApp.serializer",NULL);
dispatch_async(dispatch_queue, ^(void){
[some_object some_message];
});
EXCBADACCESS will occur when you use any deallocated object...
Try to use NSZombie.. It is a easy way to find where the EXCBADACCESS occurs... It will specify which Method where and which object gets deallocated
See this Link http://www.markj.net/iphone-memory-debug-nszombie/
My bet is that the string you pass in to the date formatter is over-released.
I was experiencing weird crashes with _sigtramp which caused to application to appear locked up but still on the screen - completely obstructing the real root cause.
It turned out indeed that we introduced multi-thread data parsing which collided with the main GUI thread trying to parse dates using NSDateFormatter.
Putting some synchronization around the NSDateFormatter formatDate calls resolved the issues.