[EXTERNAL] Lock RateLimiter.shouldProceed() entirely to avoid race conditions (#4635) via @nguyenhuy #4637
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Checklist
purchases-android
and hybridsMotivation
Introduced in #3709,
RateLimiter
is used exclusively byPurchases.syncAttributesAndOfferingsIfNeeded(completion:)
. The documentation of this method doesn't mention any threading requirement, so it's reasonable to assume that it can be called on any thread. This meansRateLimiter
needs to be thread safe, otherwise clients will run into race conditions as reported in #4440.Description
Fortunately the
RateLimiter
class is quite simple.Its contants don't need to be atomic/locked since they are thread safe by definition. Turning
timestamps
andindex
vars into atomics doesn't help much in this case becauseshouldProceed()
needs multiple reads and writes in a "transaction".Using a
Lock
to wrap the whole method is the simplest and self-contained approach so I picked it. Android is doing the same thing (here).The lock is a non-recursive lock because there is no need for reentrant. And in principle I avoid recursive locks as much as possible since one can easily lose control over the locks, especially in more complicated use cases/codebases. This is a big problem that we have had for a long time in AsyncDisplayKit/Texture.
Unit tests remain the same because testing for race conditions is rather difficult and unpredictable. Texture has a bunch of thrash tests that kind of work but not 100%. Since the subject under test in this case is rather simple and the fix is straightforward it should work. Otherwise, we can have follow up PRs for both iOS and Android.
This should resolve #4440.
This PR also renames
RateLimiterRests.swift
toRateLimiterTests.swift
.Contribution
Original PR #4635 by @nguyenhuy