mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-29 16:20:11 -04:00
openal: fix potential iterator invalidation in OpenALAudioManager::update
OpenALAudioManager::update iterates through all currently playing sounds via a std::set / phash_set object, _sounds_playing. If a stream queue corruption was detected during OpenALAudioSound::pull_used_buffers, the logic added in a895890 would call cleanup() on the sound if we could not successfully locate the target buffer and log an error. However, the act of calling OpenALAudioSound::cleanup would lead to calling stop() (since the sound was actively playing). In OpenALAudioSound::stop(), we would then proceed to call _manager->stopping_sound which would erase the current sound from _sounds_playing (while we still held an iterator to it). Per STL standard and real-world observation, std::set::erase will invalidate the current iterator held in update (https://en.cppreference.com/w/cpp/container/set/erase). This leads to a segmentation fault when we attempt the next iteration on the loop. To resolve this, let's ensure we don't hold onto invalid iterators during the updating of playing sounds. Fixes #1452
This commit is contained in:
parent
1c37522026
commit
81f7a21845
@ -959,32 +959,33 @@ stop_all_sounds() {
|
||||
*/
|
||||
void OpenALAudioManager::
|
||||
update() {
|
||||
ReMutexHolder holder(_lock);
|
||||
ReMutexHolder const holder(_lock);
|
||||
|
||||
// See if any of our playing sounds have ended we must first collect a
|
||||
// seperate list of finished sounds and then iterated over those again
|
||||
// calling their finished method. We can't call finished() within a loop
|
||||
// iterating over _sounds_playing since finished() modifies _sounds_playing
|
||||
SoundsPlaying sounds_finished;
|
||||
|
||||
double rtc = TrueClock::get_global_ptr()->get_short_time();
|
||||
SoundsPlaying::iterator it = _sounds_playing.begin();
|
||||
while (it != _sounds_playing.end()) {
|
||||
PT(OpenALAudioSound) sound = *(it++);
|
||||
// See if any of our playing sounds have ended.
|
||||
double const rtc = TrueClock::get_global_ptr()->get_short_time();
|
||||
SoundsPlaying::iterator i = _sounds_playing.begin();
|
||||
while (i != _sounds_playing.end()) {
|
||||
// The post-increment syntax is *very* important here.
|
||||
// As both OpenALAudioSound::pull_used_buffers and OpenALAudioSound::finished can modify the list of sounds playing
|
||||
// by erasing 'sound' from the list, thus invaliding the iterator.
|
||||
PT(OpenALAudioSound) sound = *(i++);
|
||||
sound->pull_used_buffers();
|
||||
|
||||
// If pull_used_buffers() encountered an error, the sound was cleaned up.
|
||||
// No need to do anymore work.
|
||||
if (!sound->is_valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sound->push_fresh_buffers();
|
||||
sound->restart_stalled_audio();
|
||||
sound->cache_time(rtc);
|
||||
if (sound->_source == 0 ||
|
||||
(sound->_stream_queued.empty() &&
|
||||
(sound->_loops_completed >= sound->_playing_loops))) {
|
||||
sounds_finished.insert(std::move(sound));
|
||||
sound->finished();
|
||||
}
|
||||
}
|
||||
|
||||
for (OpenALAudioSound *sound : sounds_finished) {
|
||||
sound->finished();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -536,7 +536,7 @@ pull_used_buffers() {
|
||||
}
|
||||
}
|
||||
if (!found_culprit) {
|
||||
audio_error("corruption in stream queue");
|
||||
audio_error(get_name() << ": corruption in stream queue");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user