From 8d710bfaca6b57291bf953bda848f3cf64ae2818 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 30 Jan 2024 03:12:50 -0800 Subject: [PATCH] Refactor RCT_handleKeyCommand to avoid concurrency issues (#42708) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42708 When pressing `r` on simulator in Bridgeless mode, we have a race condition between: - RCTKeyCommands evaluating the blocks to be invoked - ReactNative invalidating the DevMenu with the list of RCTKeyCommands (on which ReactNative is iterating). The fix checks which commands need to be executed, stores them in an array and then iterates on the array, which is local to the function call, avoiding any concurrency issue. ## Changelog: [iOS][Fixed] - Refactored RCT_handleKeyCommand to avoid concurrency issues Reviewed By: motiz88 Differential Revision: D53186262 fbshipit-source-id: 60ae8974a9df7289395c8a9e9abe2e34e4c40309 --- packages/react-native/React/Base/RCTKeyCommands.m | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Base/RCTKeyCommands.m b/packages/react-native/React/Base/RCTKeyCommands.m index 1ceaf6ec0dceb1..19ef5b1f0d461a 100644 --- a/packages/react-native/React/Base/RCTKeyCommands.m +++ b/packages/react-native/React/Base/RCTKeyCommands.m @@ -173,13 +173,24 @@ - (void)handleKeyUIEventSwizzle:(UIEvent *)event - (void)RCT_handleKeyCommand:(NSString *)input flags:(UIKeyModifierFlags)modifierFlags { + // In Bridgeless mode we might incur in some concurrency issues + // where the React Native instance is invalidated while iterating on the + // list of available commands. + // That will cleanup the set while iterating, which is a not allowed mutation. + // To work around that, we store the commands that we need to execute in a separate + // array, local to this function call, so we don't incur in concurrency issues + NSMutableArray *commandsToExecute = [NSMutableArray new]; for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) { if ([command matchesInput:input flags:modifierFlags]) { if (command.block) { - command.block(nil); + [commandsToExecute addObject:command]; } } } + + for (RCTKeyCommand *command in commandsToExecute) { + command.block(nil); + } } + (instancetype)sharedInstance