Skip to content

Refactored DeviceCache to improve thread safety#983

Merged
NachoSoto merged 11 commits into
RevenueCat:mainfrom
NachoSoto:device-cache-safety
Nov 29, 2021
Merged

Refactored DeviceCache to improve thread safety#983
NachoSoto merged 11 commits into
RevenueCat:mainfrom
NachoSoto:device-cache-safety

Conversation

@NachoSoto

@NachoSoto NachoSoto commented Nov 22, 2021

Copy link
Copy Markdown
Contributor

Fixes #739.

Changes:

  • Created Atomic, based on Lock, to abstract atomic access and modifications to values.
  • Simplified InMemoryCachedObject using Atomic (tests still pass!).
  • Created SynchronizedUserDefaults using Atomic, to enforce atomic access to UserDefaults.
  • Refactored DeviceCache using Atomic to enforce all access to UserDefaults are synchronized:
    • DeviceCache no longer stores a UserDefaults instance (only SynchronizedUserDefaults), so we guarantee we don't do unsynchronized modifications.
    • The old threadUnsafe_ methods are now static, which ensures they don't access any local state. Instead, UserDefaults is injected, and DeviceCache calls these methods using SynchronizedUserDefaults.read/SynchronizedUserDefaults.write.
    • The combination of both of these things eliminates potential for programmer error within the class.
    • There's still potential for what's described in DeviceCache thread safety #739, that a user of DeviceCache reads data, and THEN writes it again, without synchronizing both operations. This PR doesn't address that, but I haven't found any examples of that being an issue, and if we do, we should move both operations into a single method in DeviceCache that can enforce the semantics.

@NachoSoto NachoSoto requested review from a team and taquitos November 22, 2021 00:12

@taquitos taquitos left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is great. Before we merge, I'd love to see a new markdown doc in docs/ that describes the relationships between Atomic, Lock, and UserDefaults. I'm thinking something like an ELI5- keeping in mind that as we onboard junior engineers, they likely won't have a lot of exposure to generics/atomicity.

@aboedo aboedo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is absolutely brilliant! ⭐ ✨
Perhaps we should add tests for Atomic to ensure we don't break it in the future? I get the feeling that a future dev could easily mess up if they ever decide to modify it

Comment thread Purchases/Misc/Atomic.swift Outdated
@NachoSoto

NachoSoto commented Nov 24, 2021

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

Perhaps we should add tests for Atomic to ensure we don't break it in the future?

Totally 👍 will do

Before we merge, I'd love to see a new markdown doc in docs/ that describes the relationships between Atomic, Lock, and UserDefaults

Great idea! Will do.

I'm thinking something like an ELI5

ELI5?

@taquitos

Copy link
Copy Markdown
Contributor

ELI5: Literally: Explain like I'm five years old: https://www.reddit.com/r/explainlikeimfive/
Making the explanation as easy as possible for folks who have pretty much no context 😄

@aboedo

aboedo commented Nov 24, 2021

Copy link
Copy Markdown
Member

that explains a lot 😂 I googled ELI5 and this python library popped up, couldn't figure out how that would apply here

@NachoSoto

NachoSoto commented Nov 25, 2021

Copy link
Copy Markdown
Contributor Author

I think this is great. Before we merge, I'd love to see a new markdown doc in docs/ that describes the relationships between Atomic, Lock, and UserDefaults. I'm thinking something like an ELI5- keeping in mind that as we onboard junior engineers, they likely won't have a lot of exposure to generics/atomicity.

Done! Let me know what you think: https://github.com/RevenueCat/purchases-ios/blob/3d86e2635398623350a314e71205db8761eee662/docs/ThreadSafety.md

@NachoSoto

Copy link
Copy Markdown
Contributor Author

This is absolutely brilliant! ⭐ ✨
Perhaps we should add tests for Atomic to ensure we don't break it in the future? I get the feeling that a future dev could easily mess up if they ever decide to modify it

Done. I've only included very simple tests that don't cover thread-safety itself. Such tests can be brittle and, if the implementation or the test is incorrect, lead to sporadic failures. But let me know if you think we should still have something to cover that.

@aboedo aboedo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks awesome! Thanks for the updates!

Comment thread PurchasesTests/Misc/AtomicTests.swift Outdated
Comment thread PurchasesTests/Misc/AtomicTests.swift Outdated
Comment thread PurchasesTests/Misc/AtomicTests.swift Outdated
Comment thread docs/ThreadSafety.md Outdated
@NachoSoto

Copy link
Copy Markdown
Contributor Author

Thanks for the suggestions! I like adding the expected results in the test name 👍

@NachoSoto NachoSoto merged commit 5afbe63 into RevenueCat:main Nov 29, 2021
@NachoSoto NachoSoto deleted the device-cache-safety branch November 29, 2021 18:13
NachoSoto added a commit to NachoSoto/purchases-ios that referenced this pull request Jun 7, 2022
See RevenueCat#983. This is less error-prone as it's impossible to have deadlocks, or to access the data without locking (like we were doing in some cases).
Also, this approach has 2 separate locks for each property, so unrelated accesses won't lock each other now.
NachoSoto added a commit that referenced this pull request Jun 7, 2022
See #983. This is less error-prone as it's impossible to have deadlocks, or to access the data without locking (like we were doing in some cases).
Also, this approach has 2 separate locks for each property, so unrelated accesses won't lock each other now.
NachoSoto added a commit that referenced this pull request Jun 7, 2022
See #983. This is less error-prone as it's impossible to have deadlocks, or to access the data without locking (like we were doing in some cases).
Also, this approach has 2 separate locks for each property, so unrelated accesses won't lock each other now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DeviceCache thread safety

3 participants