[ios, osx] Tightened offline task state
Fixed a race condition that could occur if a progress update comes after a call to remove the task but before the removal is complete. Avoid synchronously setting the task’s state inside -resume and -suspend; instead, rely on the observer to set the state asynchronously.
This commit is contained in:
committed by
Minh Nguyễn
parent
77d99584ce
commit
7fa3b92429
@@ -104,8 +104,9 @@
|
||||
}
|
||||
|
||||
- (void)removeTask:(MGLOfflineTask *)task withCompletionHandler:(MGLOfflineTaskRemovalCompletionHandler)completion {
|
||||
mbgl::OfflineRegion *mbglOfflineRegion = task.mbglOfflineRegion;
|
||||
[task invalidate];
|
||||
self.mbglFileSource->deleteOfflineRegion(std::move(*task.mbglOfflineRegion), [&, task, completion](std::exception_ptr exception) {
|
||||
self.mbglFileSource->deleteOfflineRegion(std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) {
|
||||
NSError *error;
|
||||
if (exception) {
|
||||
error = [NSError errorWithDomain:MGLErrorDomain code:-1 userInfo:@{
|
||||
@@ -113,7 +114,7 @@
|
||||
}];
|
||||
}
|
||||
if (completion) {
|
||||
dispatch_async(dispatch_get_main_queue(), [&, task, completion, error](void) {
|
||||
dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
|
||||
completion(error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
|
||||
/**
|
||||
Assert that the current offline task is valid.
|
||||
|
||||
This macro should be used at the beginning of any public-facing instance method
|
||||
of `MGLOfflineTask`. For private methods, an assertion is more appropriate.
|
||||
*/
|
||||
#define MGLAssertOfflineTaskIsValid() \
|
||||
do { \
|
||||
if (_state == MGLOfflineTaskStateInvalid) { \
|
||||
@@ -32,7 +38,7 @@ private:
|
||||
|
||||
@interface MGLOfflineTask ()
|
||||
|
||||
@property (nonatomic, readwrite) mbgl::OfflineRegion *mbglOfflineRegion;
|
||||
@property (nonatomic, nullable, readwrite) mbgl::OfflineRegion *mbglOfflineRegion;
|
||||
@property (nonatomic, readwrite) MGLOfflineTaskState state;
|
||||
@property (nonatomic, readwrite) MGLOfflineTaskProgress progress;
|
||||
|
||||
@@ -54,8 +60,7 @@ private:
|
||||
_state = MGLOfflineTaskStateUnknown;
|
||||
|
||||
mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource];
|
||||
MBGLOfflineRegionObserver *mbglObserver = new MBGLOfflineRegionObserver(self);
|
||||
mbglFileSource->setOfflineRegionObserver(*_mbglOfflineRegion, std::make_unique<MBGLOfflineRegionObserver>(*mbglObserver));
|
||||
mbglFileSource->setOfflineRegionObserver(*_mbglOfflineRegion, std::make_unique<MBGLOfflineRegionObserver>(self));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -85,7 +90,6 @@ private:
|
||||
|
||||
mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource];
|
||||
mbglFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Active);
|
||||
self.state = MGLOfflineTaskStateActive;
|
||||
}
|
||||
|
||||
- (void)suspend {
|
||||
@@ -93,13 +97,27 @@ private:
|
||||
|
||||
mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource];
|
||||
mbglFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Inactive);
|
||||
self.state = MGLOfflineTaskStateInactive;
|
||||
}
|
||||
|
||||
- (void)invalidate {
|
||||
MGLAssertOfflineTaskIsValid();
|
||||
NSAssert(_state != MGLOfflineTaskStateInvalid, @"Cannot invalidate an already invalid offline task.");
|
||||
|
||||
self.state = MGLOfflineTaskStateInvalid;
|
||||
self.mbglOfflineRegion = nil;
|
||||
}
|
||||
|
||||
- (void)setState:(MGLOfflineTaskState)state {
|
||||
if (!self.mbglOfflineRegion) {
|
||||
// A progress update has arrived after the call to
|
||||
// -[MGLOfflineStorage removeTask:withCompletionHandler:] but before the
|
||||
// removal is complete and the completion handler is called.
|
||||
NSAssert(_state == MGLOfflineTaskStateInvalid, @"A valid MGLOfflineTask has no mbgl::OfflineRegion.");
|
||||
return;
|
||||
}
|
||||
|
||||
NSAssert(_state != MGLOfflineTaskStateInvalid, @"Cannot change the state of an invalid offline task.");
|
||||
|
||||
_state = state;
|
||||
}
|
||||
|
||||
- (void)requestProgress {
|
||||
@@ -120,7 +138,7 @@ private:
|
||||
}
|
||||
|
||||
- (void)offlineRegionStatusDidChange:(mbgl::OfflineRegionStatus)status {
|
||||
MGLAssertOfflineTaskIsValid();
|
||||
NSAssert(_state != MGLOfflineTaskStateInvalid, @"Cannot change update progress of an invalid offline task.");
|
||||
|
||||
switch (status.downloadState) {
|
||||
case mbgl::OfflineRegionDownloadState::Inactive:
|
||||
|
||||
@@ -2,12 +2,20 @@
|
||||
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MGLOfflineTask (Private)
|
||||
|
||||
@property (nonatomic) mbgl::OfflineRegion *mbglOfflineRegion;
|
||||
@property (nonatomic, nullable) mbgl::OfflineRegion *mbglOfflineRegion;
|
||||
|
||||
- (instancetype)initWithMBGLRegion:(mbgl::OfflineRegion *)region;
|
||||
|
||||
/**
|
||||
Invalidates the task and ensures that no future progress update can ever
|
||||
revalidate it.
|
||||
*/
|
||||
- (void)invalidate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -33,8 +33,12 @@
|
||||
}
|
||||
|
||||
if (!styleURL.scheme) {
|
||||
// Assume a relative path into the application bundle.
|
||||
styleURL = [NSURL URLWithString:[@"asset://" stringByAppendingString:styleURL.absoluteString]];
|
||||
[NSException raise:@"Invalid style URL" format:
|
||||
@"%@ does not support setting a relative file URL as the style URL. "
|
||||
@"To download the online resources required by this style, "
|
||||
@"specify a URL to an online copy of this style. "
|
||||
@"For Mapbox-hosted styles, use the mapbox: scheme.",
|
||||
NSStringFromClass([self class])];
|
||||
}
|
||||
|
||||
_styleURL = styleURL;
|
||||
|
||||
Reference in New Issue
Block a user