[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

@@ -204,7 +204,7 @@ namespace BufferValidationHelpers
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
if (errorType == KeyboardManagerHelper::ErrorType::NoError)
{
std::variant<DWORD, Shortcut> tempShortcut;
KeyShortcutUnion tempShortcut;
if (isHybridControl && selectedKeyCodes.size() == 1)
{
tempShortcut = selectedKeyCodes[0];

View File

@@ -252,9 +252,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow);
// Load existing remaps into UI
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyReMap_mutex);
std::unordered_map<DWORD, std::variant<DWORD, Shortcut>> singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
lock.unlock();
SingleKeyRemapTable singleKeyRemapCopy = keyboardManagerState.singleKeyReMap;
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy);
for (const auto& it : singleKeyRemapCopy)
@@ -272,9 +271,13 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
// Save the updated shortcuts remaps to file.
bool saveResult = keyboardManagerState.SaveConfigToFile();
// Disable the remappings while the remapping table is updated
keyboardManagerState.RemappingsDisabledWrapper(
[&keyboardManagerState]() {
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
// Save the updated shortcuts remaps to file.
bool saveResult = keyboardManagerState.SaveConfigToFile();
});
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
};

View File

@@ -226,17 +226,20 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
// Load existing os level shortcuts into UI
std::unique_lock<std::mutex> lockOSLevel(keyboardManagerState.osLevelShortcutReMap_mutex);
for (const auto& it : keyboardManagerState.osLevelShortcutReMap)
// Create copy of the remaps to avoid concurrent access
ShortcutRemapTable osLevelShortcutReMapCopy = keyboardManagerState.osLevelShortcutReMap;
for (const auto& it : osLevelShortcutReMapCopy)
{
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, it.first, it.second.targetShortcut);
}
lockOSLevel.unlock();
// Load existing app-specific shortcuts into UI
std::unique_lock<std::mutex> lockAppSpecific(keyboardManagerState.appSpecificShortcutReMap_mutex);
// Create copy of the remaps to avoid concurrent access
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = keyboardManagerState.appSpecificShortcutReMap;
// Iterate through all the apps
for (const auto& itApp : keyboardManagerState.appSpecificShortcutReMap)
for (const auto& itApp : appSpecificShortcutReMapCopy)
{
// Iterate through shortcuts for each app
for (const auto& itShortcut : itApp.second)
@@ -244,7 +247,6 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
ShortcutControl::AddNewShortcutControlRow(shortcutTable, keyboardRemapControlObjects, itShortcut.first, itShortcut.second.targetShortcut, itApp.first);
}
}
lockAppSpecific.unlock();
// Apply button
Button applyButton;
@@ -256,9 +258,13 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
// Save the updated key remaps to file.
bool saveResult = keyboardManagerState.SaveConfigToFile();
// Disable the remappings while the remapping table is updated
keyboardManagerState.RemappingsDisabledWrapper(
[&keyboardManagerState]() {
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
// Save the updated key remaps to file.
bool saveResult = keyboardManagerState.SaveConfigToFile();
});
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
};

View File

@@ -341,7 +341,7 @@ void KeyDropDownControl::ValidateShortcutFromDropDownList(Grid table, StackPanel
{
// Check for errors only if the current selection is a valid shortcut
std::vector<DWORD> selectedKeyCodes = KeyboardManagerHelper::GetKeyCodesFromSelectedIndices(keyDropDownControlObjects[i]->GetSelectedIndicesFromStackPanel(parent), GetKeyCodeList(true, colIndex == 1));
std::variant<DWORD, Shortcut> currentShortcut;
KeyShortcutUnion currentShortcut;
if (selectedKeyCodes.size() == 1 && isHybridControl)
{
currentShortcut = selectedKeyCodes[0];

View File

@@ -11,11 +11,11 @@ namespace LoadingAndSavingRemappingHelper
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
{
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError;
std::map<std::wstring, std::set<std::variant<DWORD, Shortcut>>> ogKeys;
std::map<std::wstring, std::set<KeyShortcutUnion>> ogKeys;
for (int i = 0; i < remappings.size(); i++)
{
std::variant<DWORD, Shortcut> ogKey = remappings[i].first[0];
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
KeyShortcutUnion ogKey = remappings[i].first[0];
KeyShortcutUnion newKey = remappings[i].first[1];
std::wstring appName = remappings[i].second;
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut());
@@ -24,7 +24,7 @@ namespace LoadingAndSavingRemappingHelper
// Add new set for a new target app name
if (ogKeys.find(appName) == ogKeys.end())
{
ogKeys[appName] = std::set<std::variant<DWORD, Shortcut>>();
ogKeys[appName] = std::set<KeyShortcutUnion>();
}
if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) == ogKeys[appName].end())
@@ -52,7 +52,7 @@ namespace LoadingAndSavingRemappingHelper
for (int i = 0; i < remappings.size(); i++)
{
DWORD ogKey = std::get<DWORD>(remappings[i].first[0]);
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
KeyShortcutUnion newKey = remappings[i].first[1];
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut())))
{
@@ -75,7 +75,7 @@ namespace LoadingAndSavingRemappingHelper
}
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
void CombineRemappings(SingleKeyRemapTable& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey)
{
if (table.find(leftKey) != table.end() && table.find(rightKey) != table.end())
{
@@ -90,7 +90,7 @@ namespace LoadingAndSavingRemappingHelper
}
// Function to pre process the remap table before loading it into the UI
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table)
void PreProcessRemapTable(SingleKeyRemapTable& table)
{
// Pre process the table to combine L and R versions of Ctrl/Alt/Shift/Win that are mapped to the same key
CombineRemappings(table, VK_LCONTROL, VK_RCONTROL, VK_CONTROL);
@@ -109,7 +109,7 @@ namespace LoadingAndSavingRemappingHelper
for (int i = 0; i < remappings.size(); i++)
{
DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
std::variant<DWORD, Shortcut> newKey = remappings[i].first[1];
KeyShortcutUnion newKey = remappings[i].first[1];
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
{
@@ -177,7 +177,7 @@ namespace LoadingAndSavingRemappingHelper
for (int i = 0; i < remappings.size(); i++)
{
Shortcut originalShortcut = std::get<Shortcut>(remappings[i].first[0]);
std::variant<DWORD, Shortcut> newShortcut = remappings[i].first[1];
KeyShortcutUnion newShortcut = remappings[i].first[1];
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut())))
{

View File

@@ -14,10 +14,10 @@ namespace LoadingAndSavingRemappingHelper
std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings);
// Function to combine remappings if the L and R version of the modifier is mapped to the same key
void CombineRemappings(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey);
void CombineRemappings(std::unordered_map<DWORD, KeyShortcutUnion>& table, DWORD leftKey, DWORD rightKey, DWORD combinedKey);
// Function to pre process the remap table before loading it into the UI
void PreProcessRemapTable(std::unordered_map<DWORD, std::variant<DWORD, Shortcut>>& table);
void PreProcessRemapTable(std::unordered_map<DWORD, KeyShortcutUnion>& table);
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired);

View File

@@ -65,7 +65,7 @@ void ShortcutControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel
}
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const std::variant<DWORD, Shortcut>& newKeys, const std::wstring& targetAppName)
void ShortcutControl::AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
{
// Textbox for target application
TextBox targetAppTextBox;

View File

@@ -48,7 +48,7 @@ public:
ShortcutControl(Grid table, const int colIndex, TextBox targetApp);
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const std::variant<DWORD, Shortcut>& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
static void AddNewShortcutControlRow(Grid& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
StackPanel getShortcutControl();

View File

@@ -72,7 +72,7 @@ void SingleKeyRemapControl::UpdateAccessibleNames(StackPanel sourceColumn, Stack
}
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const std::variant<DWORD, Shortcut> newKey)
void SingleKeyRemapControl::AddNewControlKeyRemapRow(Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutUnion newKey)
{
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;

View File

@@ -43,7 +43,7 @@ public:
SingleKeyRemapControl(Grid table, const int colIndex);
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
static void AddNewControlKeyRemapRow(winrt::Windows::UI::Xaml::Controls::Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const std::variant<DWORD, Shortcut> newKey = NULL);
static void AddNewControlKeyRemapRow(winrt::Windows::UI::Xaml::Controls::Grid& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const KeyShortcutUnion newKey = NULL);
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();