Skip to content

Commit ebbd22c

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Add nullptr check to SharedFunction (#38075)
Summary: Pull Request resolved: #38075 changelog: [internal] `SharedFunction<>` is created with nullptr for its internal `std::function`. If called after created with default constructor, it crashes app. It also does not have API to check if its internal function is not nullptr. With image cancelation, there is a race between when native component calls `imageRequest.cancel()` and when cancelation function is set in `RCTImageManager`. To fix this, this diff adds a nullptr check inside SharedFunction. So it is always safe to call. Reviewed By: javache Differential Revision: D47022957 fbshipit-source-id: 0a04a87cd1ffe6bf3ca2fded38f689f06cc92ca9
1 parent a702d05 commit ebbd22c

File tree

9 files changed

+31
-34
lines changed

9 files changed

+31
-34
lines changed

packages/react-native/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ - (void)_setStateAndResubscribeImageResponseObserver:(ImageShadowNode::ConcreteS
109109
// This will only become issue if we decouple life cycle of a
110110
// ShadowNode from ComponentView, which is not something we do now.
111111
imageRequest.cancel();
112+
imageRequest.cancel();
113+
imageRequest.cancel();
114+
imageRequest.cancel();
112115
}
113116
}
114117

packages/react-native/ReactCommon/react/renderer/components/image/ImageShadowNode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ImageShadowNode final : public ConcreteViewShadowNode<
4646
ShadowNodeFamily::Shared const & /*family*/,
4747
ComponentDescriptor const &componentDescriptor) {
4848
auto imageSource = ImageSource{ImageSource::Type::Invalid};
49-
return {imageSource, {imageSource, nullptr}, 0};
49+
return {imageSource, {imageSource, nullptr, {}}, 0};
5050
}
5151

5252
#pragma mark - LayoutableShadowNode

packages/react-native/ReactCommon/react/renderer/imagemanager/ImageRequest.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <react/renderer/imagemanager/ImageResponseObserverCoordinator.h>
1313
#include <react/renderer/imagemanager/ImageTelemetry.h>
1414
#include <react/renderer/imagemanager/primitives.h>
15+
#include <react/utils/SharedFunction.h>
1516

1617
namespace facebook::react {
1718

@@ -29,7 +30,8 @@ class ImageRequest final {
2930
*/
3031
ImageRequest(
3132
ImageSource imageSource,
32-
std::shared_ptr<const ImageTelemetry> telemetry);
33+
std::shared_ptr<const ImageTelemetry> telemetry,
34+
SharedFunction<> cancelationFunction);
3335

3436
/*
3537
* The move constructor.
@@ -41,11 +43,6 @@ class ImageRequest final {
4143
*/
4244
ImageRequest(const ImageRequest &other) = delete;
4345

44-
/**
45-
* Set cancelation function.
46-
*/
47-
void setCancelationFunction(std::function<void(void)> cancelationFunction);
48-
4946
/*
5047
* Calls cancel function if one is defined. Should be when downloading
5148
* image isn't needed anymore. E.g. <ImageView /> was removed.
@@ -94,9 +91,9 @@ class ImageRequest final {
9491
std::shared_ptr<const ImageResponseObserverCoordinator> coordinator_{};
9592

9693
/*
97-
* Function we can call to cancel image request (see destructor).
94+
* Function we can call to cancel image request.
9895
*/
99-
std::function<void(void)> cancelRequest_;
96+
SharedFunction<> cancelRequest_;
10097
};
10198

10299
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ImageRequest ImageManager::requestImage(
2424
const ImageSource &imageSource,
2525
SurfaceId /*surfaceId*/) const {
2626
// Not implemented.
27-
return {imageSource, nullptr};
27+
return {imageSource, nullptr, {}};
2828
}
2929

3030
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageRequest.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ namespace facebook::react {
1313

1414
ImageRequest::ImageRequest(
1515
ImageSource imageSource,
16-
std::shared_ptr<const ImageTelemetry> telemetry)
17-
: imageSource_(std::move(imageSource)), telemetry_(std::move(telemetry)) {
16+
std::shared_ptr<const ImageTelemetry> telemetry,
17+
SharedFunction<> cancelationFunction)
18+
: imageSource_(std::move(imageSource)),
19+
telemetry_(std::move(telemetry)),
20+
cancelRequest_(std::move(cancelationFunction)) {
1821
// Not implemented.
1922
}
2023

packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/ImageRequest.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,16 @@ namespace facebook::react {
1111

1212
ImageRequest::ImageRequest(
1313
ImageSource imageSource,
14-
std::shared_ptr<const ImageTelemetry> telemetry)
15-
: imageSource_(std::move(imageSource)), telemetry_(std::move(telemetry)) {
14+
std::shared_ptr<const ImageTelemetry> telemetry,
15+
SharedFunction<> cancelationFunction)
16+
: imageSource_(std::move(imageSource)),
17+
telemetry_(std::move(telemetry)),
18+
cancelRequest_(std::move(cancelationFunction)) {
1619
coordinator_ = std::make_shared<ImageResponseObserverCoordinator>();
1720
}
1821

19-
void ImageRequest::setCancelationFunction(
20-
std::function<void(void)> cancelationFunction) {
21-
cancelRequest_ = cancelationFunction;
22-
}
23-
2422
void ImageRequest::cancel() const {
25-
if (cancelRequest_) {
26-
cancelRequest_();
27-
}
23+
cancelRequest_();
2824
}
2925

3026
const ImageSource &ImageRequest::getImageSource() const {

packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTImageManager.mm

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,11 @@ - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfa
4848
telemetry = nullptr;
4949
}
5050

51-
auto imageRequest = ImageRequest(imageSource, telemetry);
51+
auto sharedCancelationFunction = SharedFunction<>();
52+
auto imageRequest = ImageRequest(imageSource, telemetry, sharedCancelationFunction);
5253
auto weakObserverCoordinator =
5354
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
5455

55-
auto sharedCancelationFunction = SharedFunction<>();
56-
imageRequest.setCancelationFunction(sharedCancelationFunction);
57-
5856
/*
5957
* Even if an image is being loaded asynchronously on some other background thread, some other preparation
6058
* work (such as creating an `NSURLRequest` object and some obscure logic inside `RCTImageLoader`) can take a couple

packages/react-native/ReactCommon/react/renderer/imagemanager/platform/ios/react/renderer/imagemanager/RCTSyncImageManager.mm

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,11 @@ - (instancetype)initWithImageLoader:(id<RCTImageLoaderWithAttributionProtocol>)i
3737
- (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId
3838
{
3939
auto telemetry = std::make_shared<ImageTelemetry>(surfaceId);
40-
auto imageRequest = ImageRequest(imageSource, telemetry);
40+
auto sharedCancelationFunction = SharedFunction<>();
41+
auto imageRequest = ImageRequest(imageSource, telemetry, sharedCancelationFunction);
4142
auto weakObserverCoordinator =
4243
(std::weak_ptr<const ImageResponseObserverCoordinator>)imageRequest.getSharedObserverCoordinator();
4344

44-
auto sharedCancelationFunction = SharedFunction<>();
45-
imageRequest.setCancelationFunction(sharedCancelationFunction);
46-
4745
dispatch_group_t imageWaitGroup = dispatch_group_create();
4846

4947
dispatch_group_enter(imageWaitGroup);

packages/react-native/ReactCommon/react/utils/SharedFunction.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ namespace facebook::react {
2121
* - When captured by `std::function` arguments are not copyable;
2222
* - When we need to replace the content of the callable later on the go.
2323
*/
24-
template <typename ReturnT = void, typename... ArgumentT>
24+
template <typename... ArgumentT>
2525
class SharedFunction {
26-
using T = ReturnT(ArgumentT...);
26+
using T = void(ArgumentT...);
2727

2828
struct Pair {
2929
Pair(std::function<T> &&function) : function(std::move(function)) {}
@@ -46,9 +46,11 @@ class SharedFunction {
4646
pair_->function = function;
4747
}
4848

49-
ReturnT operator()(ArgumentT... args) const {
49+
void operator()(ArgumentT... args) const {
5050
std::shared_lock lock(pair_->mutex);
51-
return pair_->function(args...);
51+
if (pair_->function) {
52+
pair_->function(args...);
53+
}
5254
}
5355

5456
private:

0 commit comments

Comments
 (0)