parse-community / Parse-SDK-Android

The Android SDK for Parse Platform
https://parseplatform.org/
Other
1.88k stars 735 forks source link

Parse Relation, add/remove object causes ConcurrentModificationException #1070

Open SkyGanfor opened 3 years ago

SkyGanfor commented 3 years ago

Hi, I noticed a possible bug in ParseRelations when trying to add or remove an object from a relation. I've found the crash consistently happening when preforming the following:

1: add an object to the relation. 2: add a different object to the same relation. 3: removing the first object from the relation.

Most of the time i get a crash first try, sometimes it doesn't but performing the same list of actions repeatedly will cause it eventually. Here is the full crash:

   java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextNode(HashMap.java:1441)
        at java.util.HashMap$KeyIterator.next(HashMap.java:1465)
        at com.parse.ParseRelationOperation.removeParseObjectFromSet(ParseRelationOperation.java:124)
        at com.parse.ParseRelationOperation.removeAllParseObjectsFromSet(ParseRelationOperation.java:136)
        at com.parse.ParseRelationOperation.mergeWithPrevious(ParseRelationOperation.java:243)
        at com.parse.ParseObject.performOperation(ParseObject.java:2998)
        at com.parse.ParseRelation.remove(ParseRelation.java:149)

My Android app currently allows an admin user to set permissions for other users, and they do this using a check boxes to allow or deny a user from reading or writing objects. I use a ParseRole and add the users to the "users" ParseRelation.

The admin users are shown a RecyclerView that holds a list of users and a check boxes allowing them to be given permission to read and write. The RecyclerViewAdapter holds a reference to the user ParseRelation for read and write, and the user is added or removed when the checkbox value changes. (Below is the a picture of the UI and my snippet of RecyclerViewAdapter code)

Screenshot_1603895363

            mReadCheckListener = (compoundButton, checked) -> {
            int position = getBindingAdapterPosition();
            if (position != RecyclerView.NO_POSITION) {
                        ParseUser user = getItem(position);
                        if (checked) {
                            mReadRelation.add(user);
                        } else {
                            mReadRelation.remove(user);
                        }
                    }
            };
            mWriteCheckListener = (compoundButton, checked) -> {
             int position = getBindingAdapterPosition();
             if (position != RecyclerView.NO_POSITION) {
                        ParseUser user = getItem(position);
                        if (checked) {
                            mWriteRelation.add(user);
                        } else {
                            mWriteRelation.remove(user);
                        }
                    }
            };

Any help or feedback is appreciated! In the meantime, i've moved over my ParseRelation logic to Cloud Code as a temporary solution to the problem.

azlekov commented 2 years ago

@shlusiak do you have idea about this?

shlusiak commented 2 years ago

@L3K0V I haven't had a look yet, but from the stack track I wouldn't be surprised if we are iterating over a HashMap's keys without copying the keys first, before we make any modifications to the Map.

azlekov commented 2 years ago

@shlusiak can we consider this as closed from your perspective, and if still occurs to open this again?

shlusiak commented 2 years ago

I think the place I fixed this is different though. I'd not be convinced that this is fixed, but I'd be happy to see if the requester can confirm that this is still happening with the latest version that has all my fixes merged in.

SkyGanfor commented 2 years ago

Sadly I am no longer working on this project, so I'm not able to confirm if this has been fixed.