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
{
public:
shared_mutex mutex;
vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{};
@ -99,19 +100,39 @@ struct music_state
switch (status)
{
case music_handler_base::player_status::end_of_media:
// Let's just play the next song for now
if (current_selection_context.content_type == CELL_SEARCH_CONTENTTYPE_MUSICLIST && handler->get_state() == CELL_MUSIC_PB_STATUS_PLAY)
{
if (const error_code error = set_playback_command(CELL_MUSIC_PB_CMD_NEXT, true))
{
cellMusic.error("Failed to play next track. error=0x%x", +error);
}
}
// Let's just play the next song for now if we are in list mode.
// 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.
m_wake_up_thread = 1;
m_wake_up_thread.notify_one();
break;
default:
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)
@ -120,6 +141,19 @@ struct music_state
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)
{
ar(func);
@ -135,7 +169,7 @@ struct music_state
}
// 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)
{
@ -150,11 +184,27 @@ struct music_state
case CELL_MUSIC_PB_CMD_FASTREVERSE:
case CELL_MUSIC_PB_CMD_NEXT:
case CELL_MUSIC_PB_CMD_PREV:
case CELL_MUSIC_PB_CMD_NEXT_TRACK:
{
std::string path;
bool automatic = false;
bool no_more_tracks = false;
{
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 u32 current_track = current_selection_context.current_track;
u32 next_track = current_track;
@ -208,6 +258,10 @@ struct music_state
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()

View File

@ -90,6 +90,8 @@ enum
CELL_MUSIC_PB_CMD_PREV = 4,
CELL_MUSIC_PB_CMD_FASTFORWARD = 5,
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

View File

@ -257,10 +257,8 @@ namespace logs
{
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)