cellMusic: fix occasional deadlock during auto-play in playlist mode

This commit is contained in:
Megamouse 2026-06-01 00:01:55 +02:00
parent ecf77ecef0
commit 17f1fc31bc
3 changed files with 67 additions and 13 deletions

View File

@ -66,6 +66,7 @@ void fmt_class_string<CellMusic2Error>::format(std::string& out, u64 arg)
struct music_state struct music_state
{ {
public:
shared_mutex mutex; shared_mutex mutex;
vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{}; vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{};
@ -99,19 +100,39 @@ struct music_state
switch (status) switch (status)
{ {
case music_handler_base::player_status::end_of_media: case music_handler_base::player_status::end_of_media:
// Let's just play the next song for now // Let's just play the next song for now if we are in list mode.
if (current_selection_context.content_type == CELL_SEARCH_CONTENTTYPE_MUSICLIST && handler->get_state() == CELL_MUSIC_PB_STATUS_PLAY) // Due to potential main thread recursion this may cause a deadlock with the internal mutex of the handler.
{ // Let's just call it from another thread instead.
if (const error_code error = set_playback_command(CELL_MUSIC_PB_CMD_NEXT, true)) m_wake_up_thread = 1;
{ m_wake_up_thread.notify_one();
cellMusic.error("Failed to play next track. error=0x%x", +error);
}
}
break; break;
default: default:
return; return;
} }
}); });
m_thread = std::make_unique<named_thread<std::function<void()>>>("cellMusic State", [this]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
while (thread_ctrl::state() != thread_state::aborting && !m_wake_up_thread)
{
thread_ctrl::wait_on(m_wake_up_thread, 0);
}
m_wake_up_thread = 0;
if (thread_ctrl::state() == thread_state::aborting)
{
return;
}
// Play the next song
if (const error_code error = set_playback_command(CELL_MUSIC_PB_CMD_NEXT_TRACK))
{
cellMusic.error("Failed to play next track. error=0x%x", +error);
}
}
});
} }
music_state(utils::serial& ar) music_state(utils::serial& ar)
@ -120,6 +141,19 @@ struct music_state
save(ar); save(ar);
} }
~music_state()
{
if (m_thread)
{
auto& thread = *m_thread;
thread = thread_state::aborting;
m_wake_up_thread = 1;
m_wake_up_thread.notify_one();
thread();
m_thread.reset();
}
}
void save(utils::serial& ar) void save(utils::serial& ar)
{ {
ar(func); ar(func);
@ -135,7 +169,7 @@ struct music_state
} }
// NOTE: This function only uses CELL_MUSIC enums. CELL_MUSIC2 enums are identical. // NOTE: This function only uses CELL_MUSIC enums. CELL_MUSIC2 enums are identical.
error_code set_playback_command(s32 command, bool automatic = false) error_code set_playback_command(u32 command)
{ {
switch (command) switch (command)
{ {
@ -150,11 +184,27 @@ struct music_state
case CELL_MUSIC_PB_CMD_FASTREVERSE: case CELL_MUSIC_PB_CMD_FASTREVERSE:
case CELL_MUSIC_PB_CMD_NEXT: case CELL_MUSIC_PB_CMD_NEXT:
case CELL_MUSIC_PB_CMD_PREV: case CELL_MUSIC_PB_CMD_PREV:
case CELL_MUSIC_PB_CMD_NEXT_TRACK:
{ {
std::string path; std::string path;
bool automatic = false;
bool no_more_tracks = false; bool no_more_tracks = false;
{ {
std::lock_guard lock(mtx); std::lock_guard lock(mtx);
// Handle auto-play of the next track in the current playlist.
if (command == CELL_MUSIC_PB_CMD_NEXT_TRACK)
{
// We only auto-play the next song if we are in list mode and the music is playing.
if (current_selection_context.content_type != CELL_SEARCH_CONTENTTYPE_MUSICLIST || handler->get_state() != CELL_MUSIC_PB_STATUS_PLAY)
{
return CELL_OK;
}
command = CELL_MUSIC_PB_CMD_NEXT;
automatic = true;
}
const std::vector<std::string>& playlist = current_selection_context.playlist; const std::vector<std::string>& playlist = current_selection_context.playlist;
const u32 current_track = current_selection_context.current_track; const u32 current_track = current_selection_context.current_track;
u32 next_track = current_track; u32 next_track = current_track;
@ -208,6 +258,10 @@ struct music_state
return CELL_OK; return CELL_OK;
} }
private:
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
atomic_t<u32> m_wake_up_thread{0};
}; };
error_code cell_music_select_contents() error_code cell_music_select_contents()

View File

@ -90,6 +90,8 @@ enum
CELL_MUSIC_PB_CMD_PREV = 4, CELL_MUSIC_PB_CMD_PREV = 4,
CELL_MUSIC_PB_CMD_FASTFORWARD = 5, CELL_MUSIC_PB_CMD_FASTFORWARD = 5,
CELL_MUSIC_PB_CMD_FASTREVERSE = 6, CELL_MUSIC_PB_CMD_FASTREVERSE = 6,
CELL_MUSIC_PB_CMD_NEXT_TRACK = 7, // RPCS3 helper for auto-play of the next track in the current playlist
}; };
enum enum

View File

@ -257,10 +257,8 @@ namespace logs
{ {
return found.first->second->enabled.observe(); return found.first->second->enabled.observe();
} }
else
{ return level::always;
return level::always;
}
} }
void set_channel_levels(const std::map<std::string, logs::level, std::less<>>& map) void set_channel_levels(const std::map<std::string, logs::level, std::less<>>& map)