Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: race condition when keys are added or removed while the object is being traversed #1062

Conversation

shlusiak
Copy link
Contributor

@shlusiak shlusiak commented Sep 5, 2020

Should fix the ConcurrentModificationException in #1061 by creating a local copy before iterating over the ParseObject's keys, with the cost of potentially missing some newly added keys or traversing into a null value.

@mtrezza
Copy link
Member

mtrezza commented Oct 11, 2021

cost of potentially missing some newly added keys or traversing into a null value.

@shlusiak Could you give more details about that cost?

@codecov
Copy link

codecov bot commented Oct 11, 2021

Codecov Report

Merging #1062 (f046475) into master (5d40917) will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #1062      +/-   ##
============================================
+ Coverage     65.28%   65.29%   +0.01%     
  Complexity     2218     2218              
============================================
  Files           122      122              
  Lines          9957     9960       +3     
  Branches       1337     1337              
============================================
+ Hits           6500     6503       +3     
  Misses         2945     2945              
  Partials        512      512              
Impacted Files Coverage Δ
parse/src/main/java/com/parse/ParseObject.java 60.30% <ø> (ø)
parse/src/main/java/com/parse/ParseTraverser.java 78.33% <100.00%> (+1.14%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fd20702...f046475. Read the comment docs.

@azlekov
Copy link
Contributor

azlekov commented Oct 11, 2021

@mtrezza I think @shlusiak refers that because of the new thread-safe approach, on traversal if there are newly added keys they will be missed in this traversal. On next traversal they will be taken in mind. The funny thing is that on Kotlin I faced this issue a lot using coroutines and patch it by trying not to have multiple traversal in same time.

@mtrezza
Copy link
Member

mtrezza commented Oct 11, 2021

How does this work technically? Like a read/write queue?

@shlusiak shlusiak changed the title Create a copy of the ParseObject's keySet when iterating over it. fix: Create a copy of the ParseObject's keySet when iterating over it. Oct 15, 2021
@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title fix: Create a copy of the ParseObject's keySet when iterating over it. fix: create a copy of the ParseObject's keySet when iterating over it. Oct 15, 2021
@parse-github-assistant
Copy link

parse-github-assistant bot commented Oct 15, 2021

Thanks for opening this pull request!

  • ❌ Please edit your post and use the provided template when creating a new pull request. This helps everyone to understand your post better and asks for essential information to quicker review the pull request.

@shlusiak
Copy link
Contributor Author

Fixed merge conflicts. Previously the app could crash if keys are added or removed while the object is being traversed. With this change, at time of traversal a copy of the keyset is created that is traversed instead, fixing the race conditions of concurrent access. This does not resolve other issues of concurrent access, e.g. adding a key while traversal is happening will miss the newly added key, but also removing a key while traversal is happening would see a key where the value does not exist any more (null) and tries to traverse into it. That should work, as the traverseInternal checks for that, but it is not ideal.

Copy link
Contributor

@azlekov azlekov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@azlekov
Copy link
Contributor

azlekov commented Oct 15, 2021

@mtrezza may you change the title from

fix: create a copy of the ParseObject's keySet when iterating over it.

to

fix: race condition when keys are added or removed while the object is being traversed

@shlusiak shlusiak changed the title fix: create a copy of the ParseObject's keySet when iterating over it. fix: race condition when keys are added or removed while the object is being traversed Oct 15, 2021
@mtrezza
Copy link
Member

mtrezza commented Oct 15, 2021

Ready for merge?

@azlekov
Copy link
Contributor

azlekov commented Oct 15, 2021

First-time contributors need a maintainer to approve running workflows.

Can we have the CI running to see the checks passed @mtrezza?

@mtrezza
Copy link
Member

mtrezza commented Oct 15, 2021

@shlusiak can you lint the code?
@L3K0V do you think we should describe how to run lint fix in the contribution guide, or is that clear for developers?

@azlekov
Copy link
Contributor

azlekov commented Oct 15, 2021

CONTRIBUTING.md is already updated. If you think it should be rephrased, let me know @mtrezza

@mtrezza
Copy link
Member

mtrezza commented Oct 15, 2021

@L3K0V Does ./gradlew spotlessApply do more than lint? What do you think about renaming this to ./gradlew lint-fix, like we have in parse server, dashboard, etc for consistency? Or is spotlessApply a term an Android developer can be expected to understand?

@shlusiak
Copy link
Contributor Author

Ran spotlessApply, which seems to reorder imports and fixes comments to satisfy lint.

@azlekov
Copy link
Contributor

azlekov commented Oct 16, 2021

@L3K0V Does ./gradlew spotlessApply do more than lint? What do you think about renaming this to ./gradlew lint-fix, like we have in parse server, dashboard, etc for consistency? Or is spotlessApply a term an Android developer can be expected to understand?

Spotless is widely spread across Java and Kotlin developers. There's standard lint task which does some other lint checks. I can make it run spotlessApply. I will consider opening a separate PR.

Meanwhile I will be glad if this and #1058 are merged soon, as they seems to fix a lot of issues.

@mtrezza mtrezza merged commit d28e64d into parse-community:master Oct 17, 2021
@mtrezza
Copy link
Member

mtrezza commented Oct 17, 2021

Looks good! Thanks for the fix!

parseplatformorg pushed a commit that referenced this pull request Oct 17, 2021
## [2.0.2](2.0.1...2.0.2) (2021-10-17)

### Bug Fixes

* race condition when keys are added or removed while the object is being traversed ([#1062](#1062)) ([d28e64d](d28e64d))
@parseplatformorg
Copy link

🎉 This pull request has been released in version 2.0.2

@parseplatformorg parseplatformorg added the state:released Released as stable version label Oct 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released Released as stable version
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants