Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible race condition in SFBAudioPlayer when creating new _playerNode #548

Open
sbooth opened this issue Jan 17, 2025 · 3 comments
Open
Assignees
Labels

Comments

@sbooth
Copy link
Owner

sbooth commented Jan 17, 2025

Since _playerNode may be swapped out in -configureProcessingGraphForFormat:forceUpdate:, there is a small chance that it could be nil when used in one of the other SFBAudioPlayer methods (e.g. -playbackPosition) if that method is called between the time _playerNode is set to nil and reassigned.

return _playerNode.playbackPosition;

The current implementation takes advantage of the fact that in Objective-C messaging nil isn't an error but the potential for incorrect behavior exists.

@sbooth sbooth added the bug label Jan 17, 2025
@sbooth sbooth self-assigned this Jan 17, 2025
sbooth added a commit that referenced this issue Jan 21, 2025
~This is a potential fix for #548 and seems preferable to the proposed
fix in #549~

Not a fix but an improvement for now.
@jlama
Copy link
Contributor

jlama commented Jan 22, 2025

I believe a similar bug exists in AudioPlayerNode in all methods accessing the current decoder. For example:

SFBPlaybackTime SFB::AudioPlayerNode::PlaybackTime() const noexcept
{
const auto decoderState = GetActiveDecoderStateWithSmallestSequenceNumber();
if(!decoderState)
return SFBInvalidPlaybackTime;
SFBPlaybackTime playbackTime = SFBInvalidPlaybackTime;
const auto framePosition = decoderState->FramePosition();
const auto frameLength = decoderState->FrameLength();
if(const auto sampleRate = decoderState->mSampleRate; sampleRate > 0) {
if(framePosition != SFBUnknownFramePosition)
playbackTime.currentTime = framePosition / sampleRate;
if(frameLength != SFBUnknownFrameLength)
playbackTime.totalTime = frameLength / sampleRate;
}
return playbackTime;
}

After the decoderState pointer is loaded, it could become invalid (deleted) at any time. Access and deletion of a DecoderState should probably be protected by a lock.

@sbooth
Copy link
Owner Author

sbooth commented Jan 22, 2025

I believe you're right. A lock isn't an option because of usage in the IOProc but additional synchronization is probably needed.

@jlama
Copy link
Contributor

jlama commented Jan 22, 2025

Ah yes indeed. I would argue that the render block should not fiddle with the decoder states and only report how many frames were rendered. Let the current decoder thread handle the frame accounting.
The drawback is that reporting start/stop render timings is more difficult, but I don't think these timings are very useful nor accurate (they don't account for downstream nodes latencies).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants