From b905548a3b43b2130c35360c3602eda722f82e7f Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 25 Jan 2019 08:38:18 -0800 Subject: [PATCH] Fabric: Replace ImageLoader promise implementation with observer model Summary: Folly promises/futures have been replaced by an observer model which keeps track of loading state. This resolves at least one crash that I can no longer repro and simplifies the code a bit (IMO). Reviewed By: shergin Differential Revision: D13743393 fbshipit-source-id: 2b650841525db98b2f67add85f2097f24259c6cf --- .../Image/RCTImageComponentView.h | 10 +- .../Image/RCTImageComponentView.mm | 86 +++++++++++++-- React/Fabric/RCTScheduler.mm | 4 +- .../components/image/ImageEventEmitter.cpp | 8 +- .../components/image/ImageEventEmitter.h | 2 +- .../fabric/imagemanager/ImageRequest.h | 46 ++++---- .../fabric/imagemanager/ImageResponse.h | 10 +- .../imagemanager/ImageResponseObserver.h | 27 +++++ .../ImageResponseObserverCoordinator.cpp | 103 ++++++++++++++++++ .../ImageResponseObserverCoordinator.h | 91 ++++++++++++++++ .../platform/android/ImageManager.cpp | 2 +- .../platform/android/ImageRequest.cpp | 14 +-- .../platform/ios/ImageRequest.cpp | 35 +++--- .../platform/ios/RCTImageManager.mm | 45 ++++---- 14 files changed, 392 insertions(+), 91 deletions(-) create mode 100644 ReactCommon/fabric/imagemanager/ImageResponseObserver.h create mode 100644 ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.cpp create mode 100644 ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.h diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h index ebeea523d2..026bc5a95a 100644 --- a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h @@ -11,10 +11,18 @@ NS_ASSUME_NONNULL_BEGIN +@protocol RCTImageResponseDelegate + +- (void)didReceiveImage:(UIImage *)image fromObserver:(void*)observer; +- (void)didReceiveProgress:(float)progress fromObserver:(void*)observer; +- (void)didReceiveFailureFromObserver:(void*)observer; + +@end + /** * UIView class for root component. */ -@interface RCTImageComponentView : RCTViewComponentView +@interface RCTImageComponentView : RCTViewComponentView @end diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm index 538ba8ed2f..6073b3f925 100644 --- a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm @@ -13,6 +13,7 @@ #import #import #import +#import #import #import "RCTConversions.h" @@ -20,9 +21,40 @@ using namespace facebook::react; +class ImageResponseObserverProxy: public ImageResponseObserver { +public: + ImageResponseObserverProxy(void* delegate): delegate_((__bridge id)delegate) {} + + void didReceiveImage(const ImageResponse &imageResponse) override { + UIImage *image = (__bridge UIImage *)imageResponse.getImage().get(); + void *this_ = this; + dispatch_async(dispatch_get_main_queue(), ^{ + [delegate_ didReceiveImage:image fromObserver:this_]; + }); + } + + void didReceiveProgress (float p) override { + void *this_ = this; + dispatch_async(dispatch_get_main_queue(), ^{ + [delegate_ didReceiveProgress:p fromObserver:this_]; + }); + } + void didReceiveFailure() override { + void *this_ = this; + dispatch_async(dispatch_get_main_queue(), ^{ + [delegate_ didReceiveFailureFromObserver:this_]; + }); + } + +private: + id delegate_; +}; + @implementation RCTImageComponentView { UIImageView *_imageView; SharedImageLocalData _imageLocalData; + std::shared_ptr _coordinator; + std::unique_ptr _imageResponseObserverProxy; } - (instancetype)initWithFrame:(CGRect)frame @@ -35,6 +67,8 @@ using namespace facebook::react; _imageView.clipsToBounds = YES; _imageView.contentMode = (UIViewContentMode)RCTResizeModeFromImageResizeMode(defaultProps->resizeMode); + + _imageResponseObserverProxy = std::make_unique((__bridge void *)self); self.contentView = _imageView; } @@ -78,23 +112,42 @@ using namespace facebook::react; { _imageLocalData = std::static_pointer_cast(localData); assert(_imageLocalData); - auto future = _imageLocalData->getImageRequest().getResponseFuture(); - future.via(&MainQueueExecutor::instance()).thenValue([self](ImageResponse &&imageResponse) { - self.image = (__bridge UIImage *)imageResponse.getImage().get(); - }); + self.coordinator = _imageLocalData->getImageRequest().getObserverCoordinator(); + + // Loading actually starts a little before this + std::static_pointer_cast(_eventEmitter)->onLoadStart(); +} + +- (void)setCoordinator:(std::shared_ptr)coordinator { + if (_coordinator) { + _coordinator->removeObserver(_imageResponseObserverProxy.get()); + } + _coordinator = coordinator; + if (_coordinator != nullptr) { + _coordinator->addObserver(_imageResponseObserverProxy.get()); + } } - (void)prepareForRecycle { [super prepareForRecycle]; + self.coordinator = nullptr; _imageView.image = nil; _imageLocalData.reset(); } -#pragma mark - Other - -- (void)setImage:(UIImage *)image +-(void)dealloc { + self.coordinator = nullptr; + _imageResponseObserverProxy.reset(); +} + +#pragma mark - RCTImageResponseDelegate + +- (void)didReceiveImage:(UIImage *)image fromObserver:(void*)observer +{ + std::static_pointer_cast(_eventEmitter)->onLoad(); + const auto &imageProps = *std::static_pointer_cast(_props); if (imageProps.tintColor) { @@ -110,11 +163,22 @@ using namespace facebook::react; resizingMode:UIImageResizingModeStretch]; } - _imageView.image = image; - + self->_imageView.image = image; + // Apply trilinear filtering to smooth out mis-sized images. - _imageView.layer.minificationFilter = kCAFilterTrilinear; - _imageView.layer.magnificationFilter = kCAFilterTrilinear; + self->_imageView.layer.minificationFilter = kCAFilterTrilinear; + self->_imageView.layer.magnificationFilter = kCAFilterTrilinear; + + std::static_pointer_cast(self->_eventEmitter)->onLoadEnd(); } +- (void)didReceiveProgress:(float)progress fromObserver:(void*)observer { + std::static_pointer_cast(_eventEmitter)->onProgress(progress); +} + +- (void)didReceiveFailureFromObserver:(void*)observer { + std::static_pointer_cast(_eventEmitter)->onError(); +} + + @end diff --git a/React/Fabric/RCTScheduler.mm b/React/Fabric/RCTScheduler.mm index 579256c01a..5e41dbf902 100644 --- a/React/Fabric/RCTScheduler.mm +++ b/React/Fabric/RCTScheduler.mm @@ -47,11 +47,11 @@ private: std::shared_ptr _delegateProxy; } -- (instancetype)initWithContextContainer:(std::shared_ptr)contextContatiner +- (instancetype)initWithContextContainer:(std::shared_ptr)contextContainer { if (self = [super init]) { _delegateProxy = std::make_shared((__bridge void *)self); - _scheduler = std::make_shared(std::static_pointer_cast(contextContatiner), getDefaultComponentRegistryFactory()); + _scheduler = std::make_shared(std::static_pointer_cast(contextContainer), getDefaultComponentRegistryFactory()); _scheduler->setDelegate(_delegateProxy.get()); } diff --git a/ReactCommon/fabric/components/image/ImageEventEmitter.cpp b/ReactCommon/fabric/components/image/ImageEventEmitter.cpp index bc83aa4d9b..1d70b79c13 100644 --- a/ReactCommon/fabric/components/image/ImageEventEmitter.cpp +++ b/ReactCommon/fabric/components/image/ImageEventEmitter.cpp @@ -22,8 +22,12 @@ void ImageEventEmitter::onLoadEnd() const { dispatchEvent("loadEnd"); } -void ImageEventEmitter::onProgress() const { - dispatchEvent("progress"); +void ImageEventEmitter::onProgress(double progress) const { + dispatchEvent("progress", [=](jsi::Runtime &runtime) { + auto payload = jsi::Object(runtime); + payload.setProperty(runtime, "progress", progress); + return payload; + }); } void ImageEventEmitter::onError() const { diff --git a/ReactCommon/fabric/components/image/ImageEventEmitter.h b/ReactCommon/fabric/components/image/ImageEventEmitter.h index a853caf753..2c19024a59 100644 --- a/ReactCommon/fabric/components/image/ImageEventEmitter.h +++ b/ReactCommon/fabric/components/image/ImageEventEmitter.h @@ -18,7 +18,7 @@ class ImageEventEmitter : public ViewEventEmitter { void onLoadStart() const; void onLoad() const; void onLoadEnd() const; - void onProgress() const; + void onProgress(double) const; void onError() const; void onPartialLoad() const; }; diff --git a/ReactCommon/fabric/imagemanager/ImageRequest.h b/ReactCommon/fabric/imagemanager/ImageRequest.h index ebe0a93e7b..87e4c248f5 100644 --- a/ReactCommon/fabric/imagemanager/ImageRequest.h +++ b/ReactCommon/fabric/imagemanager/ImageRequest.h @@ -7,11 +7,9 @@ #pragma once -#include - -#include -#include #include +#include +#include #include namespace facebook { @@ -22,7 +20,6 @@ namespace react { * The separate object must be constructed for every single separate * image request. The object cannot be copied because it would make managing of * event listeners hard and inefficient; the object can be moved though. - * To subscribe for notifications use `getResponseFuture()` method. * Destroy to cancel the underlying request. */ class ImageRequest final { @@ -33,15 +30,10 @@ class ImageRequest final { */ class ImageNoLongerNeededException; - ImageRequest(); - /* - * `ImageRequest` is constructed with `ImageSource` and - * `ImageResponse` future which must be moved in inside the object. + * The default constructor */ - ImageRequest( - const ImageSource &imageSource, - folly::Future &&responseFuture); + ImageRequest(const ImageSource &imageSource); /* * The move constructor. @@ -51,32 +43,36 @@ class ImageRequest final { /* * `ImageRequest` does not support copying by design. */ - ImageRequest(const ImageRequest &) = delete; + ImageRequest(const ImageRequest &other) = delete; ~ImageRequest(); - /* - * Creates and returns a *new* future object with promised `ImageResponse` - * result. Multiple consumers can call this method many times to create - * their own subscriptions to promised value. + /** + * Set cancelation function. */ - folly::Future getResponseFuture() const; + void setCancelationFunction(std::function cancelationFunction); + + /* + * Get observer coordinator. + */ + std::shared_ptr + getObserverCoordinator() const; private: - /* - * Mutext to protect an access to the future. - */ - mutable std::mutex mutex_; - /* * Image source assosiated with the request. */ ImageSource imageSource_; /* - * Future splitter powers factory-like `getResponseFuture()` method. + * Event coordinator associated with the reqest. */ - mutable folly::FutureSplitter responseFutureSplitter_; + std::shared_ptr coordinator_{}; + + /* + * Function we can call to cancel image request (see destructor). + */ + std::function cancelRequest_; /* * Indicates that the object was moved and hence cannot be used anymore. diff --git a/ReactCommon/fabric/imagemanager/ImageResponse.h b/ReactCommon/fabric/imagemanager/ImageResponse.h index 91f8722773..3aef630db1 100644 --- a/ReactCommon/fabric/imagemanager/ImageResponse.h +++ b/ReactCommon/fabric/imagemanager/ImageResponse.h @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +#pragma once + #include namespace facebook { @@ -15,12 +17,18 @@ namespace react { */ class ImageResponse final { public: + enum class Status { + Loading, + Completed, + Failed, + }; + ImageResponse(const std::shared_ptr &image); std::shared_ptr getImage() const; private: - std::shared_ptr image_; + std::shared_ptr image_{}; }; } // namespace react diff --git a/ReactCommon/fabric/imagemanager/ImageResponseObserver.h b/ReactCommon/fabric/imagemanager/ImageResponseObserver.h new file mode 100644 index 0000000000..55fe4f64f5 --- /dev/null +++ b/ReactCommon/fabric/imagemanager/ImageResponseObserver.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace react { + +/* + * Represents any observer of ImageResponse progression, completion, or failure. + */ +class ImageResponseObserver { + public: + virtual void didReceiveProgress(float) = 0; + virtual void didReceiveImage(const ImageResponse &imageResponse) = 0; + virtual void didReceiveFailure() = 0; + virtual ~ImageResponseObserver() noexcept = default; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.cpp b/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.cpp new file mode 100644 index 0000000000..57b72966e3 --- /dev/null +++ b/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.cpp @@ -0,0 +1,103 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ImageResponseObserverCoordinator.h" + +#include + +namespace facebook { +namespace react { + +ImageResponseObserverCoordinator::ImageResponseObserverCoordinator() { + status_ = ImageResponse::Status::Loading; +} + +ImageResponseObserverCoordinator::~ImageResponseObserverCoordinator() {} + +void ImageResponseObserverCoordinator::addObserver( + ImageResponseObserver *observer) const { + ImageResponse::Status status = [this] { + std::shared_lock read(mutex_); + return status_; + }(); + + if (status == ImageResponse::Status::Loading) { + std::unique_lock write(mutex_); + observers_.push_back(observer); + } else if (status == ImageResponse::Status::Completed) { + ImageResponse imageResponseCopy = [this] { + std::unique_lock read(mutex_); + return ImageResponse(imageData_); + }(); + observer->didReceiveImage(imageResponseCopy); + } else { + observer->didReceiveFailure(); + } +} + +void ImageResponseObserverCoordinator::removeObserver( + ImageResponseObserver *observer) const { + std::unique_lock write(mutex_); + + auto position = std::find(observers_.begin(), observers_.end(), observer); + if (position != observers_.end()) { + observers_.erase(position, observers_.end()); + } +} + +void ImageResponseObserverCoordinator::nativeImageResponseProgress( + float progress) const { + std::vector observersCopy = [this] { + std::shared_lock read(mutex_); + return observers_; + }(); + + for (auto observer : observersCopy) { + observer->didReceiveProgress(progress); + } +} + +void ImageResponseObserverCoordinator::nativeImageResponseComplete( + const ImageResponse &imageResponse) const { + { + std::unique_lock write(mutex_); + imageData_ = imageResponse.getImage(); + status_ = ImageResponse::Status::Completed; + } + + std::vector observersCopy = [this] { + std::shared_lock read(mutex_); + return observers_; + }(); + + for (auto observer : observersCopy) { + ImageResponse imageResponseCopy = [this] { + std::unique_lock read(mutex_); + return ImageResponse(imageData_); + }(); + observer->didReceiveImage(imageResponseCopy); + } +} + +void ImageResponseObserverCoordinator::nativeImageResponseFailed() const { + { + std::unique_lock write(mutex_); + status_ = ImageResponse::Status::Failed; + } + + std::vector observersCopy = [this] { + std::shared_lock read(mutex_); + return observers_; + }(); + + for (auto observer : observersCopy) { + observer->didReceiveFailure(); + } +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.h b/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.h new file mode 100644 index 0000000000..b5b22e402a --- /dev/null +++ b/ReactCommon/fabric/imagemanager/ImageResponseObserverCoordinator.h @@ -0,0 +1,91 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace facebook { +namespace react { + +/* + * The ImageResponseObserverCoordinator receives events and completed image + * data from native image loaders and sends events to any observers attached + * to the coordinator. The Coordinator also keeps track of response status + * and caches completed images. + */ +class ImageResponseObserverCoordinator { + public: + /* + * Default constructor. + */ + ImageResponseObserverCoordinator(); + + /* + * Default destructor. + */ + ~ImageResponseObserverCoordinator(); + + /* + * Interested parties may observe the image response. + */ + void addObserver(ImageResponseObserver *observer) const; + + /* + * Interested parties may stop observing the image response. + */ + void removeObserver(ImageResponseObserver *observer) const; + + /* + * Platform-specific image loader will call this method with progress updates. + */ + void nativeImageResponseProgress(float) const; + + /* + * Platform-specific image loader will call this method with a completed image + * response. + */ + void nativeImageResponseComplete(const ImageResponse &imageResponse) const; + + /* + * Platform-specific image loader will call this method in case of any + * failures. + */ + void nativeImageResponseFailed() const; + + private: + /* + * List of observers. + * Mutable: protected by mutex_. + */ + mutable std::vector observers_; + + /* + * Current status of image loading. + * Mutable: protected by mutex_. + */ + mutable ImageResponse::Status status_; + + /* + * Cache image data. + * Mutable: protected by mutex_. + */ + mutable std::shared_ptr imageData_{}; + + /* + * Observer and data mutex. + */ + mutable folly::SharedMutex mutex_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/platform/android/ImageManager.cpp b/ReactCommon/fabric/imagemanager/platform/android/ImageManager.cpp index b0f8f410fa..fce052dccc 100644 --- a/ReactCommon/fabric/imagemanager/platform/android/ImageManager.cpp +++ b/ReactCommon/fabric/imagemanager/platform/android/ImageManager.cpp @@ -22,7 +22,7 @@ ImageManager::~ImageManager() { ImageRequest ImageManager::requestImage(const ImageSource &imageSource) const { // Not implemented. - return {}; + return ImageRequest(imageSource); } } // namespace react diff --git a/ReactCommon/fabric/imagemanager/platform/android/ImageRequest.cpp b/ReactCommon/fabric/imagemanager/platform/android/ImageRequest.cpp index 35b2ecfd86..e4ceb7d377 100644 --- a/ReactCommon/fabric/imagemanager/platform/android/ImageRequest.cpp +++ b/ReactCommon/fabric/imagemanager/platform/android/ImageRequest.cpp @@ -10,17 +10,14 @@ namespace facebook { namespace react { -ImageRequest::ImageRequest() {} - -ImageRequest::ImageRequest( - const ImageSource &imageSource, - folly::Future &&responseFuture) { +ImageRequest::ImageRequest(const ImageSource &imageSource) + : imageSource_(imageSource) { // Not implemented. } ImageRequest::ImageRequest(ImageRequest &&other) noexcept : imageSource_(std::move(other.imageSource_)), - responseFutureSplitter_(std::move(other.responseFutureSplitter_)) { + coordinator_(std::move(other.coordinator_)) { // Not implemented. } @@ -28,8 +25,9 @@ ImageRequest::~ImageRequest() { // Not implemented. } -folly::Future ImageRequest::getResponseFuture() const { - // Not implemented. +std::shared_ptr +ImageRequest::getObserverCoordinator() const { + // Not implemented abort(); } diff --git a/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp b/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp index afa6cb3bd9..1b9f4eb1ac 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp +++ b/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp @@ -16,34 +16,33 @@ class ImageRequest::ImageNoLongerNeededException : public std::logic_error { : std::logic_error("Image no longer needed.") {} }; -ImageRequest::ImageRequest( - const ImageSource &imageSource, - folly::Future &&responseFuture) - : imageSource_(imageSource), - responseFutureSplitter_(folly::splitFuture(std::move(responseFuture))) {} +ImageRequest::ImageRequest(const ImageSource &imageSource) + : imageSource_(imageSource) { + coordinator_ = std::make_shared(); +} ImageRequest::ImageRequest(ImageRequest &&other) noexcept : imageSource_(std::move(other.imageSource_)), - responseFutureSplitter_(std::move(other.responseFutureSplitter_)) { + coordinator_(std::move(other.coordinator_)) { other.moved_ = true; -}; + other.coordinator_ = nullptr; + other.cancelRequest_ = nullptr; +} ImageRequest::~ImageRequest() { - if (!moved_) { - auto future = responseFutureSplitter_.getFuture(); - if (!future.isReady()) { - future.raise(ImageNoLongerNeededException()); - } + if (cancelRequest_) { + cancelRequest_(); } } -folly::Future ImageRequest::getResponseFuture() const { - if (moved_) { - abort(); - } +void ImageRequest::setCancelationFunction( + std::function cancelationFunction) { + cancelRequest_ = cancelationFunction; +} - std::lock_guard lock(mutex_); - return responseFutureSplitter_.getFuture(); +std::shared_ptr +ImageRequest::getObserverCoordinator() const { + return coordinator_; } } // namespace react diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm index b3e97541b9..94e3a26a43 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm @@ -7,10 +7,9 @@ #import "RCTImageManager.h" -#import -#import - #import +#import +#import #import "RCTImagePrimitivesConversions.h" @@ -29,23 +28,28 @@ using namespace facebook::react; } - (ImageRequest)requestImage:(const ImageSource &)imageSource { - __block auto promise = folly::Promise(); + auto imageRequest = ImageRequest(imageSource); + + auto observerCoordinator = imageRequest.getObserverCoordinator(); NSURLRequest *request = NSURLRequestFromImageSource(imageSource); auto completionBlock = ^(NSError *error, UIImage *image) { - auto imageResponse = ImageResponse( - std::shared_ptr((__bridge_retained void *)image, CFRelease)); - promise.setValue(std::move(imageResponse)); - }; - - auto interruptBlock = ^(const folly::exception_wrapper &exceptionWrapper) { - if (!promise.isFulfilled()) { - promise.setException(exceptionWrapper); + if (image && !error) { + auto imageResponse = ImageResponse( + std::shared_ptr((__bridge_retained void *)image, CFRelease)); + observerCoordinator->nativeImageResponseComplete( + std::move(imageResponse)); + } else { + observerCoordinator->nativeImageResponseFailed(); } }; - RCTImageLoaderCancellationBlock cancellationBlock = + auto progressBlock = ^(int64_t progress, int64_t total) { + observerCoordinator->nativeImageResponseProgress(progress / (float)total); + }; + + RCTImageLoaderCancellationBlock cancelationBlock = [_imageLoader loadImageWithURLRequest:request size:CGSizeMake( imageSource.size.width, @@ -53,18 +57,17 @@ using namespace facebook::react; scale:imageSource.scale clipped:YES resizeMode:RCTResizeModeStretch - progressBlock:nil + progressBlock:progressBlock partialLoadBlock:nil completionBlock:completionBlock]; - promise.setInterruptHandler( - [cancellationBlock, - interruptBlock](const folly::exception_wrapper &exceptionWrapper) { - cancellationBlock(); - interruptBlock(exceptionWrapper); - }); + std::function cancelationFunction = [cancelationBlock](void) { + cancelationBlock(); + }; - return ImageRequest(imageSource, promise.getFuture()); + imageRequest.setCancelationFunction(cancelationFunction); + + return imageRequest; } @end