ソースを参照

Fix crash due to reentrancy in AudioStreamPlayer* finished signal.

This crash occurred when an audio stream finished playing in NOTIFICATION_INTERNAL_PROCESS,
during which it would iterate through a loop of playbacks,
leading to a "finished" signal, which removed the audio player from the tree
which led to a NOTIFICATION_EXIT_TREE,
which would mutate the array of playbacks while within the above loop.

This moves the signal callback outside of the loop which avoids the crash.
Note: previously, the signal was called multiple times if the same player finishes multiple times in one frame. Now it is at most once per frame.

Affects AudioStreamPlayer, AudioStreamPlayer2D and AudioStreamPlayer3D
Lyuma 3 年 前
コミット
c088a2dd89

+ 3 - 1
scene/2d/audio_stream_player_2d.cpp

@@ -78,7 +78,6 @@ void AudioStreamPlayer2D::_notification(int p_what) {
 			Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
 			for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
 				if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-					emit_signal(SNAME("finished"));
 					playbacks_to_remove.push_back(playback);
 				}
 			}
@@ -91,6 +90,9 @@ void AudioStreamPlayer2D::_notification(int p_what) {
 				active.clear();
 				set_physics_process_internal(false);
 			}
+			if (!playbacks_to_remove.is_empty()) {
+				emit_signal(SNAME("finished"));
+			}
 		}
 
 		while (stream_playbacks.size() > max_polyphony) {

+ 3 - 1
scene/3d/audio_stream_player_3d.cpp

@@ -292,7 +292,6 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 			Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
 			for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
 				if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-					emit_signal(SNAME("finished"));
 					playbacks_to_remove.push_back(playback);
 				}
 			}
@@ -305,6 +304,9 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 				active.clear();
 				set_physics_process_internal(false);
 			}
+			if (!playbacks_to_remove.is_empty()) {
+				emit_signal(SNAME("finished"));
+			}
 		}
 
 		while (stream_playbacks.size() > max_polyphony) {

+ 3 - 1
scene/audio/audio_stream_player.cpp

@@ -45,7 +45,6 @@ void AudioStreamPlayer::_notification(int p_what) {
 		Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
 		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
 			if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-				emit_signal(SNAME("finished"));
 				playbacks_to_remove.push_back(playback);
 			}
 		}
@@ -58,6 +57,9 @@ void AudioStreamPlayer::_notification(int p_what) {
 			active.clear();
 			set_process_internal(false);
 		}
+		if (!playbacks_to_remove.is_empty()) {
+			emit_signal(SNAME("finished"));
+		}
 	}
 
 	if (p_what == NOTIFICATION_EXIT_TREE) {