Skip to content

Commit

Permalink
fix: race condition when keys are added or removed while the object i…
Browse files Browse the repository at this point in the history
…s being traversed (#1062)
  • Loading branch information
shlusiak committed Oct 17, 2021
1 parent fd20702 commit d28e64d
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 1 deletion.
3 changes: 3 additions & 0 deletions parse/src/main/java/com/parse/ParseObject.java
Expand Up @@ -1474,6 +1474,9 @@ public Date getCreatedAt() {
/**
* Returns a set view of the keys contained in this object. This does not include createdAt,
* updatedAt, authData, or objectId. It does include things like username and ACL.
*
* <p>Note that while the returned set is unmodifiable, it is in fact not thread-safe, and
* creating a copy is recommended before iterating over it.
*/
public Set<String> keySet() {
synchronized (mutex) {
Expand Down
11 changes: 10 additions & 1 deletion parse/src/main/java/com/parse/ParseTraverser.java
Expand Up @@ -8,10 +8,12 @@
*/
package com.parse;

import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
Expand Down Expand Up @@ -94,7 +96,14 @@ private void traverseInternal(
} else if (root instanceof ParseObject) {
if (traverseParseObjects) {
ParseObject object = (ParseObject) root;
for (String key : object.keySet()) {
// Because the object's keySet is not thread safe, because the underlying Map isn't,
// we need to create a copy before iterating over the object's keys to avoid
// ConcurrentModificationExceptions
Set<String> keySet;
synchronized (object.mutex) {
keySet = new HashSet<>(object.keySet());
}
for (String key : keySet) {
traverseInternal(object.get(key), true, seen);
}
}
Expand Down

0 comments on commit d28e64d

Please sign in to comment.