[KBM] Refactor KBM's thread safety logic to avoid mutex re-entrancy bugs and improve performance (#6803)

* Unlock mutex before ResetModifierForLowerLevelKeyHandlers method to avoid crash if two instances of KBM are running

* Added alias for Shortcut DWORD variant to clean up code

* Removed mutex usage in single key remap method and added GetSingleKeyRemap

* Added more alias

* Moved to boolean disable remapping

* Added missing ! in condition

* Remove lock statement from bad auto-merge
This commit is contained in:
Arjun Balgovind
2020-10-08 11:28:24 -07:00
committed by GitHub
parent 9928579364
commit e1d22c74b0
18 changed files with 190 additions and 109 deletions

View File

@@ -8,7 +8,7 @@
// Constructor
KeyboardManagerState::KeyboardManagerState() :
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI1(nullptr), currentShortcutUI2(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL)
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI1(nullptr), currentShortcutUI2(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL), remappingsEnabled(true)
{
configFile_mutex = CreateMutex(
NULL, // default security descriptor
@@ -104,7 +104,6 @@ void KeyboardManagerState::ResetUIState()
// Function to clear the OS Level shortcut remapping table
void KeyboardManagerState::ClearOSLevelShortcuts()
{
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
osLevelShortcutReMap.clear();
osLevelShortcutReMapSortedKeys.clear();
}
@@ -112,23 +111,19 @@ void KeyboardManagerState::ClearOSLevelShortcuts()
// Function to clear the Keys remapping table.
void KeyboardManagerState::ClearSingleKeyRemaps()
{
std::lock_guard<std::mutex> lock(singleKeyReMap_mutex);
singleKeyReMap.clear();
}
// Function to clear the App specific shortcut remapping table
void KeyboardManagerState::ClearAppSpecificShortcuts()
{
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
appSpecificShortcutReMap.clear();
appSpecificShortcutReMapSortedKeys.clear();
}
// Function to add a new OS level shortcut remapping
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC)
{
std::lock_guard<std::mutex> lock(osLevelShortcutReMap_mutex);
// Check if the shortcut is already remapped
auto it = osLevelShortcutReMap.find(originalSC);
if (it != osLevelShortcutReMap.end())
@@ -144,10 +139,8 @@ bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const
}
// Function to add a new single key to key/shortcut remapping
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const std::variant<DWORD, Shortcut>& newRemapKey)
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey)
{
std::lock_guard<std::mutex> lock(singleKeyReMap_mutex);
// Check if the key is already remapped
auto it = singleKeyReMap.find(originalKey);
if (it != singleKeyReMap.end())
@@ -160,10 +153,8 @@ bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const std
}
// Function to add a new App specific shortcut remapping
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const std::variant<DWORD, Shortcut>& newSC)
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC)
{
std::lock_guard<std::mutex> lock(appSpecificShortcutReMap_mutex);
// Convert app name to lower case
std::wstring process_name;
process_name.resize(app.length());
@@ -191,6 +182,54 @@ bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const
return true;
}
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
std::optional<SingleKeyRemapTable::iterator> KeyboardManagerState::GetSingleKeyRemap(const DWORD& originalKey)
{
auto it = singleKeyReMap.find(originalKey);
if (it != singleKeyReMap.end())
{
return it;
}
return std::nullopt;
}
bool KeyboardManagerState::CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName)
{
// Assumes appName exists in the app-specific remap table
ShortcutRemapTable& currentRemapTable = appName ? appSpecificShortcutReMap[*appName] : osLevelShortcutReMap;
for (auto& it : currentRemapTable)
{
if (it.second.isShortcutInvoked)
{
return true;
}
}
return false;
}
std::vector<Shortcut>& KeyboardManagerState::GetSortedShortcutRemapVector(const std::optional<std::wstring>& appName)
{
// Assumes appName exists in the app-specific remap table
return appName ? appSpecificShortcutReMapSortedKeys[*appName] : osLevelShortcutReMapSortedKeys;
}
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped
ShortcutRemapTable& KeyboardManagerState::GetShortcutRemapTable(const std::optional<std::wstring>& appName)
{
if (appName)
{
auto itTable = appSpecificShortcutReMap.find(*appName);
if (itTable != appSpecificShortcutReMap.end())
{
return itTable->second;
}
}
return osLevelShortcutReMap;
}
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
{
@@ -468,7 +507,6 @@ bool KeyboardManagerState::SaveConfigToFile()
json::JsonArray inProcessRemapKeysArray;
json::JsonArray appSpecificRemapShortcutsArray;
json::JsonArray globalRemapShortcutsArray;
std::unique_lock<std::mutex> lockSingleKeyReMap(singleKeyReMap_mutex);
for (const auto& it : singleKeyReMap)
{
json::JsonObject keys;
@@ -488,9 +526,7 @@ bool KeyboardManagerState::SaveConfigToFile()
inProcessRemapKeysArray.Append(keys);
}
lockSingleKeyReMap.unlock();
std::unique_lock<std::mutex> lockOsLevelShortcutReMap(osLevelShortcutReMap_mutex);
for (const auto& it : osLevelShortcutReMap)
{
json::JsonObject keys;
@@ -510,9 +546,7 @@ bool KeyboardManagerState::SaveConfigToFile()
globalRemapShortcutsArray.Append(keys);
}
lockOsLevelShortcutReMap.unlock();
std::unique_lock<std::mutex> lockAppSpecificShortcutReMap(appSpecificShortcutReMap_mutex);
for (const auto& itApp : appSpecificShortcutReMap)
{
// Iterate over apps
@@ -538,7 +572,6 @@ bool KeyboardManagerState::SaveConfigToFile()
appSpecificRemapShortcutsArray.Append(keys);
}
}
lockAppSpecificShortcutReMap.unlock();
remapShortcuts.SetNamedValue(KeyboardManagerConstants::GlobalRemapShortcutsSettingName, globalRemapShortcutsArray);
remapShortcuts.SetNamedValue(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName, appSpecificRemapShortcutsArray);
@@ -596,3 +629,20 @@ std::wstring KeyboardManagerState::GetActivatedApp()
{
return activatedAppSpecificShortcutTarget;
}
bool KeyboardManagerState::AreRemappingsEnabled()
{
return remappingsEnabled;
}
void KeyboardManagerState::RemappingsDisabledWrapper(std::function<void()> method)
{
// Disable keyboard remappings
remappingsEnabled = false;
// Run the method which requires the remappings to be disabled
method();
// Re-enable the keyboard remappings
remappingsEnabled = true;
}