TheAmazingAudioEngine/TheAmazingAudioEngine — pull request #112 by ndonald2 (2014-09-17 20:23:26+00:00)
Title: Fix race condition where schedule is already cancelled

I have been encountering a bad access crash when trying to cancel scheduled blocks in `AEBlockScheduler` and I think I found a way to fix it. Granted, not being 100% sure what was causing it, I'd really appreciate it if you could take a look at this as well.

There were two main issues here:
1. The array returned by `schedules` was cast down to `NSArray` from `NSMutableArray` and thus could be mutated during enumeration when, for example, iterating through the array to cancel all schedules.
2. In rare cases, a schedule being executed on the audio thread will simultaneously be cancelled on the main thread. For some reason this results in the corresponding `schedule_t` having its members released and nulled *after* the cancellation method has already found the matching instance (and copied by value), so the copy in the cancellation method has dangling references to the blocks, etc. At the end of the cancellation method, these are overreleased.

My guess is that the synchronous message exchange in `cancelScheduleWithIdentifier` is allowing the schedule's completion method to fire while waiting, so the schedule_t gets cleared there while the value copy is left in scope while waiting, but I haven't proven that.

The fix here is to check the schedule identifiers array to see if it contains the identifier of the schedule being cancelled *before* releasing its members. If so, the schedule is cancelled and its members successfully released. If not, the value copy goes out of scope and all is well.

Please let me know if I have misinterpreted what is going on here. The code in which I encountered this bug
is proprietary but I can probably produce an isolated example if necessary.