Skip to content

Commit 62d3409

Browse files
dryganetsfacebook-github-bot
authored andcommitted
There is a small gap in the SynchronizedWeakHashSet implementation. T… (#24015)
Summary: There is a small gap in the SynchronizedWeakHashSet implementation - the containsKey method of the WeakHashMap is modifying hence calling it during the iteration might cause ConcurrentModificationException. Added a command DO_IF_CONTAINS to safely handle this case. [Android] [Bugfix] - Should fix a ConcurrentModificationException in onResume. Pull Request resolved: #24015 Reviewed By: mdvacca Differential Revision: D14507342 Pulled By: fkgozali fbshipit-source-id: 2998bffb06e2cbacd8df1780964355842b1cc4a0
1 parent f49f181 commit 62d3409

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,17 @@ public void addLifecycleEventListener(final LifecycleEventListener listener) {
176176
new Runnable() {
177177
@Override
178178
public void run() {
179-
if (!mLifecycleEventListeners.contains(listener)) {
180-
return;
181-
}
182-
try {
183-
listener.onHostResume();
184-
} catch (RuntimeException e) {
185-
handleException(e);
186-
}
179+
180+
mLifecycleEventListeners.doIfContains(listener, new Runnable() {
181+
@Override
182+
public void run() {
183+
try {
184+
listener.onHostResume();
185+
} catch (RuntimeException e) {
186+
handleException(e);
187+
}
188+
}
189+
});
187190
}
188191
});
189192
break;

ReactAndroid/src/main/java/com/facebook/react/bridge/SynchronizedWeakHashSet.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@ public class SynchronizedWeakHashSet<T> {
2424
private Queue<Pair<T, Command>> mPendingOperations = new ArrayDeque<>();
2525
private boolean mIterating;
2626

27-
public boolean contains(T item) {
27+
public void doIfContains(T item, Runnable runnable) {
2828
synchronized (mMap) {
29-
return mMap.containsKey(item);
29+
if (mIterating) {
30+
mPendingOperations.add(new Pair<>(item, Command.newDoIfContains(runnable)));
31+
} else {
32+
if (mMap.containsKey(item)) {
33+
runnable.run();
34+
}
35+
}
3036
}
3137
}
3238

@@ -61,13 +67,19 @@ public void iterate(Iteration<T> iterated) {
6167

6268
while (!mPendingOperations.isEmpty()) {
6369
Pair<T, Command> pair = mPendingOperations.poll();
64-
switch (pair.second) {
70+
Command command = pair.second;
71+
switch (command.getType()) {
6572
case ADD:
6673
mMap.put(pair.first, null);
6774
break;
6875
case REMOVE:
6976
mMap.remove(pair.first);
7077
break;
78+
case DO_IF_CONTAINS:
79+
if (mMap.containsKey(pair.first)) {
80+
command.execute();
81+
}
82+
break;
7183
default:
7284
throw new AssertionException("Unsupported command" + pair.second);
7385
}
@@ -79,8 +91,40 @@ public interface Iteration<T> {
7991
void iterate(T item);
8092
}
8193

82-
private enum Command {
94+
private enum CommandType {
8395
ADD,
84-
REMOVE
96+
REMOVE,
97+
DO_IF_CONTAINS
98+
}
99+
100+
private static class Command {
101+
public static final Command ADD = new Command(CommandType.ADD);
102+
public static final Command REMOVE = new Command(CommandType.REMOVE);
103+
104+
private CommandType mType;
105+
private Runnable mRunnable;
106+
107+
public static Command newDoIfContains(Runnable runnable) {
108+
return new Command(CommandType.DO_IF_CONTAINS, runnable);
109+
}
110+
111+
private Command(CommandType type) {
112+
this(type, null);
113+
}
114+
115+
private Command(CommandType type, Runnable runnable) {
116+
mType = type;
117+
mRunnable = runnable;
118+
}
119+
120+
public CommandType getType() {
121+
return mType;
122+
}
123+
124+
public void execute() {
125+
if (mRunnable != null) {
126+
mRunnable.run();
127+
}
128+
}
85129
}
86130
}

0 commit comments

Comments
 (0)