-
Notifications
You must be signed in to change notification settings - Fork 24.7k
[iOS] Fix data races in RCTImageLoader
and RCTNetworkTask
with shared atomic counters
#45114
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Conversation
Base commit: e320ab4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch and smart solution!
I left some questions and a few minor changes requested.
@@ -863,6 +867,7 @@ | |||
isa = XCBuildConfiguration; | |||
buildSettings = { | |||
ALWAYS_SEARCH_USER_PATHS = NO; | |||
CC = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change should be reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typedef NSUInteger (^NSUIntegerCounter)(void); | ||
typedef uint64_t (^UInt64Counter)(void); | ||
|
||
RCT_EXTERN NSUIntegerCounter RCTCreateAtomicNSUIntegerCounter(void); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the approach, but I'm a bit confused by the API namig.
We have an API that creates an atomicCounter and that returns a function that returns the counter an increment it.
It's a bit confusing that we use the RCTCreateAtomicNSUIntegerCounter
to actually return a function that is getAndIncrementCounter
.
By calling RCTCreateAtomicNSUIntegerCounter
I expect to get a counter as a result not a function to increment a counter. 🤔
Can we think of a better name for this API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of RCTNetworkTask:24
, we create a static counter like so:
static const auto generateRequestId = RCTCreateAtomicNSUIntegerCounter();
That is, the counter function is not static unless it is held statically. In turn, calling on generateRequestId
will yield an incremented value. I believe it is more appropriate to name the variable that holds the block generateRequestId
, as calling this block indeed increments the counter, while RCTCreateAtomicNSUIntegerCounter
creates a new counter block where count is initialized to 0
for each call.
#include <memory> | ||
|
||
NSUIntegerCounter RCTCreateAtomicNSUIntegerCounter(void) { | ||
auto count = std::make_shared<std::atomic<NSUInteger>>(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the approach is very clever, but talking about memory management, when is the std::atomic
destroyed?
Because as far as I can see, the count is captured by the closure. So I'd say that when there are no closures that keep the reference alive, the shared pointer will be deleted. Correct?
Can we add a test to make sure that this is actually happening?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that this is correct. I have attempted to allow for testing this via this commit
Hi @cipolleschi, thanks for the feedback! I will look into the comments and think of improvements. Will push changes when I have the time. |
@cipolleschi please see the proposed changes above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for verifying that the memory is deallocated.
For maintainability I'd rather remove the spy code, so the code is clearer and easier to understand and maintain.
Thanks for putting extra work in verifying the memory management! ❤️
(also, remember to rebase on top of main, please! 🙏 ) |
Since we already use Objective-C++ quite widely across the codebase, can we just replace this with std::atomic? |
We certainly can. My approach was to generalize the solution, seeing it as one could make use of atomic counter functions for cases to come. I believe testing becomes easier as well, as one does not have to for example instantiate a class wherein a static count variable is used. Is it more preferable from your point of view not to introduce the counters? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather rely on standard C++ fundamentals and use those directly, rather than using wrappers which involve shared_ptr, which have their own.
In pure Objective-C, we should also be able to use OSAtomicIncrement64
as a much simpler solution.
@javache I have revised according to your review. Please see changes. |
@javache has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
This pull request was successfully merged by @hakonk in ffc16fc. When will my fix make it into a release? | How to file a pick request? |
…tomic counters (#45114) Summary: In order to fix the data races described in #44715, I propose a simple solution by leveraging shared counter functions wherein `std::atomic` is the backing for the integer values. ## Changelog: [iOS] [Fixed] - Implement shared atomic counters and replace static integers in `RCTImageLoader` and `RCTNetworkTask` that were accessed concurrently, which in some cases lead to data races. Pull Request resolved: #45114 Test Plan: Added unit tests for the counters in `RCTSharedCounterTests`. Reviewed By: cipolleschi Differential Revision: D59155076 Pulled By: javache fbshipit-source-id: f73afce6a816ad3226ed8c123cb2ccf4183549a0
…tomic counters (#45114) Summary: In order to fix the data races described in #44715, I propose a simple solution by leveraging shared counter functions wherein `std::atomic` is the backing for the integer values. ## Changelog: [iOS] [Fixed] - Implement shared atomic counters and replace static integers in `RCTImageLoader` and `RCTNetworkTask` that were accessed concurrently, which in some cases lead to data races. Pull Request resolved: #45114 Test Plan: Added unit tests for the counters in `RCTSharedCounterTests`. Reviewed By: cipolleschi Differential Revision: D59155076 Pulled By: javache fbshipit-source-id: f73afce6a816ad3226ed8c123cb2ccf4183549a0
Summary:
In order to fix the data races described in #44715, I propose a simple solution by leveraging shared counter functions wherein
std::atomic
is the backing for the integer values.Changelog:
[iOS] [Fixed] - Implement shared atomic counters and replace static integers in
RCTImageLoader
andRCTNetworkTask
that were accessed concurrently, which in some cases lead to data races.Test Plan:
Added unit tests for the counters in
RCTSharedCounterTests
.