mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 08:44:19 -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,33 +959,34 @@ stop_all_sounds() {
|
|||||||
*/
|
*/
|
||||||
void OpenALAudioManager::
|
void OpenALAudioManager::
|
||||||
update() {
|
update() {
|
||||||
ReMutexHolder holder(_lock);
|
ReMutexHolder const holder(_lock);
|
||||||
|
|
||||||
// See if any of our playing sounds have ended we must first collect a
|
// See if any of our playing sounds have ended.
|
||||||
// seperate list of finished sounds and then iterated over those again
|
double const rtc = TrueClock::get_global_ptr()->get_short_time();
|
||||||
// calling their finished method. We can't call finished() within a loop
|
SoundsPlaying::iterator i = _sounds_playing.begin();
|
||||||
// iterating over _sounds_playing since finished() modifies _sounds_playing
|
while (i != _sounds_playing.end()) {
|
||||||
SoundsPlaying sounds_finished;
|
// The post-increment syntax is *very* important here.
|
||||||
|
// As both OpenALAudioSound::pull_used_buffers and OpenALAudioSound::finished can modify the list of sounds playing
|
||||||
double rtc = TrueClock::get_global_ptr()->get_short_time();
|
// by erasing 'sound' from the list, thus invaliding the iterator.
|
||||||
SoundsPlaying::iterator it = _sounds_playing.begin();
|
PT(OpenALAudioSound) sound = *(i++);
|
||||||
while (it != _sounds_playing.end()) {
|
|
||||||
PT(OpenALAudioSound) sound = *(it++);
|
|
||||||
sound->pull_used_buffers();
|
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->push_fresh_buffers();
|
||||||
sound->restart_stalled_audio();
|
sound->restart_stalled_audio();
|
||||||
sound->cache_time(rtc);
|
sound->cache_time(rtc);
|
||||||
if (sound->_source == 0 ||
|
if (sound->_source == 0 ||
|
||||||
(sound->_stream_queued.empty() &&
|
(sound->_stream_queued.empty() &&
|
||||||
(sound->_loops_completed >= sound->_playing_loops))) {
|
(sound->_loops_completed >= sound->_playing_loops))) {
|
||||||
sounds_finished.insert(std::move(sound));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (OpenALAudioSound *sound : sounds_finished) {
|
|
||||||
sound->finished();
|
sound->finished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -536,7 +536,7 @@ pull_used_buffers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found_culprit) {
|
if (!found_culprit) {
|
||||||
audio_error("corruption in stream queue");
|
audio_error(get_name() << ": corruption in stream queue");
|
||||||
cleanup();
|
cleanup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user