Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
Merge pull request #76 from mapbox/tile-cancels
Browse files Browse the repository at this point in the history
fixes #67: cancel unnecessary tile fetches
  • Loading branch information
incanus committed Feb 28, 2014
2 parents 675b8eb + 0782cfc commit a31fab1
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 60 deletions.
6 changes: 4 additions & 2 deletions include/llmr/map/tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <llmr/util/noncopyable.hpp>
#include <llmr/geometry/debug_font_buffer.hpp>
#include <llmr/geometry/vao.hpp>
#include <llmr/platform/platform.hpp>

#include <cstdint>
#include <forward_list>
Expand Down Expand Up @@ -43,7 +44,8 @@ class Tile : public std::enable_shared_from_this<Tile>,
enum state {
initial,
loading,
ready,
loaded,
parsed,
obsolete
};

Expand Down Expand Up @@ -92,8 +94,8 @@ class Tile : public std::enable_shared_from_this<Tile>,
private:
// Source data
std::string data;

const Style& style;
platform::Request req;
};

}
Expand Down
14 changes: 10 additions & 4 deletions include/llmr/platform/platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@

namespace llmr {

class Tile;

namespace platform {

// Restarts painting. This could for example trigger the event loop of the controlling application.
void restart();

struct Request {
int16_t identifier = -1;
std::string original_url;
};

struct Response {
int16_t code = -1;
std::string body;
};

// Makes an HTTP request of a URL on a background thread, calls a function with the results on the same thread, and finally calls a callback function on the main thread.
void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback);
// Makes an HTTP request of a URL on a background thread, calls a function with the results on the same thread, and finally calls a callback function on the main thread. Returns a cancellable request.
Request request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback);

// Cancels an HTTP request.
void cancel_request_http(Request request);

// Returns a relative timestamp in seconds. This value must be monotonic.
double time();
Expand Down
53 changes: 31 additions & 22 deletions ios/MBXViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

#include <llmr/llmr.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/map/tile.hpp>

NSString *const MBXNeedsRenderNotification = @"MBXNeedsRenderNotification";
NSString *const MBXUpdateActivityNotification = @"MBXUpdateActivityNotification";

@interface MBXViewController () <UIGestureRecognizerDelegate>

@property (nonatomic) EAGLContext *context;
@property (nonatomic) NSUInteger activityCount;
@property (nonatomic) CGPoint center;
@property (nonatomic) CGFloat scale;
@property (nonatomic) CGFloat angle;
Expand Down Expand Up @@ -153,11 +153,10 @@ - (void)updateRender:(NSNotification *)notification

- (void)updateNetworkActivity:(NSNotification *)notification
{
NSInteger input = [[notification userInfo][@"count"] integerValue];

self.activityCount += input;

[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:(self.activityCount > 0)];
[[NSURLSession sharedSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:([downloadTasks count] > 0)];
}];
}

- (void)updateRender
Expand Down Expand Up @@ -391,7 +390,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni

MBXMapView *mapView;
CADisplayLink *displayLink;
NSOperationQueue *queue;

namespace llmr
{
Expand All @@ -402,22 +400,15 @@ void restart()
[[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
}

void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback)
Request request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback)
{
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@1 forKey:@"count"]];

if (!queue)
queue = [NSOperationQueue new];

NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@(url.c_str())]];

[NSURLConnection sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@(url.c_str())] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil];

Response res;

if (error == nil)
if ( ! error && [response isKindOfClass:[NSHTTPURLResponse class]])
{
res.code = [(NSHTTPURLResponse *)response statusCode];
res.body = { (const char *)[data bytes], [data length] };
Expand All @@ -427,11 +418,29 @@ void request_http(std::string url, std::function<void(Response&)> background_fun

dispatch_async(dispatch_get_main_queue(), ^(void)
{
foreground_callback();
foreground_callback();
});
}];

[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@(-1) forKey:@"count"]];
[[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
[task resume];

[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil];

Request req;

req.identifier = task.taskIdentifier;
req.original_url = url;

return req;
}

void cancel_request_http(Request request)
{
[[NSURLSession sharedSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
{
for (NSURLSessionDownloadTask *task in downloadTasks)
if (task.taskIdentifier == request.identifier)
return [task cancel];
}];
}

Expand Down
2 changes: 1 addition & 1 deletion ios/llmr-app.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
'INFOPLIST_FILE': 'Info.plist',
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD':'c++11',
'IPHONEOS_DEPLOYMENT_TARGET':'6.0',
'IPHONEOS_DEPLOYMENT_TARGET':'7.0',
'TARGETED_DEVICE_FAMILY': '1,2',
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
'CLANG_ENABLE_OBJC_ARC': 'YES'
Expand Down
4 changes: 2 additions & 2 deletions llmr.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
'xcode_settings': {
'SDKROOT': 'macosx',
'SUPPORTED_PLATFORMS':['macosx'],
'MACOSX_DEPLOYMENT_TARGET':'10.8',
'MACOSX_DEPLOYMENT_TARGET':'10.9',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
'OTHER_CPLUSPLUSFLAGS':[
'<@(png_cflags)'
Expand Down Expand Up @@ -140,7 +140,7 @@
'ARCHS': [ "armv7", "armv7s", "arm64", "i386" ],
'TARGETED_DEVICE_FAMILY': '1,2',
'CODE_SIGN_IDENTITY': 'iPhone Developer',
'IPHONEOS_DEPLOYMENT_TARGET': '5.0',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
'GCC_INPUT_FILETYPE':'sourcecode.cpp.cpp',
'OTHER_CPLUSPLUSFLAGS':[
Expand Down
2 changes: 1 addition & 1 deletion macosx/llmr-app.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
'INFOPLIST_FILE': 'Info.plist',
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD':'c++11',
'MACOSX_DEPLOYMENT_TARGET':'10.8',
'MACOSX_DEPLOYMENT_TARGET':'10.9',
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
'CLANG_ENABLE_OBJC_ARC': 'YES'
},
Expand Down
47 changes: 30 additions & 17 deletions macosx/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#include <llmr/platform/platform.hpp>
#include <llmr/map/tile.hpp>
#include "settings.hpp"

#include <cstdio>
Expand Down Expand Up @@ -239,33 +240,45 @@ void restart() {
[[NSApplication sharedApplication] postEvent: [NSEvent eventWithCGEvent:event] atStart:NO];
}

void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback) {
if (!queue) {
queue = [NSOperationQueue new];
}

NSMutableURLRequest *urlRequest = [NSMutableURLRequest
requestWithURL:[NSURL
URLWithString:@(url.c_str())]];

[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler: ^(NSURLResponse * response, NSData * data, NSError * error) {
Request request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback)
{
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@(url.c_str())] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
Response res;
if (error == nil) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
res.code = [httpResponse statusCode];

if ( ! error && [response isKindOfClass:[NSHTTPURLResponse class]])
{
res.code = [(NSHTTPURLResponse *)response statusCode];
res.body = { (const char *)[data bytes], [data length] };
}

background_function(res);
dispatch_async(dispatch_get_main_queue(), ^(void) {

dispatch_async(dispatch_get_main_queue(), ^(void)
{
foreground_callback();
});
}];

[task resume];

Request req;

req.identifier = task.taskIdentifier;
req.original_url = url;

return req;
}

void cancel_request_http(Request request)
{
[[NSURLSession sharedSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
{
for (NSURLSessionDownloadTask *task in downloadTasks)
if (task.taskIdentifier == request.identifier)
return [task cancel];
}];
}

double time() {
return glfwGetTime();
Expand Down
8 changes: 4 additions & 4 deletions src/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ bool Map::findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::f
auto ids = Tile::children(id, z + 1);
for (const Tile::ID& child_id : ids) {
const Tile::Ptr& tile = hasTile(child_id);
if (tile && tile->state == Tile::ready) {
if (tile && tile->state == Tile::parsed) {
assert(tile);
retain.emplace_front(tile->id);
} else {
Expand All @@ -305,7 +305,7 @@ bool Map::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::for
for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
const Tile::ID parent_id = Tile::parent(id, z);
const Tile::Ptr tile = hasTile(parent_id);
if (tile && tile->state == Tile::ready) {
if (tile && tile->state == Tile::parsed) {
assert(tile);
retain.emplace_front(tile->id);
return true;
Expand Down Expand Up @@ -361,7 +361,7 @@ bool Map::updateTiles() {
Tile::Ptr tile = addTile(id);
assert(tile);

if (tile->state != Tile::ready) {
if (tile->state != Tile::parsed) {
// The tile we require is not yet loaded. Try to find a parent or
// child tile that we already have.

Expand Down Expand Up @@ -416,7 +416,7 @@ bool Map::render() {

for (Tile::Ptr& tile : tiles) {
assert(tile);
if (tile->state == Tile::ready) {
if (tile->state == Tile::parsed) {
painter.changeMatrix(tile->id);
painter.render(tile);
}
Expand Down
14 changes: 8 additions & 6 deletions src/map/tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/renderer/line_bucket.hpp>
#include <llmr/renderer/point_bucket.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/util/pbf.hpp>
#include <llmr/util/string.hpp>

Expand Down Expand Up @@ -75,22 +74,25 @@ void Tile::request() {

// Note: Somehow this feels slower than the change to request_http()
std::shared_ptr<Tile> tile = shared_from_this();
platform::request_http(url, [=](platform::Response& res) {
if (res.code == 200) {
platform::Request request = platform::request_http(url, [=](platform::Response& res) {
if (res.code == 200 && tile->state != obsolete) {
tile->state = Tile::loaded;
tile->data.swap(res.body);
tile->parse();
} else {
} else if (tile->state != obsolete) {
fprintf(stderr, "tile loading failed\n");
}
}, []() {
platform::restart();
});
req = request;
}

void Tile::cancel() {
// TODO: thread safety
if (state != obsolete) {
state = obsolete;
platform::cancel_request_http(req);
} else {
assert((!"logic error? multiple cancelleations"));
}
Expand All @@ -99,7 +101,7 @@ void Tile::cancel() {
bool Tile::parse() {
// std::lock_guard<std::mutex> lock(mtx);

if (state == obsolete) {
if (state != loaded) {
return false;
}

Expand All @@ -115,7 +117,7 @@ bool Tile::parse() {
if (state == obsolete) {
return false;
} else {
state = ready;
state = parsed;
}

return true;
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/painter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void Painter::clear() {
}

void Painter::render(const Tile::Ptr& tile) {
if (tile->state != Tile::ready) {
if (tile->state != Tile::parsed) {
return;
}

Expand Down

0 comments on commit a31fab1

Please sign in to comment.