yuzu-emu
/
yuzu-mainline
Archived
1
0
Fork 0

input/sdl: lock map mutex after SDL call

Any SDL invocation can call the even callback on the same thread, which can call GetSDLJoystickBySDLID and eventually cause double lock on joystick_map_mutex. To avoid this, lock guard should be placed as closer as possible to the object accessing code, so that any SDL invocation is with the mutex unlocked
This commit is contained in:
Weiyi Wang 2018-10-02 11:06:42 -04:00 committed by fearlessTobi
parent 09ac66388c
commit 8b98f60e3c
1 changed files with 17 additions and 11 deletions

View File

@ -159,9 +159,9 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
* it to a SDLJoystick with the same guid and that port * it to a SDLJoystick with the same guid and that port
*/ */
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick); const std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
auto map_it = joystick_map.find(guid); auto map_it = joystick_map.find(guid);
if (map_it != joystick_map.end()) { if (map_it != joystick_map.end()) {
auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(), auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
@ -193,13 +193,13 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
} }
void SDLState::InitJoystick(int joystick_index) { void SDLState::InitJoystick(int joystick_index) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
if (!sdl_joystick) { if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index); LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return; return;
} }
std::string guid = GetGUID(sdl_joystick); std::string guid = GetGUID(sdl_joystick);
std::lock_guard<std::mutex> lock(joystick_map_mutex);
if (joystick_map.find(guid) == joystick_map.end()) { if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
joystick_map[guid].emplace_back(std::move(joystick)); joystick_map[guid].emplace_back(std::move(joystick));
@ -218,16 +218,22 @@ void SDLState::InitJoystick(int joystick_index) {
} }
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) { void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::string guid = GetGUID(sdl_joystick); std::string guid = GetGUID(sdl_joystick);
// This call to guid is safe since the joystick is guaranteed to be in the map std::shared_ptr<SDLJoystick> joystick;
auto& joystick_guid_list = joystick_map[guid]; {
const auto joystick_it = std::lock_guard<std::mutex> lock(joystick_map_mutex);
std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), // This call to guid is safe since the joystick is guaranteed to be in the map
[&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) { auto& joystick_guid_list = joystick_map[guid];
return joystick->GetSDLJoystick() == sdl_joystick; const auto joystick_it =
}); std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
(*joystick_it)->SetSDLJoystick(nullptr, [](SDL_Joystick*) {}); [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
return joystick->GetSDLJoystick() == sdl_joystick;
});
joystick = *joystick_it;
}
// Destruct SDL_Joystick outside the lock guard because SDL can internally call event calback
// which locks the mutex again
joystick->SetSDLJoystick(nullptr, [](SDL_Joystick*) {});
} }
void SDLState::HandleGameControllerEvent(const SDL_Event& event) { void SDLState::HandleGameControllerEvent(const SDL_Event& event) {