Merge branch 'master' into libuv-0.10-headless-display

Conflicts:
	common/curl_request.cpp
	common/glfw_view.cpp
	common/glfw_view.hpp
	include/mbgl/platform/request.hpp
	ios/mapbox-gl-cocoa
	setup-libraries.sh
	src/map/map.cpp
	src/platform/request.cpp
	test/fixtures/fixture_request.cpp
This commit is contained in:
Mike Morris
2014-10-10 12:24:45 -04:00
115 changed files with 5035 additions and 866 deletions

View File

@@ -108,4 +108,4 @@ distclean: clean
-rm -rf ./mapnik-packaging/osx/out/universal
-find ./mapnik-packaging/osx/out/packages -type d ! -name 'packages' -maxdepth 1 -exec rm -rf {} \;
.PHONY: mbgl test linux
.PHONY: mbgl test linux build/test/Makefile

112
common/Reachability.h Normal file
View File

@@ -0,0 +1,112 @@
/*
Copyright (c) 2011, Tony Million.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
/**
* Does ARC support GCD objects?
* It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
*
* @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h
**/
#if OS_OBJECT_USE_OBJC
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
#else
#define NEEDS_DISPATCH_RETAIN_RELEASE 1
#endif
/**
* Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
*
* @see http://nshipster.com/ns_enum-ns_options/
**/
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif
extern NSString *const kReachabilityChangedNotification;
typedef NS_ENUM(NSInteger, NetworkStatus) {
// Apple NetworkStatus Compatible Names.
NotReachable = 0,
ReachableViaWiFi = 2,
ReachableViaWWAN = 1
};
@class Reachability;
typedef void (^NetworkReachable)(Reachability * reachability);
typedef void (^NetworkUnreachable)(Reachability * reachability);
@interface Reachability : NSObject
@property (nonatomic, copy) NetworkReachable reachableBlock;
@property (nonatomic, copy) NetworkUnreachable unreachableBlock;
@property (nonatomic, assign) BOOL reachableOnWWAN;
+(Reachability*)reachabilityWithHostname:(NSString*)hostname;
// This is identical to the function above, but is here to maintain
//compatibility with Apples original code. (see .m)
+(Reachability*)reachabilityWithHostName:(NSString*)hostname;
+(Reachability*)reachabilityForInternetConnection;
+(Reachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
+(Reachability*)reachabilityForLocalWiFi;
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
-(BOOL)startNotifier;
-(void)stopNotifier;
-(BOOL)isReachable;
-(BOOL)isReachableViaWWAN;
-(BOOL)isReachableViaWiFi;
// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.
-(BOOL)isConnectionRequired; // Identical DDG variant.
-(BOOL)connectionRequired; // Apple's routine.
// Dynamic, on demand connection?
-(BOOL)isConnectionOnDemand;
// Is user intervention required?
-(BOOL)isInterventionRequired;
-(NetworkStatus)currentReachabilityStatus;
-(SCNetworkReachabilityFlags)reachabilityFlags;
-(NSString*)currentReachabilityString;
-(NSString*)currentReachabilityFlags;
@end

527
common/Reachability.m Normal file
View File

@@ -0,0 +1,527 @@
/*
Copyright (c) 2011, Tony Million.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#import "Reachability.h"
NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
@interface Reachability ()
@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
#if NEEDS_DISPATCH_RETAIN_RELEASE
@property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue;
#else
@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
#endif
@property (nonatomic, strong) id reachabilityObject;
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
@end
static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
{
return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
#if TARGET_OS_IPHONE
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
#else
'X',
#endif
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
}
// Start listening for reachability notifications on the current run loop
static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target)
#if __has_feature(objc_arc)
Reachability *reachability = ((__bridge Reachability*)info);
#else
Reachability *reachability = ((Reachability*)info);
#endif
// We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
// but what the heck eh?
@autoreleasepool
{
[reachability reachabilityChanged:flags];
}
}
@implementation Reachability
@synthesize reachabilityRef;
@synthesize reachabilitySerialQueue;
@synthesize reachableOnWWAN;
@synthesize reachableBlock;
@synthesize unreachableBlock;
@synthesize reachabilityObject;
#pragma mark - Class Constructor Methods
+(Reachability*)reachabilityWithHostName:(NSString*)hostname
{
return [Reachability reachabilityWithHostname:hostname];
}
+(Reachability*)reachabilityWithHostname:(NSString*)hostname
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
#if __has_feature(objc_arc)
return reachability;
#else
return [reachability autorelease];
#endif
}
return nil;
}
+(Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
{
SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
if (ref)
{
id reachability = [[self alloc] initWithReachabilityRef:ref];
#if __has_feature(objc_arc)
return reachability;
#else
return [reachability autorelease];
#endif
}
return nil;
}
+(Reachability *)reachabilityForInternetConnection
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self reachabilityWithAddress:&zeroAddress];
}
+(Reachability*)reachabilityForLocalWiFi
{
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
return [self reachabilityWithAddress:&localWifiAddress];
}
// Initialization methods
-(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
{
self = [super init];
if (self != nil)
{
self.reachableOnWWAN = YES;
self.reachabilityRef = ref;
}
return self;
}
-(void)dealloc
{
[self stopNotifier];
if(self.reachabilityRef)
{
CFRelease(self.reachabilityRef);
self.reachabilityRef = nil;
}
self.reachableBlock = nil;
self.unreachableBlock = nil;
#if !(__has_feature(objc_arc))
[super dealloc];
#endif
}
#pragma mark - Notifier Methods
// Notifier
// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
-(BOOL)startNotifier
{
SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
// this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
// woah
self.reachabilityObject = self;
// First, we need to create a serial queue.
// We allocate this once for the lifetime of the notifier.
self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
if(self.reachabilitySerialQueue == nil)
{
return NO;
}
#if __has_feature(objc_arc)
context.info = (__bridge void *)self;
#else
context.info = (void *)self;
#endif
if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
{
#ifdef DEBUG
NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
#endif
// Clear out the dispatch queue
if(self.reachabilitySerialQueue)
{
#if NEEDS_DISPATCH_RETAIN_RELEASE
dispatch_release(self.reachabilitySerialQueue);
#endif
self.reachabilitySerialQueue = nil;
}
self.reachabilityObject = nil;
return NO;
}
// Set it as our reachability queue, which will retain the queue
if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
{
#ifdef DEBUG
NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
#endif
// UH OH - FAILURE!
// First stop, any callbacks!
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
// Then clear out the dispatch queue.
if(self.reachabilitySerialQueue)
{
#if NEEDS_DISPATCH_RETAIN_RELEASE
dispatch_release(self.reachabilitySerialQueue);
#endif
self.reachabilitySerialQueue = nil;
}
self.reachabilityObject = nil;
return NO;
}
return YES;
}
-(void)stopNotifier
{
// First stop, any callbacks!
SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
// Unregister target from the GCD serial dispatch queue.
SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
if(self.reachabilitySerialQueue)
{
#if NEEDS_DISPATCH_RETAIN_RELEASE
dispatch_release(self.reachabilitySerialQueue);
#endif
self.reachabilitySerialQueue = nil;
}
self.reachabilityObject = nil;
}
#pragma mark - reachability tests
// This is for the case where you flick the airplane mode;
// you end up getting something like this:
//Reachability: WR ct-----
//Reachability: -- -------
//Reachability: WR ct-----
//Reachability: -- -------
// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
{
BOOL connectionUP = YES;
if(!(flags & kSCNetworkReachabilityFlagsReachable))
connectionUP = NO;
if( (flags & testcase) == testcase )
connectionUP = NO;
#if TARGET_OS_IPHONE
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
{
// We're on 3G.
if(!self.reachableOnWWAN)
{
// We don't want to connect when on 3G.
connectionUP = NO;
}
}
#endif
return connectionUP;
}
-(BOOL)isReachable
{
SCNetworkReachabilityFlags flags;
if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
return NO;
return [self isReachableWithFlags:flags];
}
-(BOOL)isReachableViaWWAN
{
#if TARGET_OS_IPHONE
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
// Check we're REACHABLE
if(flags & kSCNetworkReachabilityFlagsReachable)
{
// Now, check we're on WWAN
if(flags & kSCNetworkReachabilityFlagsIsWWAN)
{
return YES;
}
}
}
#endif
return NO;
}
-(BOOL)isReachableViaWiFi
{
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
// Check we're reachable
if((flags & kSCNetworkReachabilityFlagsReachable))
{
#if TARGET_OS_IPHONE
// Check we're NOT on WWAN
if((flags & kSCNetworkReachabilityFlagsIsWWAN))
{
return NO;
}
#endif
return YES;
}
}
return NO;
}
// WWAN may be available, but not active until a connection has been established.
// WiFi may require a connection for VPN on Demand.
-(BOOL)isConnectionRequired
{
return [self connectionRequired];
}
-(BOOL)connectionRequired
{
SCNetworkReachabilityFlags flags;
if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
// Dynamic, on demand connection?
-(BOOL)isConnectionOnDemand
{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
(flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
}
return NO;
}
// Is user intervention required?
-(BOOL)isInterventionRequired
{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
(flags & kSCNetworkReachabilityFlagsInterventionRequired));
}
return NO;
}
#pragma mark - reachability status stuff
-(NetworkStatus)currentReachabilityStatus
{
if([self isReachable])
{
if([self isReachableViaWiFi])
return ReachableViaWiFi;
#if TARGET_OS_IPHONE
return ReachableViaWWAN;
#endif
}
return NotReachable;
}
-(SCNetworkReachabilityFlags)reachabilityFlags
{
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return flags;
}
return 0;
}
-(NSString*)currentReachabilityString
{
NetworkStatus temp = [self currentReachabilityStatus];
if(temp == reachableOnWWAN)
{
// Updated for the fact that we have CDMA phones now!
return NSLocalizedString(@"Cellular", @"");
}
if (temp == ReachableViaWiFi)
{
return NSLocalizedString(@"WiFi", @"");
}
return NSLocalizedString(@"No Connection", @"");
}
-(NSString*)currentReachabilityFlags
{
return reachabilityFlags([self reachabilityFlags]);
}
#pragma mark - Callback function calls this method
-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
{
if([self isReachableWithFlags:flags])
{
if(self.reachableBlock)
{
self.reachableBlock(self);
}
}
else
{
if(self.unreachableBlock)
{
self.unreachableBlock(self);
}
}
// this makes sure the change notification happens on the MAIN THREAD
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
object:self];
});
}
#pragma mark - Debug Description
- (NSString *) description
{
NSString *description = [NSString stringWithFormat:@"<%@: %#x>",
NSStringFromClass([self class]), (unsigned int) self];
return description;
}
@end

View File

@@ -1 +0,0 @@
#import <Foundation/Foundation.h>

View File

@@ -1,115 +0,0 @@
#import "foundation_request.h"
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#include <atomic>
#endif
#include <memory>
#include <string>
#include <functional>
#include <mbgl/platform/request.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/uv.hpp>
dispatch_once_t request_initialize = 0;
NSURLSession *session = nullptr;
#if TARGET_OS_IPHONE
std::atomic<int> active_tasks;
#endif
// We're using a child class to make sure ARC is working correctly, as well as to add activity
// indicators on iOS.
class FoundationRequest : public mbgl::platform::Request {
public:
FoundationRequest(const std::string &url,
std::function<void(mbgl::platform::Response *)> callback,
std::shared_ptr<uv::loop> loop)
: Request(url, callback, loop) {
#if TARGET_OS_IPHONE
active_tasks++;
dispatch_async(dispatch_get_main_queue(), ^(void) {
[[UIApplication sharedApplication]
setNetworkActivityIndicatorVisible:(active_tasks > 0)];
});
#endif
}
~FoundationRequest() {
#if TARGET_OS_IPHONE
active_tasks--;
dispatch_async(dispatch_get_main_queue(), ^(void) {
[[UIApplication sharedApplication]
setNetworkActivityIndicatorVisible:(active_tasks > 0)];
});
#endif
}
NSURLSessionDataTask *task = nullptr;
};
std::shared_ptr<mbgl::platform::Request>
mbgl::platform::request_http(const std::string &url,
std::function<void(Response *)> callback,
std::shared_ptr<uv::loop> loop) {
dispatch_once(&request_initialize, ^{
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForResource = 30;
sessionConfig.HTTPMaximumConnectionsPerHost = 8;
sessionConfig.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
session = [NSURLSession sessionWithConfiguration:sessionConfig];
#if TARGET_OS_IPHONE
active_tasks = 0;
#endif
});
std::shared_ptr<FoundationRequest> req =
std::make_shared<FoundationRequest>(url, callback, loop);
// Note that we are creating a new shared_ptr pointer(!) to make sure there is at least one
// shared_ptr in existence while the NSURLSession is loading our data. We are making sure in the
// callback that this pointer gets destroyed again.
std::shared_ptr<Request> *req_ptr = new std::shared_ptr<Request>(req);
NSURLSessionDataTask *task = [session
dataTaskWithURL:[NSURL URLWithString:@(url.c_str())]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([error code] == NSURLErrorCancelled) {
// We intentionally cancelled this request. Make sure we clear the shared_ptr to resolve
// the circular reference. We're referencing this shared_ptr by value so that the object
// stays around until this completion handler is invoked.
delete req_ptr;
return;
}
if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
(*req_ptr)->res->code = [(NSHTTPURLResponse *)response statusCode];
(*req_ptr)->res->body = {(const char *)[data bytes], [data length]};
} else {
(*req_ptr)->res->error_message = [[error localizedDescription] UTF8String];
}
(*req_ptr)->complete();
delete req_ptr;
}];
req->task = task;
[task resume];
return req;
}
void mbgl::platform::cancel_request_http(const std::shared_ptr<Request> &req) {
if (req) {
[((FoundationRequest *)(req.get()))->task cancel];
}
}

View File

@@ -186,7 +186,9 @@ int GLFWView::run() {
glfwWaitEvents();
}
map->stop();
map->stop([](void *) {
glfwWaitEvents();
});
return 0;
}
@@ -199,6 +201,10 @@ void GLFWView::make_inactive() {
glfwMakeContextCurrent(nullptr);
}
void GLFWView::notify() {
glfwPostEmptyEvent();
}
void GLFWView::swap() {
glfwPostEmptyEvent();

View File

@@ -18,6 +18,7 @@ public:
void swap();
void make_active();
void make_inactive();
void notify();
void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0);
static void key(GLFWwindow *window, int key, int scancode, int action, int mods);

View File

@@ -161,6 +161,10 @@ HeadlessView::~HeadlessView() {
#endif
}
void HeadlessView::notify() {
// no-op
}
void HeadlessView::notify_map_change(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) {
// no-op
}

View File

@@ -28,6 +28,7 @@ public:
void resize(uint16_t width, uint16_t height, float pixelRatio);
const std::unique_ptr<uint32_t[]> readPixels();
void notify();
void notify_map_change(MapChange change, timestamp delay = 0);
void make_active();
void make_inactive();

View File

@@ -0,0 +1,149 @@
#include <mbgl/storage/http_request_baton.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/parsedate.h>
#include <uv.h>
#include <mbgl/util/uv.hpp>
#import <Foundation/Foundation.h>
#include <ctime>
#include <xlocale.h>
namespace mbgl {
dispatch_once_t request_initialize = 0;
NSURLSession *session = nullptr;
void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &ptr) {
assert(uv_thread_self() == ptr->thread_id);
// Starts the request.
util::ptr<HTTPRequestBaton> baton = ptr;
// Create a C locale
static locale_t locale = newlocale(LC_ALL_MASK, nullptr, nullptr);
dispatch_once(&request_initialize, ^{
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForResource = 30;
sessionConfig.HTTPMaximumConnectionsPerHost = 8;
sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
sessionConfig.URLCache = nil;
session = [NSURLSession sessionWithConfiguration:sessionConfig];
});
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@(baton->path.c_str())]];
if (baton->response) {
if (!baton->response->etag.empty()) {
[request addValue:@(baton->response->etag.c_str()) forHTTPHeaderField:@"If-None-Match"];
} else if (baton->response->modified) {
const time_t modified = baton->response->modified;
struct tm *timeinfo = std::gmtime(&modified);
char buffer[32];
strftime_l(buffer, 32, "%a, %d %b %Y %H:%M:%S GMT", timeinfo, locale);
[request addValue:@(buffer) forHTTPHeaderField:@"If-Modified-Since"];
}
}
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *res, NSError *error) {
if (error) {
if ([error code] == NSURLErrorCancelled) {
// The response code remains at 0 to indicate cancelation.
// In addition, we don't need any response object.
baton->response.reset();
baton->type = HTTPResponseType::Canceled;
} else {
// TODO: Use different codes for host not found, timeout, invalid URL etc.
// These can be categorized in temporary and permanent errors.
baton->response = std::make_unique<Response>();
baton->response->code = [(NSHTTPURLResponse *)res statusCode];
baton->response->message = [[error localizedDescription] UTF8String];
switch ([error code]) {
case NSURLErrorBadServerResponse: // 5xx errors
baton->type = HTTPResponseType::TemporaryError;
break;
case NSURLErrorTimedOut:
case NSURLErrorUserCancelledAuthentication:
baton->type = HTTPResponseType::SingularError; // retry immediately
break;
case NSURLErrorNetworkConnectionLost:
case NSURLErrorCannotFindHost:
case NSURLErrorCannotConnectToHost:
case NSURLErrorDNSLookupFailed:
case NSURLErrorNotConnectedToInternet:
case NSURLErrorInternationalRoamingOff:
case NSURLErrorCallIsActive:
case NSURLErrorDataNotAllowed:
baton->type = HTTPResponseType::ConnectionError;
break;
default:
baton->type = HTTPResponseType::PermanentError;
}
}
} else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
const long code = [(NSHTTPURLResponse *)res statusCode];
if (code == 304) {
// Assume a Response object already exists.
assert(baton->response);
} else {
baton->response = std::make_unique<Response>();
baton->response->code = code;
baton->response->data = {(const char *)[data bytes], [data length]};
}
if (code == 304) {
baton->type = HTTPResponseType::NotModified;
} else if (code == 200) {
baton->type = HTTPResponseType::Successful;
} else {
baton->type = HTTPResponseType::PermanentError;
}
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
if (cache_control) {
baton->response->expires = Response::parseCacheControl([cache_control UTF8String]);
}
NSString *last_modified = [headers objectForKey:@"Last-Modified"];
if (last_modified) {
baton->response->modified = parse_date([last_modified UTF8String]);
}
NSString *etag = [headers objectForKey:@"ETag"];
if (etag) {
baton->response->etag = [etag UTF8String];
}
} else {
// This should never happen.
baton->type = HTTPResponseType::PermanentError;
baton->response = std::make_unique<Response>();
baton->response->code = -1;
baton->response->message = "response class is not NSHTTPURLResponse";
}
uv_async_send(baton->async);
}];
[task resume];
baton->ptr = const_cast<void *>(CFBridgingRetain(task));
}
void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &ptr) {
assert(uv_thread_self() == ptr->thread_id);
assert(ptr->ptr);
NSURLSessionDataTask *task = CFBridgingRelease(ptr->ptr);
ptr->ptr = nullptr;
[task cancel];
}
}

View File

@@ -0,0 +1,465 @@
#include <mbgl/storage/http_request_baton.hpp>
#include <mbgl/util/uv-messenger.h>
#include <mbgl/util/std.hpp>
#include <mbgl/util/ptr.hpp>
#include <uv.h>
#include <curl/curl.h>
#include <queue>
#include <cassert>
#include <cstring>
#include <ctime>
#include <xlocale.h>
// This file contains code from http://curl.haxx.se/libcurl/c/multi-uv.html:
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* Example application code using the multi socket interface to download
multiple files at once, but instead of using curl_multi_perform and
curl_multi_wait, which uses select(), we use libuv.
It supports epoll, kqueue, etc. on unixes and fast IO completion ports on
Windows, which means, it should be very fast on all platforms..
Written by Clemens Gruber, based on an outdated example from uvbook and
some tests from libuv.
Requires libuv and (of course) libcurl.
See http://nikhilm.github.com/uvbook/ for more information on libuv.
*/
// Handles the request thread + messaging to the thread.
static uv_once_t once;
static uv_loop_t loop;
static uv_messenger_t start_messenger;
static uv_messenger_t stop_messenger;
static uv_thread_t thread;
static unsigned long thread_id;
// Used as the CURL timer function to periodically check for socket updates.
static uv_timer_t timeout;
// CURL multi handle that we use to request multiple URLs at the same time, without having to block
// and spawn threads.
static CURLM *multi = nullptr;
// CURL share handles are used for sharing session state (e.g.)
static uv_mutex_t share_mutex;
static CURLSH *share = nullptr;
// A queue that we use for storing resuable CURL easy handles to avoid creating and destroying them
// all the time.
static std::queue<CURL *> handles;
namespace mbgl {
struct CURLContext {
util::ptr<HTTPRequestBaton> baton;
uv_poll_t *poll_handle = nullptr;
curl_socket_t sockfd = 0;
curl_slist *headers = nullptr;
CURLContext(const util::ptr<HTTPRequestBaton> &baton_) : baton(baton_) {
}
~CURLContext() {
// We are destructing the poll handle in a CURL callback already.
assert(!poll_handle);
// Once the CURLContext gets destroyed, CURL doesn't need any headers anymore.
if (headers) {
curl_slist_free_all(headers);
headers = nullptr;
}
}
};
// Locks the CURL share handle
void curl_share_lock(CURL *, curl_lock_data, curl_lock_access, void *) {
uv_mutex_lock(&share_mutex);
}
// Unlocks the CURL share handle
void curl_share_unlock(CURL *, curl_lock_data, void *) {
uv_mutex_unlock(&share_mutex);
}
// This function must run in the CURL thread.
// It is either called when the request is completed, or when we try to cancel the request.
void finish_request(const util::ptr<HTTPRequestBaton> &baton) {
assert(uv_thread_self() == thread_id);
if (baton->ptr) {
CURL *handle = (CURL *)baton->ptr;
CURLMcode error = curl_multi_remove_handle(multi, handle);
if (error != CURLM_OK) {
baton->response = std::make_unique<Response>();
baton->response->code = -1;
baton->response->message = curl_multi_strerror(error);
}
// Destroy the shared pointer. We still have one pointing to it
CURLContext *context = nullptr;
curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context);
curl_easy_setopt(handle, CURLOPT_PRIVATE, nullptr);
delete context;
// TODO: delete the headers object again.
curl_easy_reset(handle);
handles.push(handle);
baton->ptr = nullptr;
}
}
void curl_perform(uv_poll_t *req, int, int events) {
int running_handles;
int flags = 0;
CURLContext *context = (CURLContext *)req->data;
CURLMsg *message;
int pending;
uv_timer_stop(&timeout);
if (events & UV_READABLE) {
flags |= CURL_CSELECT_IN;
}
if (events & UV_WRITABLE) {
flags |= CURL_CSELECT_OUT;
}
curl_multi_socket_action(multi, context->sockfd, flags, &running_handles);
while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
CURLContext *context = nullptr;
curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&context);
// Make a copy so that the Baton stays around even after we are calling finish_request
util::ptr<HTTPRequestBaton> baton = context->baton;
// Add human-readable error code
if (message->data.result != CURLE_OK) {
baton->response->message = curl_easy_strerror(message->data.result);
baton->response->code = -1;
switch (message->data.result) {
case CURLE_COULDNT_RESOLVE_PROXY:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_COULDNT_CONNECT:
baton->type = HTTPResponseType::ConnectionError;
break;
case CURLE_OPERATION_TIMEDOUT:
baton->type = HTTPResponseType::TemporaryError;
break;
default:
baton->type = HTTPResponseType::PermanentError;
}
} else {
long code = 0;
curl_easy_getinfo(message->easy_handle, CURLINFO_RESPONSE_CODE, &code);
if (code != 304) {
baton->response->code = code;
}
if (code == 304) {
baton->type = HTTPResponseType::NotModified;
} else if (code == 200) {
baton->type = HTTPResponseType::Successful;
} else if (code >= 500 && code < 600) {
baton->type = HTTPResponseType::TemporaryError;
} else if (code >= 400 && code < 500) {
baton->type = HTTPResponseType::PermanentError;
} else {
assert(!"code must be either 200 or 304");
}
}
// We're currently in the CURL request thread.
finish_request(baton);
if (baton->async) {
uv_async_send(baton->async);
baton->async = nullptr;
}
break;
}
default:
// This should never happen, because there are no other message types.
throw std::runtime_error("CURLMSG returned unknown message type");
}
}
}
int handle_socket(CURL *handle, curl_socket_t sockfd, int action, void *, void *socketp) {
CURLContext *context = nullptr;
curl_easy_getinfo(handle, CURLINFO_PRIVATE, (char *)&context);
if (!socketp && action != CURL_POLL_REMOVE) {
// We haven't initialized the socket yet, so we need to do this now.
assert(context->sockfd == 0);
context->sockfd = sockfd;
assert(!context->poll_handle);
context->poll_handle = new uv_poll_t;
uv_poll_init_socket(&loop, context->poll_handle, sockfd);
context->poll_handle->data = context;
curl_multi_assign(multi, sockfd, context);
}
if (context) {
if (action == CURL_POLL_IN || action == CURL_POLL_INOUT) {
uv_poll_start(context->poll_handle, UV_READABLE, curl_perform);
}
if (action == CURL_POLL_OUT || action == CURL_POLL_INOUT) {
uv_poll_start(context->poll_handle, UV_WRITABLE, curl_perform);
}
if (action == CURL_POLL_REMOVE && socketp) {
uv_poll_stop(context->poll_handle);
uv_close((uv_handle_t *)context->poll_handle, [](uv_handle_t *handle) {
delete (uv_poll_t *)handle;
});
context->poll_handle = nullptr;
curl_multi_assign(multi, sockfd, NULL);
}
}
return 0;
}
void on_timeout(uv_timer_t *) {
int running_handles;
CURLMcode error = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (error != CURLM_OK) {
throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
}
}
void start_timeout(CURLM *, long timeout_ms, void *) {
if (timeout_ms <= 0) {
on_timeout(&timeout);
} else {
uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
}
}
void thread_init(void *) {
#ifdef __APPLE__
pthread_setname_np("CURL");
#endif
thread_id = uv_thread_self();
uv_timer_init(&loop, &timeout);
CURLSHcode share_error;
share = curl_share_init();
share_error = curl_share_setopt(share, CURLSHOPT_LOCKFUNC, curl_share_lock);
if (share_error != CURLSHE_OK) {
throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error));
}
share_error = curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, curl_share_unlock);
if (share_error != CURLSHE_OK) {
throw std::runtime_error(std::string("CURL share error: ") + curl_share_strerror(share_error));
}
CURLMcode multi_error;
multi = curl_multi_init();
multi_error = curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handle_socket);
if (multi_error != CURLM_OK) {
throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error));
}
multi_error = curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, start_timeout);
if (multi_error != CURLM_OK) {
throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(multi_error));
}
// Main event loop. This will not return until the request loop is terminated.
uv_run(&loop, UV_RUN_DEFAULT);
curl_multi_cleanup(multi);
multi = nullptr;
curl_share_cleanup(share);
share = nullptr;
thread_id = -1;
}
// This function is called when we have new data for a request. We just append it to the string
// containing the previous data.
size_t curl_write_cb(void *const contents, const size_t size, const size_t nmemb, void *const userp) {
((std::string *)userp)->append((char *)contents, size * nmemb);
return size * nmemb;
}
// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!)
// header string. If the data buffer contains the header string at the beginning, it returns
// the length of the header string == begin of the value, otherwise it returns npos.
// The comparison of the header is ASCII-case-insensitive.
size_t header_matches(const char *const header, const char *const buffer, const size_t length) {
const size_t header_length = strlen(header);
if (length < header_length) return std::string::npos;
size_t i = 0;
while (i < length && i < header_length && std::tolower(buffer[i]) == header[i]) {
i++;
}
return i == header_length ? i : std::string::npos;
}
size_t curl_header_cb(char * const buffer, const size_t size, const size_t nmemb, void *const userp) {
const size_t length = size * nmemb;
Response *response = static_cast<Response *>(userp);
size_t begin = std::string::npos;
if ((begin = header_matches("last-modified: ", buffer, length)) != std::string::npos) {
// Always overwrite the modification date; We might already have a value here from the
// Date header, but this one is more accurate.
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
response->modified = curl_getdate(value.c_str(), nullptr);
} else if ((begin = header_matches("etag: ", buffer, length)) != std::string::npos) {
response->etag = { buffer + begin, length - begin - 2 }; // remove \r\n
} else if ((begin = header_matches("cache-control: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
response->expires = Response::parseCacheControl(value.c_str());
}
return length;
}
// This function must run in the CURL thread.
void start_request(void *const ptr) {
assert(uv_thread_self() == thread_id);
std::unique_ptr<util::ptr<HTTPRequestBaton>> baton_guard { (util::ptr<HTTPRequestBaton> *)ptr };
util::ptr<HTTPRequestBaton> &baton = *baton_guard.get();
assert(baton);
// Create a C locale
static locale_t locale = newlocale(LC_ALL_MASK, nullptr, nullptr);
CURL *handle = nullptr;
if (!handles.empty()) {
handle = handles.front();
handles.pop();
} else {
handle = curl_easy_init();
}
baton->ptr = handle;
// Wrap this in a unique_ptr for now so that it destructs until we assign it the the CURL handle.
std::unique_ptr<CURLContext> context = std::make_unique<CURLContext>(baton);
if (baton->response) {
if (!baton->response->etag.empty()) {
const std::string header = std::string("If-None-Match: ") + baton->response->etag;
context->headers = curl_slist_append(context->headers, header.c_str());
} else if (baton->response->modified) {
const time_t modified = baton->response->modified;
struct tm *timeinfo = std::gmtime(&modified);
char buffer[64];
strftime_l(buffer, 64, "If-Modified-Since: %a, %d %b %Y %H:%M:%S GMT", timeinfo, locale);
context->headers = curl_slist_append(context->headers, buffer);
}
}
if (context->headers) {
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, context->headers);
}
if (!baton->response) {
baton->response = std::make_unique<Response>();
}
// Carry on the shared pointer in the private information of the CURL handle.
curl_easy_setopt(handle, CURLOPT_PRIVATE, context.release());
curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt");
curl_easy_setopt(handle, CURLOPT_URL, baton->path.c_str());
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &baton->response->data);
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
curl_easy_setopt(handle, CURLOPT_HEADERDATA, baton->response.get());
curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
curl_easy_setopt(handle, CURLOPT_SHARE, share);
// Start requesting the information.
curl_multi_add_handle(multi, handle);
}
// This function must run in the CURL thread.
void stop_request(void *const ptr) {
assert(uv_thread_self() == thread_id);
std::unique_ptr<util::ptr<HTTPRequestBaton>> baton_guard { (util::ptr<HTTPRequestBaton> *)ptr };
util::ptr<HTTPRequestBaton> &baton = *baton_guard.get();
assert(baton);
if (baton->async) {
baton->type = HTTPResponseType::Canceled;
// We can still stop the request because it is still in progress.
finish_request(baton);
uv_async_send(baton->async);
baton->async = nullptr;
} else {
// If the async handle is gone, it means that the actual request has been completed before
// we got a chance to cancel it. In this case, this is a no-op. It is likely that
// the pointer below is the last lifeline of the HTTPRequestBaton. This means we're going
// to delete the HTTPRequestBaton in the current (CURL) thread.
}
}
void create_thread() {
uv_mutex_init(&share_mutex);
uv_loop_init(&loop);
uv_messenger_init(&loop, &start_messenger, start_request);
uv_messenger_init(&loop, &stop_messenger, stop_request);
uv_thread_create(&thread, thread_init, nullptr);
}
// This function must be run from the main thread (== where the HTTPRequestBaton was created)
void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &ptr) {
assert(uv_thread_self() == ptr->thread_id);
uv_once(&once, create_thread);
uv_messenger_send(&start_messenger, new util::ptr<HTTPRequestBaton>(ptr));
}
// This function must be run from the main thread (== where the HTTPRequestBaton was created)
void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &ptr) {
assert(uv_thread_self() == ptr->thread_id);
uv_once(&once, create_thread);
uv_messenger_send(&stop_messenger, new util::ptr<HTTPRequestBaton>(ptr));
}
}

21
common/ios.mm Normal file
View File

@@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
#include <mbgl/platform/platform.hpp>
namespace mbgl {
namespace platform {
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
if ([paths count] == 0) {
// Disable the cache if we don't have a location to write.
return "";
}
NSString *libraryDirectory = [paths objectAtIndex:0];
return [[libraryDirectory stringByAppendingPathComponent:@"cache.db"] UTF8String];
}
}
}

12
common/linux.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include <mbgl/platform/platform.hpp>
namespace mbgl {
namespace platform {
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
return "/tmp/mbgl-cache.db";
}
}
}

31
common/osx.mm Normal file
View File

@@ -0,0 +1,31 @@
#import <Foundation/Foundation.h>
#include <mbgl/platform/platform.hpp>
namespace mbgl {
namespace platform {
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ([paths count] == 0) {
// Disable the cache if we don't have a location to write.
return "";
}
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Mapbox GL"];
if (![[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:nil]) {
// Disable the cache if we couldn't create the directory.
return "";
}
return [[path stringByAppendingPathComponent:@"cache.db"] UTF8String];
}
}
}

9
configure vendored
View File

@@ -107,6 +107,14 @@ def configure_curl(o):
o['variables']['curl_libraries'] = ret[0].split()
o['variables']['curl_cflags'] = ret[1].split()
def configure_sqlite3(o):
ret = pkg_config('sqlite3', options.pkgconfig_root)
if not ret:
sys.stderr.write('could not find sqlite3 with pkg-config')
sys.exit(-1)
o['variables']['sqlite3_libraries'] = ret[0].split()
o['variables']['sqlite3_cflags'] = ret[1].split()
def write(filename, data):
filename = os.path.join(root_dir, filename)
print "creating ", filename
@@ -129,6 +137,7 @@ if __name__ == '__main__':
configure_uv(output)
configure_png(output)
configure_curl(output)
configure_sqlite3(output)
pprint.pprint(output, indent=2)
write('config.gypi', "# Do not edit. Generated by the configure script.\n" +

View File

@@ -7,11 +7,11 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <atomic>
#include <iosfwd>
#include <memory>
#include <set>
#include <vector>
@@ -31,21 +31,31 @@ class FileSource;
class View;
class Map : private util::noncopyable {
typedef void (*stop_callback)(void *);
public:
explicit Map(View &view);
~Map();
// Start/stop the map render thread
// Start the map render thread. It is asynchronous.
void start();
void stop();
// Runs the map event loop.
// Stop the map render thread. This call will block until the map rendering thread stopped.
// The optional callback function will be invoked repeatedly until the map thread is stopped.
// The callback function should wait until it is woken up again by view.notify(), otherwise
// this will be a busy waiting loop. The optional data parameter will be passed to the callback
// function.
void stop(stop_callback cb = nullptr, void *data = nullptr);
// Runs the map event loop. ONLY run this function when you want to get render a single frame
// with this map object. It will *not* spawn a separate thread and instead block until the
// frame is completely rendered.
void run();
// Triggers a lazy rerender: only performs a render when the map is not clean.
void rerender();
void renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
void renderLayer(util::ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id = nullptr, const mat4* matrix = nullptr);
// Forces a map update: always triggers a rerender.
void update();
@@ -65,7 +75,7 @@ public:
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
// Styling
const std::set<std::shared_ptr<StyleSource>> getActiveSources() const;
const std::set<util::ptr<StyleSource>> getActiveSources() const;
void setAppliedClasses(const std::vector<std::string> &classes);
void toggleClass(const std::string &name);
const std::vector<std::string> &getAppliedClasses() const;
@@ -116,16 +126,20 @@ public:
void toggleDebug();
bool getDebug() const;
// Call this when the network reachability changed.
void setReachability(bool status);
public:
inline const TransformState &getState() const { return state; }
inline std::shared_ptr<FileSource> getFileSource() const { return fileSource; }
inline std::shared_ptr<Style> getStyle() const { return style; }
inline std::shared_ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; }
inline std::shared_ptr<GlyphStore> getGlyphStore() { return glyphStore; }
inline std::shared_ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
std::shared_ptr<Sprite> getSprite();
inline std::shared_ptr<Texturepool> getTexturepool() { return texturepool; }
inline std::shared_ptr<uv::loop> getLoop() { return loop; }
inline util::ptr<FileSource> getFileSource() const { return fileSource; }
inline util::ptr<Style> getStyle() const { return style; }
inline util::ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; }
inline util::ptr<GlyphStore> getGlyphStore() { return glyphStore; }
inline util::ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; }
util::ptr<Sprite> getSprite();
inline util::ptr<Texturepool> getTexturepool() { return texturepool; }
inline util::ptr<uv::loop> getLoop() { return loop; }
uv::worker &getWorker();
inline timestamp getAnimationTime() const { return animationTime; }
inline timestamp getTime() const { return animationTime; }
void updateTiles();
@@ -141,7 +155,7 @@ private:
void setup();
void updateSources();
void updateSources(const std::shared_ptr<StyleLayerGroup> &group);
void updateSources(const util::ptr<StyleLayerGroup> &group);
void updateRenderState();
@@ -154,15 +168,16 @@ private:
// Unconditionally performs a render with the current map state.
void render();
void renderLayers(std::shared_ptr<StyleLayerGroup> group);
void renderLayers(util::ptr<StyleLayerGroup> group);
private:
bool async = false;
std::shared_ptr<uv::loop> loop;
util::ptr<uv::loop> loop;
std::unique_ptr<uv::worker> workers;
std::unique_ptr<uv::thread> thread;
uv_async_t *async_terminate = nullptr;
uv_async_t *async_render = nullptr;
uv_async_t *async_cleanup = nullptr;
std::unique_ptr<uv_async_t> async_terminate;
std::unique_ptr<uv_async_t> async_render;
std::unique_ptr<uv_async_t> async_cleanup;
private:
// If cleared, the next time the render thread attempts to render the map, it will *actually*
@@ -177,24 +192,33 @@ private:
// ready for rendering.
std::atomic_flag is_rendered = ATOMIC_FLAG_INIT;
// Stores whether the map thread has been stopped already.
std::atomic_bool is_stopped;
public:
View &view;
private:
#ifndef NDEBUG
const unsigned long main_thread;
unsigned long map_thread = -1;
#endif
Transform transform;
TransformState state;
std::shared_ptr<FileSource> fileSource;
util::ptr<FileSource> fileSource;
std::shared_ptr<Style> style;
std::shared_ptr<GlyphAtlas> glyphAtlas;
std::shared_ptr<GlyphStore> glyphStore;
std::shared_ptr<SpriteAtlas> spriteAtlas;
std::shared_ptr<Sprite> sprite;
std::shared_ptr<Texturepool> texturepool;
util::ptr<Style> style;
util::ptr<GlyphAtlas> glyphAtlas;
util::ptr<GlyphStore> glyphStore;
util::ptr<SpriteAtlas> spriteAtlas;
util::ptr<Sprite> sprite;
util::ptr<Texturepool> texturepool;
Painter painter;
std::string styleURL;
std::string styleJSON = "";
std::string accessToken = "";
@@ -203,7 +227,7 @@ private:
int indent = 0;
std::set<std::shared_ptr<StyleSource>> activeSources;
std::set<util::ptr<StyleSource>> activeSources;
};

View File

@@ -5,8 +5,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <memory>
namespace mbgl {
class Map;
@@ -18,12 +16,12 @@ class RasterTileData : public TileData {
friend class TileParser;
public:
RasterTileData(Tile::ID id, Map &map, const SourceInfo &source);
RasterTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~RasterTileData();
virtual void parse();
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
virtual bool hasData(util::ptr<StyleLayer> layer_desc) const;
protected:
StyleBucketRaster properties;

View File

@@ -8,12 +8,12 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <forward_list>
#include <iosfwd>
#include <map>
#include <memory>
namespace mbgl {
@@ -25,7 +25,7 @@ struct box;
class Source : public std::enable_shared_from_this<Source>, private util::noncopyable {
public:
Source(SourceInfo& info);
Source(const util::ptr<SourceInfo>& info);
void load(Map &map);
@@ -33,8 +33,8 @@ public:
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
size_t getTileCount() const;
void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc);
void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
void render(Painter &painter, util::ptr<StyleLayer> layer_desc);
void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
void finishRender(Painter &painter);
std::forward_list<Tile::ID> getIDs() const;
@@ -54,7 +54,7 @@ private:
double getZoom(const TransformState &state) const;
SourceInfo& info;
util::ptr<SourceInfo> info;
bool loaded = false;
// Stores the time when this source was most recently updated.

View File

@@ -3,11 +3,11 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <atomic>
#include <iosfwd>
#include <memory>
#include <string>
#include <unordered_map>
#include <future>
@@ -34,11 +34,11 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
void load(const std::shared_ptr<FileSource> &fileSource);
void load(const util::ptr<FileSource> &fileSource);
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
static std::shared_ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource);
static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const util::ptr<FileSource> &fileSource);
const SpritePosition &getSpritePosition(const std::string& name) const;

View File

@@ -3,6 +3,7 @@
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <bitset>
@@ -10,7 +11,6 @@
#include <cstdint>
#include <forward_list>
#include <iosfwd>
#include <memory>
#include <string>
namespace mbgl {
@@ -78,7 +78,7 @@ public:
const Tile::ID id;
ClipID clip;
mat4 matrix;
std::shared_ptr<TileData> data;
util::ptr<TileData> data;
};
}

View File

@@ -6,11 +6,11 @@
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <atomic>
#include <exception>
#include <iosfwd>
#include <memory>
#include <string>
namespace mbgl {
@@ -19,8 +19,7 @@ class Map;
class Painter;
class SourceInfo;
class StyleLayer;
namespace platform { class Request; }
class Request;
class TileData : public std::enable_shared_from_this<TileData>,
private util::noncopyable {
@@ -29,7 +28,7 @@ public:
struct geometry_too_long_exception : exception {};
public:
typedef std::shared_ptr<TileData> Ptr;
typedef util::ptr<TileData> Ptr;
enum class State {
invalid,
@@ -41,7 +40,7 @@ public:
};
public:
TileData(Tile::ID id, Map &map, const SourceInfo &source);
TileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~TileData();
void request();
@@ -57,8 +56,8 @@ public:
virtual void beforeParse();
virtual void parse() = 0;
virtual void afterParse();
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0;
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const = 0;
virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0;
virtual bool hasData(util::ptr<StyleLayer> layer_desc) const = 0;
public:
@@ -69,10 +68,10 @@ protected:
Map &map;
public:
const SourceInfo &source;
util::ptr<SourceInfo> source;
protected:
std::weak_ptr<platform::Request> req;
std::unique_ptr<Request> req;
std::string data;
// Contains the tile ID string for painting debug information.

View File

@@ -4,10 +4,10 @@
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <iosfwd>
#include <memory>
#include <string>
namespace mbgl {
@@ -32,11 +32,11 @@ class Collision;
class TileParser {
public:
TileParser(const std::string &data, VectorTileData &tile,
const std::shared_ptr<const Style> &style,
const std::shared_ptr<GlyphAtlas> &glyphAtlas,
const std::shared_ptr<GlyphStore> &glyphStore,
const std::shared_ptr<SpriteAtlas> &spriteAtlas,
const std::shared_ptr<Sprite> &sprite);
const util::ptr<const Style> &style,
const util::ptr<GlyphAtlas> &glyphAtlas,
const util::ptr<GlyphStore> &glyphStore,
const util::ptr<SpriteAtlas> &spriteAtlas,
const util::ptr<Sprite> &sprite);
~TileParser();
public:
@@ -44,11 +44,11 @@ public:
private:
bool obsolete() const;
void parseStyleLayers(std::shared_ptr<StyleLayerGroup> group);
std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc);
void parseStyleLayers(util::ptr<StyleLayerGroup> group);
std::unique_ptr<Bucket> createBucket(util::ptr<StyleBucket> bucket_desc);
std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
std::unique_ptr<Bucket> createRasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster &raster);
std::unique_ptr<Bucket> createRasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster &raster);
std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line);
std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol);
@@ -59,12 +59,12 @@ private:
VectorTileData& tile;
// Cross-thread shared data.
std::shared_ptr<const Style> style;
std::shared_ptr<GlyphAtlas> glyphAtlas;
std::shared_ptr<GlyphStore> glyphStore;
std::shared_ptr<SpriteAtlas> spriteAtlas;
std::shared_ptr<Sprite> sprite;
std::shared_ptr<Texturepool> texturePool;
util::ptr<const Style> style;
util::ptr<GlyphAtlas> glyphAtlas;
util::ptr<GlyphStore> glyphStore;
util::ptr<SpriteAtlas> spriteAtlas;
util::ptr<Sprite> sprite;
util::ptr<Texturepool> texturePool;
std::unique_ptr<Collision> collision;
};

View File

@@ -98,10 +98,10 @@ private:
// cache values for spherical mercator math
double Bc, Cc;
std::forward_list<std::shared_ptr<util::transition>> transitions;
std::shared_ptr<util::transition> scale_timeout;
std::shared_ptr<util::transition> rotate_timeout;
std::shared_ptr<util::transition> pan_timeout;
std::forward_list<util::ptr<util::transition>> transitions;
util::ptr<util::transition> scale_timeout;
util::ptr<util::transition> rotate_timeout;
util::ptr<util::transition> pan_timeout;
};
}

View File

@@ -26,14 +26,14 @@ class VectorTileData : public TileData {
friend class TileParser;
public:
VectorTileData(Tile::ID id, Map &map, const SourceInfo &source);
VectorTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source);
~VectorTileData();
virtual void beforeParse();
virtual void parse();
virtual void afterParse();
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
virtual bool hasData(std::shared_ptr<StyleLayer> layer_desc) const;
virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
virtual bool hasData(util::ptr<StyleLayer> layer_desc) const;
protected:
// Holds the actual geometries in this tile.

View File

@@ -45,6 +45,8 @@ public:
return 0;
}
virtual void notify() = 0;
// Notifies a watcher of map x/y/scale/rotation changes.
// Must only be called from the same thread that caused the change.
// Must not be called from the render thread.

View File

@@ -29,6 +29,7 @@ enum class Event : uint8_t {
ParseStyle,
ParseTile,
Render,
Database,
HttpRequest,
Sprite,
};
@@ -40,6 +41,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
{ Event::ParseStyle, "ParseStyle" },
{ Event::ParseTile, "ParseTile" },
{ Event::Render, "Render" },
{ Event::Database, "Database" },
{ Event::HttpRequest, "HttpRequest" },
{ Event::Sprite, "Sprite" },
{ Event(-1), "Unknown" },

View File

@@ -4,7 +4,6 @@
#include <mbgl/util/uv.hpp>
#include <memory>
#include <functional>
#include <string>
namespace mbgl {
@@ -12,30 +11,14 @@ namespace platform {
class Request;
struct Response {
Response(std::function<void(Response *)> callback) : callback(callback) {}
int16_t code = -1;
std::string body;
std::string error_message;
std::function<void(Response *)> callback;
};
// Makes an HTTP request of a URL, preferrably on a background thread, and calls a function with the
// results in the original thread (which runs the libuv loop).
// If the loop pointer is NULL, the callback function will be called on an arbitrary thread.
// Returns a cancellable request.
std::shared_ptr<Request> request_http(const std::string &url,
std::function<void(Response *)> callback,
std::shared_ptr<uv::loop> loop = nullptr);
// Uppercase a string, potentially using platform-specific routines.
std::string uppercase(const std::string &string);
// Lowercase a string, potentially using platform-specific routines.
std::string lowercase(const std::string &string);
// Cancels an HTTP request.
void cancel_request_http(const std::shared_ptr<Request> &req);
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase();
// Shows an alpha image with the specified dimensions in a named window.
void show_debug_image(std::string name, const char *data, size_t width, size_t height);

View File

@@ -1,41 +0,0 @@
#ifndef MBGL_PLATFORM_REQUEST
#define MBGL_PLATFORM_REQUEST
#include <string>
#include <functional>
#include <memory>
#include <atomic>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/uv.hpp>
namespace mbgl {
namespace platform {
struct Response;
class Request : public std::enable_shared_from_this<Request>, private util::noncopyable {
public:
Request(const std::string &url,
std::function<void(Response *)> callback,
std::shared_ptr<uv::loop> loop);
~Request();
void complete();
private:
static void complete(uv_async_t *async, int status);
public:
const std::string url;
std::unique_ptr<Response> res;
std::atomic<bool> cancelled;
public:
uv_async_t *async = nullptr;
std::shared_ptr<uv::loop> loop;
};
}
}
#endif

View File

@@ -1,11 +1,11 @@
#ifndef MBGL_RENDERER_BUCKET
#define MBGL_RENDERER_BUCKET
#include <string>
#include <memory>
#include <mbgl/map/tile.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <string>
namespace mbgl {
class Painter;
@@ -13,7 +13,7 @@ class StyleLayer;
class Bucket : private util::noncopyable {
public:
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0;
virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0;
virtual bool hasData() const = 0;
virtual ~Bucket() {}

View File

@@ -6,7 +6,6 @@
#include <mbgl/geometry/vao.hpp>
#include <vector>
#include <memory>
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
@@ -20,7 +19,7 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void drawLines(PlainShader& shader);

View File

@@ -44,7 +44,7 @@ public:
const StyleBucketFill& properties);
~FillBucket();
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void addGeometry(pbf& data);

View File

@@ -1,13 +1,12 @@
#ifndef MBGL_RENDERER_LINEBUCKET
#define MBGL_RENDERER_LINEBUCKET
#include "bucket.hpp"
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/line_buffer.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <memory>
#include <vector>
namespace mbgl {
@@ -30,7 +29,7 @@ public:
PointElementsBuffer& pointElementsBuffer,
const StyleBucketLine& properties);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
void addGeometry(pbf& data);

View File

@@ -22,6 +22,7 @@
#include <mbgl/shader/gaussian_shader.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/util/ptr.hpp>
#include <map>
#include <unordered_map>
@@ -73,7 +74,7 @@ public:
void changeMatrix();
// Renders a particular layer from a tile.
void renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix);
void renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix);
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -83,11 +84,11 @@ public:
void renderDebugText(DebugBucket& bucket, const mat4 &matrix);
void renderDebugText(const std::vector<std::string> &strings);
void renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderSymbol(SymbolBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderBackground(std::shared_ptr<StyleLayer> layer_desc);
void renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderSymbol(SymbolBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
void renderBackground(util::ptr<StyleLayer> layer_desc);
float saturationFactor(float saturation);
float contrastFactor(float contrast);
@@ -97,7 +98,7 @@ public:
void renderPrerenderedTexture(RasterBucket &bucket, const mat4 &matrix, const RasterProperties& properties);
void createPrerendered(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id);
void createPrerendered(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id);
void resize();
@@ -111,7 +112,7 @@ public:
// Configures the painter strata that is used for early z-culling of fragments.
void setStrata(float strata);
void drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &sources);
void drawClippingMasks(const std::set<util::ptr<StyleSource>> &sources);
void drawClippingMask(const mat4& matrix, const ClipID& clip);
void resetFramebuffer();

View File

@@ -16,9 +16,9 @@ class VertexArrayObject;
class RasterBucket : public Bucket {
public:
RasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster& properties);
RasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster& properties);
virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix);
virtual bool hasData() const;
bool setImage(const std::string &data);

View File

@@ -56,7 +56,7 @@ class SymbolBucket : public Bucket {
public:
SymbolBucket(const StyleBucketSymbol &properties, Collision &collision);
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix);
virtual bool hasData() const;
virtual bool hasTextData() const;
virtual bool hasIconData() const;

View File

@@ -0,0 +1,62 @@
#ifndef MBGL_STORAGE_BASE_REQUEST
#define MBGL_STORAGE_BASE_REQUEST
#include <mbgl/storage/request_callback.hpp>
#include <mbgl/util/ptr.hpp>
#include <string>
#include <forward_list>
#include <functional>
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_async_s uv_async_t;
namespace mbgl {
class Response;
class Request;
class BaseRequest {
private:
// Make noncopyable and immovable
BaseRequest(const BaseRequest &) = delete;
BaseRequest(BaseRequest &&) = delete;
BaseRequest& operator=(const BaseRequest &) = delete;
BaseRequest& operator=(BaseRequest &&) = delete;
public:
BaseRequest(const std::string &path);
virtual ~BaseRequest();
Callback *add(Callback &&callback, const util::ptr<BaseRequest> &request);
void remove(Callback *callback);
// Must be called by subclasses when a valid Response object is available. It will notify
// all listeners.
void notify();
// This function is called when the request ought to be stopped. Any subclass must make sure this
// is also called in its destructor. Calling this function repeatedly must be safe.
// This function must call notify().
virtual void cancel() = 0;
// This function is called when the request should be reattempted immediately. This is typically
// reaction to a network status change.
virtual void retryImmediately();
public:
const unsigned long thread_id;
const std::string path;
std::unique_ptr<Response> response;
protected:
// This object may hold a shared_ptr to itself. It does this to prevent destruction of this object
// while a request is in progress.
util::ptr<BaseRequest> self;
std::forward_list<std::unique_ptr<Callback>> callbacks;
};
}
#endif

View File

@@ -0,0 +1,27 @@
#ifndef MBGL_STORAGE_FILE_REQUEST
#define MBGL_STORAGE_FILE_REQUEST
#include <mbgl/storage/base_request.hpp>
namespace mbgl {
typedef struct uv_loop_s uv_loop_t;
struct FileRequestBaton;
class FileRequest : public BaseRequest {
public:
FileRequest(const std::string &path, uv_loop_t *loop);
~FileRequest();
void cancel();
private:
FileRequestBaton *ptr = nullptr;
friend struct FileRequestBaton;
};
}
#endif

View File

@@ -0,0 +1,35 @@
#ifndef MBGL_STORAGE_FILE_REQUEST_BATON
#define MBGL_STORAGE_FILE_REQUEST_BATON
#include <mbgl/storage/file_request.hpp>
#include <uv.h>
namespace mbgl {
struct FileRequestBaton {
FileRequestBaton(FileRequest *request_, const std::string &path, uv_loop_t *loop);
~FileRequestBaton();
void cancel();
static void file_opened(uv_fs_t *req);
static void file_stated(uv_fs_t *req);
static void file_read(uv_fs_t *req);
static void file_closed(uv_fs_t *req);
static void notify_error(uv_fs_t *req);
static void cleanup(uv_fs_t *req);
const unsigned long thread_id;
FileRequest *request = nullptr;
uv_fs_t req;
uv_file fd = -1;
bool canceled = false;
std::string body;
uv_buf_t buffer;
};
}
#endif

View File

@@ -0,0 +1,55 @@
#ifndef MBGL_STORAGE_FILE_SOURCE
#define MBGL_STORAGE_FILE_SOURCE
#include <mbgl/storage/resource_type.hpp>
#include <mbgl/storage/request.hpp>
#include <string>
#include <unordered_map>
#include <functional>
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_messenger_s uv_messenger_t;
namespace mbgl {
class BaseRequest;
class SQLiteStore;
class FileSource {
private:
FileSource(const FileSource &) = delete;
FileSource(FileSource &&) = delete;
FileSource& operator=(const FileSource &) = delete;
FileSource& operator=(FileSource &&) = delete;
public:
FileSource(uv_loop_t *loop, const std::string &path);
~FileSource();
public:
// Stores and retrieves the base path/URL for relative requests
void setBase(const std::string &value);
const std::string &getBase() const;
std::unique_ptr<Request> request(ResourceType type, const std::string &url);
void prepare(std::function<void()> fn);
void retryAllPending();
private:
const unsigned long thread_id;
// Stores a URL that is used as a base for loading resources with relative path.
std::string base;
std::unordered_map<std::string, std::weak_ptr<BaseRequest>> pending;
util::ptr<SQLiteStore> store;
uv_loop_t *loop = nullptr;
uv_messenger_t *queue = nullptr;
};
}
#endif

View File

@@ -0,0 +1,57 @@
#ifndef MBGL_STORAGE_HTTP_REQUEST
#define MBGL_STORAGE_HTTP_REQUEST
#include <mbgl/storage/resource_type.hpp>
#include <mbgl/storage/base_request.hpp>
#include <mbgl/storage/http_request_baton.hpp>
#include <string>
#include <memory>
#include <cassert>
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_timer_s uv_timer_t;
namespace mbgl {
struct CacheRequestBaton;
struct HTTPRequestBaton;
struct CacheEntry;
class SQLiteStore;
class HTTPRequest : public BaseRequest {
public:
HTTPRequest(ResourceType type, const std::string &path, uv_loop_t *loop, util::ptr<SQLiteStore> store);
~HTTPRequest();
void cancel();
void retryImmediately();
private:
void startCacheRequest();
void handleCacheResponse(std::unique_ptr<Response> &&response);
void startHTTPRequest(std::unique_ptr<Response> &&res);
void handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&response);
void retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout);
void removeCacheBaton();
void removeHTTPBaton();
void removeBackoffTimer();
private:
const unsigned long thread_id;
uv_loop_t *const loop;
CacheRequestBaton *cache_baton = nullptr;
util::ptr<HTTPRequestBaton> http_baton;
uv_timer_t *backoff_timer = nullptr;
util::ptr<SQLiteStore> store;
const ResourceType type;
uint8_t attempts = 0;
friend struct HTTPRequestBaton;
};
}
#endif

View File

@@ -0,0 +1,73 @@
#ifndef MBGL_STORAGE_HTTP_REQUEST_BATON
#define MBGL_STORAGE_HTTP_REQUEST_BATON
#include <mbgl/storage/response.hpp>
#include <mbgl/util/ptr.hpp>
#include <string>
typedef struct uv_async_s uv_async_t;
namespace mbgl {
class HTTPRequest;
enum class HTTPResponseType : int8_t {
// This error probably won't be resolved by retrying anytime soon. We are giving up.
PermanentError = -5,
// This error might be resolved by waiting some time (e.g. server issues).
// We are going to do an exponential back-off and will try again in a few seconds.
TemporaryError = -4,
// This error was caused by a temporary error and it is likely that it will be resolved
// immediately. We are going to try again right away. This is like the TemporaryError, except
// that we will not perform exponential back-off.
SingularError = -3,
// This error might be resolved once the network reachability status changes.
// We are going to watch the network status for changes and will retry as soon as the operating
// system notifies us of a network status change.
ConnectionError = -2,
// The request was canceled programatically.
Canceled = -1,
// The request is still in progress.
Unknown = 0,
// The request returned data successfully. We retrieved and decoded the data successfully.
Successful = 1,
// The request confirmed that the data wasn't changed. We already have the data.
NotModified = 2,
};
struct HTTPRequestBaton {
HTTPRequestBaton(const std::string &path);
~HTTPRequestBaton();
const unsigned long thread_id;
const std::string path;
HTTPRequest *request = nullptr;
uv_async_t *async = nullptr;
HTTPResponseType type = HTTPResponseType::Unknown;
std::unique_ptr<Response> response;
// Implementation specific use.
void *ptr = nullptr;
// IMPLEMENT THESE 3 PLATFORM SPECIFIC FUNCTIONS:
// Begin the HTTP request. Platform-specific implementation.
static void start(const util::ptr<HTTPRequestBaton> &ptr);
// This will be called to stop/cancel the HTTP request (if possible). Platform-specific implementation.
static void stop(const util::ptr<HTTPRequestBaton> &ptr);
};
}
#endif

View File

@@ -0,0 +1,39 @@
#ifndef MBGL_STORAGE_REQUEST
#define MBGL_STORAGE_REQUEST
#include <mbgl/storage/request_callback.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/ptr.hpp>
#include <forward_list>
typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class BaseRequest;
class Request {
private:
Request(const Request &) = delete;
Request(Request &&) = delete;
Request& operator=(const Request &) = delete;
Request& operator=(Request &&) = delete;
public:
Request(const util::ptr<BaseRequest> &base);
~Request();
void onload(CompletedCallback cb);
void oncancel(AbortedCallback cb);
void cancel();
private:
const unsigned long thread_id;
util::ptr<BaseRequest> base;
std::forward_list<Callback *> callbacks;
};
}
#endif

View File

@@ -0,0 +1,22 @@
#ifndef MBGL_STORAGE_REQUEST_CALLBACK
#define MBGL_STORAGE_REQUEST_CALLBACK
#include <mbgl/util/variant.hpp>
#include <functional>
namespace mbgl {
class Response;
using CompletedCallback = std::function<void(const Response &)>;
using AbortedCallback = std::function<void()>;
using Callback = mapbox::util::variant<
CompletedCallback,
AbortedCallback
>;
}
#endif

View File

@@ -0,0 +1,18 @@
#ifndef MBGL_STORAGE_RESOURCE_TYPE
#define MBGL_STORAGE_RESOURCE_TYPE
#include <cstdint>
namespace mbgl {
enum class ResourceType : uint8_t {
Unknown = 0,
Tile = 1,
Glyphs = 2,
Image = 3,
JSON = 4
};
}
#endif

View File

@@ -0,0 +1,26 @@
#ifndef MBGL_STORAGE_RESPONSE
#define MBGL_STORAGE_RESPONSE
#include <string>
#include <ctime>
namespace mbgl {
class Response {
public:
long code = 0;
int64_t modified = 0;
int64_t expires = 0;
std::string etag;
std::string data;
std::string message;
static int64_t parseCacheControl(const char *value);
};
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef MBGL_STORAGE_SQLITE_STORE
#define MBGL_STORAGE_SQLITE_STORE
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/response.hpp>
#include <uv.h>
#include <string>
typedef struct uv_worker_s uv_worker_t;
namespace mapbox {
namespace sqlite {
class Database;
}
}
namespace mbgl {
class SQLiteStore {
public:
SQLiteStore(uv_loop_t *loop, const std::string &path);
~SQLiteStore();
typedef void (*GetCallback)(std::unique_ptr<Response> &&entry, void *ptr);
void get(const std::string &path, GetCallback cb, void *ptr);
void put(const std::string &path, ResourceType type, const Response &entry);
void updateExpiration(const std::string &path, int64_t expires);
private:
void createSchema();
void closeDatabase();
static void runGet(uv_work_t *req);
static void runPut(uv_work_t *req);
static void deliverResult(uv_work_t *req, int status);
private:
const unsigned long thread_id;
util::ptr<mapbox::sqlite::Database> db;
uv_worker_t *worker = nullptr;
};
}
#endif

View File

@@ -6,6 +6,7 @@
#include <mbgl/util/time.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <map>
@@ -13,7 +14,6 @@
#include <unordered_map>
#include <vector>
#include <set>
#include <memory>
namespace mbgl {
@@ -49,7 +49,7 @@ public:
const std::string &getSpriteURL() const;
public:
std::shared_ptr<StyleLayerGroup> layers;
util::ptr<StyleLayerGroup> layers;
std::vector<std::string> appliedClasses;
std::string glyph_url;

View File

@@ -8,8 +8,8 @@
#include <mbgl/util/vec.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <memory>
#include <forward_list>
namespace mbgl {
@@ -93,12 +93,12 @@ typedef mapbox::util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbo
class StyleBucket {
public:
typedef std::shared_ptr<StyleBucket> Ptr;
typedef util::ptr<StyleBucket> Ptr;
StyleBucket(StyleLayerType type);
std::string name;
std::shared_ptr<StyleSource> style_source;
util::ptr<StyleSource> style_source;
std::string source_layer;
FilterExpression filter;
StyleBucketRender render = std::false_type();

View File

@@ -6,8 +6,9 @@
#include <mbgl/style/style_properties.hpp>
#include <mbgl/style/applied_class_properties.hpp>
#include <mbgl/util/ptr.hpp>
#include <vector>
#include <memory>
#include <string>
#include <map>
#include <set>
@@ -64,7 +65,7 @@ public:
// Bucket information, telling the renderer how to generate the geometries
// for this layer (feature property filters, tessellation instructions, ...).
std::shared_ptr<StyleBucket> bucket;
util::ptr<StyleBucket> bucket;
// Contains all style classes that can be applied to this layer.
const std::map<ClassID, ClassProperties> styles;
@@ -80,7 +81,7 @@ public:
StyleProperties properties;
// Child layer array (if this layer has child layers).
std::shared_ptr<StyleLayerGroup> layers;
util::ptr<StyleLayerGroup> layers;
};
}

View File

@@ -15,7 +15,7 @@ public:
bool hasTransitions() const;
public:
std::vector<std::shared_ptr<StyleLayer>> layers;
std::vector<util::ptr<StyleLayer>> layers;
};
}

View File

@@ -27,7 +27,7 @@ public:
void parse(JSVal document);
std::shared_ptr<StyleLayerGroup> getLayers() {
util::ptr<StyleLayerGroup> getLayers() {
return root;
}
@@ -46,14 +46,14 @@ private:
void parseSources(JSVal value);
std::unique_ptr<StyleLayerGroup> createLayers(JSVal value);
std::shared_ptr<StyleLayer> createLayer(JSVal value);
util::ptr<StyleLayer> createLayer(JSVal value);
void parseLayers();
void parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair);
void parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair);
void parseStyles(JSVal value, std::map<ClassID, ClassProperties> &styles);
void parseStyle(JSVal, ClassProperties &properties);
void parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseReference(JSVal value, util::ptr<StyleLayer> &layer);
void parseBucket(JSVal value, util::ptr<StyleLayer> &layer);
void parseRender(JSVal value, util::ptr<StyleLayer> &layer);
void parseSprite(JSVal value);
void parseGlyphURL(JSVal value);
@@ -94,13 +94,13 @@ private:
private:
std::unordered_map<std::string, const rapidjson::Value *> constants;
std::unordered_map<std::string, const std::shared_ptr<StyleSource>> sources;
std::unordered_map<std::string, const util::ptr<StyleSource>> sources;
// This stores the root layer.
std::shared_ptr<StyleLayerGroup> root;
util::ptr<StyleLayerGroup> root;
// This maps ids to Layer objects, with all items being at the root level.
std::unordered_map<std::string, std::pair<JSVal, std::shared_ptr<StyleLayer>>> layers;
std::unordered_map<std::string, std::pair<JSVal, util::ptr<StyleLayer>>> layers;
// Store a stack of layers we're parsing right now. This is to prevent reference cycles.
std::forward_list<StyleLayer *> stack;

View File

@@ -2,18 +2,18 @@
#define MBGL_STYLE_STYLE_SOURCE
#include <mbgl/style/types.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <rapidjson/document.h>
#include <memory>
#include <vector>
#include <string>
#include <rapidjson/document.h>
namespace mbgl {
class Source;
class SourceInfo {
class SourceInfo : private util::noncopyable {
public:
SourceType type = SourceType::Vector;
std::string url;
@@ -31,12 +31,12 @@ public:
class StyleSource : public std::enable_shared_from_this<StyleSource> {
public:
SourceInfo info;
util::ptr<SourceInfo> info;
bool enabled = false;
std::shared_ptr<Source> source;
util::ptr<Source> source;
StyleSource(const SourceInfo &info)
StyleSource(const util::ptr<SourceInfo> &info)
: info(info)
{}
};

View File

@@ -4,6 +4,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/pbf.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/ptr.hpp>
#include <cstdint>
#include <vector>
@@ -47,8 +48,15 @@ private:
class GlyphPBF {
public:
GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource);
GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const util::ptr<FileSource> &fileSource);
private:
GlyphPBF(const GlyphPBF &) = delete;
GlyphPBF(GlyphPBF &&) = delete;
GlyphPBF &operator=(const GlyphPBF &) = delete;
GlyphPBF &operator=(GlyphPBF &&) = delete;
public:
void parse(FontStack &stack);
std::shared_future<GlyphPBF &> getFuture();
@@ -63,7 +71,7 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
GlyphStore(const std::shared_ptr<FileSource> &fileSource);
GlyphStore(const util::ptr<FileSource> &fileSource);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
@@ -82,7 +90,7 @@ public:
std::string glyphURL;
private:
const std::shared_ptr<FileSource> fileSource;
const util::ptr<FileSource> fileSource;
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::mutex mtx;

View File

@@ -0,0 +1,15 @@
#ifndef MBGL_UTIL_COMPRESSION
#define MBGL_UTIL_COMPRESSION
#include <string>
namespace mbgl {
namespace util {
std::string compress(const std::string &raw);
std::string decompress(const std::string &raw);
}
}
#endif

View File

@@ -1,43 +0,0 @@
#ifndef MBGL_UTIL_FILESOURCE
#define MBGL_UTIL_FILESOURCE
#include <mbgl/util/uv.hpp>
#include <string>
#include <memory>
#include <functional>
namespace mbgl {
namespace platform {
struct Response;
}
enum class ResourceType : uint8_t {
Unknown,
Tile,
Glyphs,
Image,
JSON
};
class FileSource {
public:
FileSource();
void setBase(const std::string &value);
const std::string &getBase() const;
void load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop = nullptr);
private:
// Stores a URL that is used as a base for loading resources with relative path.
std::string base;
// Stores the absolute path to the cache directory.
const std::string cache;
};
}
#endif

View File

@@ -1,6 +1,8 @@
#ifndef MBGL_UTIL_INTERPOLATE
#define MBGL_UTIL_INTERPOLATE
#include <array>
namespace mbgl {
namespace util {

View File

@@ -0,0 +1,38 @@
#ifndef HEADER_PARSEDATE_H
#define HEADER_PARSEDATE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
time_t parse_date(const char *p);
#ifdef __cplusplus
}
#endif
#endif /* HEADER_PARSEDATE_H */

29
include/mbgl/util/ptr.hpp Normal file
View File

@@ -0,0 +1,29 @@
#ifndef MBGL_UTIL_PTR
#define MBGL_UTIL_PTR
#include <memory>
#include <cassert>
namespace mbgl {
namespace util {
template <typename T>
class ptr : public ::std::shared_ptr<T> {
public:
template <typename... Args>
inline ptr(Args &&... args)
: ::std::shared_ptr<T>(::std::forward<Args>(args)...) {}
inline auto operator->() const -> decltype(this->::std::shared_ptr<T>::operator->()) {
assert(*this);
return ::std::shared_ptr<T>::operator->();
}
inline auto operator*() const -> decltype(this->::std::shared_ptr<T>::operator*()) {
assert(*this);
return ::std::shared_ptr<T>::operator*();
}
};
}
}
#endif

92
include/mbgl/util/queue.h Normal file
View File

@@ -0,0 +1,92 @@
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef QUEUE_H_
#define QUEUE_H_
typedef void *QUEUE[2];
/* Private macros. */
#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
/* Public macros. */
#define QUEUE_DATA(ptr, type, field) \
((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
#define QUEUE_FOREACH(q, h) \
for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
#define QUEUE_EMPTY(q) \
((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
#define QUEUE_HEAD(q) \
(QUEUE_NEXT(q))
#define QUEUE_INIT(q) \
do { \
QUEUE_NEXT(q) = (q); \
QUEUE_PREV(q) = (q); \
} \
while (0)
#define QUEUE_ADD(h, n) \
do { \
QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \
QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV(h) = QUEUE_PREV(n); \
QUEUE_PREV_NEXT(h) = (h); \
} \
while (0)
#define QUEUE_SPLIT(h, q, n) \
do { \
QUEUE_PREV(n) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(n) = (n); \
QUEUE_NEXT(n) = (q); \
QUEUE_PREV(h) = QUEUE_PREV(q); \
QUEUE_PREV_NEXT(h) = (h); \
QUEUE_PREV(q) = (n); \
} \
while (0)
#define QUEUE_INSERT_HEAD(h, q) \
do { \
QUEUE_NEXT(q) = QUEUE_NEXT(h); \
QUEUE_PREV(q) = (h); \
QUEUE_NEXT_PREV(q) = (q); \
QUEUE_NEXT(h) = (q); \
} \
while (0)
#define QUEUE_INSERT_TAIL(h, q) \
do { \
QUEUE_NEXT(q) = (h); \
QUEUE_PREV(q) = QUEUE_PREV(h); \
QUEUE_PREV_NEXT(q) = (q); \
QUEUE_PREV(h) = (q); \
} \
while (0)
#define QUEUE_REMOVE(q) \
do { \
QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \
QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \
} \
while (0)
#endif /* QUEUE_H_ */

View File

@@ -4,11 +4,11 @@
#include <mbgl/util/transition.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/renderer/prerendered_texture.hpp>
#include <string>
#include <mutex>
#include <memory>
typedef struct uv_loop_s uv_loop_t;
@@ -17,7 +17,7 @@ namespace mbgl {
class Raster : public std::enable_shared_from_this<Raster> {
public:
Raster(const std::shared_ptr<Texturepool> &texturepool);
Raster(const util::ptr<Texturepool> &texturepool);
~Raster();
// load image data
@@ -57,7 +57,7 @@ private:
bool loaded = false;
// shared texture pool
std::shared_ptr<Texturepool> texturepool;
util::ptr<Texturepool> texturepool;
// min/mag filter
uint32_t filter = 0;
@@ -66,7 +66,7 @@ private:
std::unique_ptr<util::Image> img;
// fade in transition
std::shared_ptr<util::transition> fade_transition = nullptr;
util::ptr<util::transition> fade_transition = nullptr;
};
}

View File

@@ -0,0 +1,74 @@
#pragma once
#include <string>
#include <stdexcept>
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;
namespace mapbox {
namespace sqlite {
enum OpenFlag : int {
ReadOnly = 0x00000001,
ReadWrite = 0x00000002,
Create = 0x00000004,
NoMutex = 0x00008000,
FullMutex = 0x00010000,
SharedCache = 0x00020000,
PrivateCache = 0x00040000,
};
struct Exception : std::runtime_error {
inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
const int code = 0;
};
class Statement;
class Database {
private:
Database(const Database &) = delete;
Database &operator=(const Database &) = delete;
public:
Database(const std::string &filename, int flags = 0);
Database(Database &&);
~Database();
Database &operator=(Database &&);
operator bool() const;
void exec(const std::string &sql);
Statement prepare(const char *query);
private:
sqlite3 *db = nullptr;
};
class Statement {
private:
Statement(const Statement &) = delete;
Statement &operator=(const Statement &) = delete;
public:
Statement(sqlite3 *db, const char *sql);
Statement(Statement &&);
~Statement();
Statement &operator=(Statement &&);
operator bool() const;
template <typename T> void bind(int offset, T value);
void bind(int offset, const std::string &value, bool retain = true);
template <typename T> T get(int offset);
bool run();
void reset();
private:
sqlite3_stmt *stmt = nullptr;
};
}
}

View File

@@ -0,0 +1,29 @@
#ifndef MBGL_UTIL_UV_CHANNEL
#define MBGL_UTIL_UV_CHANNEL
#ifdef __cplusplus
extern "C" {
#endif
#include <uv.h>
// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
typedef struct uv_chan_s uv_chan_t;
struct uv_chan_s {
uv_mutex_t mutex;
uv_cond_t cond;
void *q[2];
};
int uv_chan_init(uv_chan_t *chan);
void uv_chan_send(uv_chan_t *chan, void *data);
void *uv_chan_receive(uv_chan_t *chan);
void uv_chan_destroy(uv_chan_t *chan);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,32 @@
#ifndef MBGL_UTIL_UV_MESSENGER
#define MBGL_UTIL_UV_MESSENGER
#ifdef __cplusplus
extern "C" {
#endif
#include <uv.h>
typedef struct uv_messenger_s uv_messenger_t;
typedef void (*uv_messenger_cb)(void *arg);
typedef void (*uv_messenger_stop_cb)(uv_messenger_t *msgr);
struct uv_messenger_s {
uv_mutex_t mutex;
uv_async_t async;
uv_messenger_cb callback;
void *data;
void *queue[2];
};
int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback);
void uv_messenger_send(uv_messenger_t *msgr, void *arg);
void uv_messenger_stop(uv_messenger_t *msgr);
void uv_messenger_ref(uv_messenger_t *msgr);
void uv_messenger_unref(uv_messenger_t *msgr);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,42 @@
#ifndef MBGL_UTIL_UV_WORKER
#define MBGL_UTIL_UV_WORKER
#ifdef __cplusplus
extern "C" {
#endif
typedef struct uv_messenger_s uv_messenger_t;
#include <mbgl/util/uv-channel.h>
#include <stdlib.h>
typedef struct uv_worker_s uv_worker_t;
typedef void (*uv_worker_cb)(void *data);
typedef void (*uv_worker_after_cb)(void *data);
typedef void (*uv_worker_close_cb)(uv_worker_t *worker);
struct uv_worker_s {
#ifndef NDEBUG
unsigned long thread_id;
#endif
uv_loop_t *loop;
uv_messenger_t *msgr;
uv_chan_t chan;
const char *name;
int count;
uv_worker_close_cb close_cb;
unsigned int active_items;
};
int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name);
void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
uv_worker_after_cb after_work_cb);
void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -4,6 +4,7 @@
#include <string>
typedef struct uv_async_s uv_async_t;
typedef struct uv_timer_s uv_timer_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_loop_s uv_loop_t;
@@ -11,9 +12,15 @@ namespace uv {
std::string cwd();
struct deleter {
void operator()(uv_async_t *async);
void operator()(uv_timer_t *timer);
};
class thread;
class rwlock;
class loop;
class worker;
}

View File

@@ -1,21 +1,13 @@
#ifndef MBGL_UTIL_UV_DETAIL
#define MBGL_UTIL_UV_DETAIL
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/uv-worker.h>
#include <uv.h>
#include <functional>
#include <cassert>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include <boost/lockfree/queue.hpp>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include <string>
@@ -112,6 +104,24 @@ private:
uv_once_t o = UV_ONCE_INIT;
};
class worker {
public:
inline worker(uv_loop_t *loop, unsigned int count, const char *name = nullptr) : w(new uv_worker_t) {
uv_worker_init(w, loop, count, name);
}
inline ~worker() {
uv_worker_close(w, [](uv_worker_t *worker) {
delete worker;
});
}
inline void add(void *data, uv_worker_cb work_cb, uv_worker_after_cb after_work_cb) {
uv_worker_send(w, data, work_cb, after_work_cb);
}
private:
uv_worker_t *w;
};
template <typename T>
class work {
public:
@@ -119,30 +129,26 @@ public:
typedef void (*after_work_callback)(T &object);
template<typename... Args>
work(const std::shared_ptr<loop> &loop, work_callback work_cb, after_work_callback after_work_cb, Args&&... args)
: loop(loop),
data(std::forward<Args>(args)...),
work(worker &worker, work_callback work_cb, after_work_callback after_work_cb, Args&&... args)
: data(std::forward<Args>(args)...),
work_cb(work_cb),
after_work_cb(after_work_cb) {
req.data = this;
uv_queue_work(**loop, &req, do_work, after_work);
worker.add(this, do_work, after_work);
}
private:
static void do_work(uv_work_t *req) {
work<T> *w = static_cast<work<T> *>(req->data);
static void do_work(void *data) {
work<T> *w = reinterpret_cast<work<T> *>(data);
w->work_cb(w->data);
}
static void after_work(uv_work_t *req, int) {
work<T> *w = static_cast<work<T> *>(req->data);
static void after_work(void *data) {
work<T> *w = reinterpret_cast<work<T> *>(data);
w->after_work_cb(w->data);
delete w;
}
private:
std::shared_ptr<uv::loop> loop;
uv_work_t req;
T data;
work_callback work_cb;
after_work_callback after_work_cb;

View File

@@ -15,7 +15,8 @@
'../common/platform_default.cpp',
'../common/glfw_view.hpp',
'../common/glfw_view.cpp',
'../common/curl_request.cpp',
'../common/http_request_baton_curl.cpp',
'../common/linux.cpp',
'../common/stderr_log.hpp',
'../common/stderr_log.cpp',
],
@@ -32,7 +33,6 @@
],
'OTHER_LDFLAGS': [
'<@(glfw3_libraries)',
'<@(curl_libraries)',
],
}
},
@@ -46,7 +46,6 @@
'link_settings': {
'libraries': [
'<@(glfw3_libraries)',
'<@(curl_libraries)',
'-lboost_regex'
],
},

View File

@@ -1,9 +1,12 @@
#include "../common/settings_nsuserdefaults.hpp"
#include "../common/glfw_view.hpp"
#include "../common/nslog_log.hpp"
#include "../common/Reachability.h"
#import <Foundation/Foundation.h>
#include <uv.h>
@interface URLHandler : NSObject
@property (nonatomic) mbgl::Map *map;
@@ -68,16 +71,25 @@
@end
int main() {
fprintf(stderr, "main thread: 0x%lx\n", uv_thread_self());
mbgl::Log::Set<mbgl::NSLogBackend>();
GLFWView view;
mbgl::Map map(view);
mbgl::Map *map_ptr = &map;
URLHandler *handler = [[URLHandler alloc] init];
[handler setMap:&map];
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
// Notify map object when network reachability status changes.
Reachability* reachability = [Reachability reachabilityForInternetConnection];
reachability.reachableBlock = ^(Reachability *reachability) {
map_ptr->setReachability(true);
};
[reachability startNotifier];
// Load settings
mbgl::Settings_NSUserDefaults settings;
map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
@@ -97,6 +109,8 @@ int main() {
int ret = view.run();
[reachability stopNotifier];
// Save settings
map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom);
settings.bearing = map.getBearing();

View File

@@ -15,8 +15,10 @@
'../common/platform_nsstring.mm',
'../common/glfw_view.hpp',
'../common/glfw_view.cpp',
'../common/foundation_request.h',
'../common/foundation_request.mm',
'../common/reachability.h',
'../common/reachability.m',
'../common/http_request_baton_cocoa.mm',
'../common/osx.mm',
'../common/nslog_log.hpp',
'../common/nslog_log.mm',
],
@@ -33,6 +35,7 @@
],
'OTHER_LDFLAGS': [
'<@(glfw3_libraries)',
'-framework SystemConfiguration',
],
'SDKROOT': 'macosx',
'INFOPLIST_FILE': 'Info.plist',

View File

@@ -31,7 +31,7 @@
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gl.cpp',
'<(SHARED_INTERMEDIATE_DIR)/src/shader/shaders_gles2.cpp'
],
'include_dirs':[
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)/include/',
]
}
@@ -126,53 +126,70 @@
'<!@(find src -name "*.glsl")',
'bin/style.json'
],
'xcode_settings': {
'SDKROOT': 'macosx',
'SUPPORTED_PLATFORMS':['macosx'],
'MACOSX_DEPLOYMENT_TARGET':'10.9',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
'OTHER_CPLUSPLUSFLAGS':[
'<@(png_cflags)',
'<@(uv_cflags)',
'-I<(boost_root)/include',
]
},
'include_dirs':[
'./include'
'include_dirs': [
'./include'
],
'cflags': [
'<@(png_cflags)',
'-I<(boost_root)/include',
],
'direct_dependent_settings': {
'include_dirs':[
'./include',
],
'cflags': [
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'SDKROOT': 'macosx',
'SUPPORTED_PLATFORMS': ['macosx'],
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
'OTHER_CPLUSPLUSFLAGS': [
'<@(png_cflags)',
'<@(uv_cflags)',
],
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS':[
'<@(png_cflags)',
'<@(uv_cflags)',
]
'<@(curl_cflags)',
'<@(sqlite3_cflags)',
'-I<(boost_root)/include',
],
'OTHER_CFLAGS': [
'<@(uv_cflags)',
],
},
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_LDFLAGS': [
'<@(png_libraries)',
'<@(uv_libraries)',
]
}
}, {
'libraries': [
}, {
'cflags': [
'<@(png_cflags)',
'<@(uv_cflags)',
'<@(curl_cflags)',
'<@(sqlite3_cflags)',
'-I<(boost_root)/include',
],
}]
],
'direct_dependent_settings': {
'include_dirs': [
'./include',
],
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [
'<@(uv_cflags)',
'<@(curl_cflags)',
'-I<(boost_root)/include',
],
'OTHER_LDFLAGS': [
'<@(png_libraries)',
'<@(uv_libraries)',
'<@(curl_libraries)',
'<@(sqlite3_libraries)',
]
}]
]
}
}, {
'cflags': [
'<@(uv_cflags)',
'<@(curl_cflags)',
'-I<(boost_root)/include',
],
'libraries': [
'<@(png_libraries)',
'<@(uv_libraries)',
'<@(curl_libraries)',
'<@(sqlite3_libraries)',
]
}]
]
}
},
{
@@ -181,7 +198,7 @@
'type': 'static_library',
'hard_dependency': 1,
'dependencies': [
'shaders',
'shaders',
],
'sources': [
'<!@(find src/ \( -name "*.cpp" ! -name shaders.hpp ! -name shaders_gles2.cpp ! -name shaders_gl.cpp \))',
@@ -195,56 +212,42 @@
],
'xcode_settings': {
'SDKROOT': 'iphoneos',
'SUPPORTED_PLATFORMS':['iphonesimulator','iphoneos'],
'SUPPORTED_PLATFORMS': ['iphonesimulator','iphoneos'],
'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
'TARGETED_DEVICE_FAMILY': '1,2',
'CODE_SIGN_IDENTITY': 'iPhone Developer',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'PUBLIC_HEADERS_FOLDER_PATH': 'include',
'GCC_INPUT_FILETYPE':'sourcecode.cpp.cpp',
'OTHER_CPLUSPLUSFLAGS':[
'GCC_INPUT_FILETYPE': 'sourcecode.cpp.cpp',
'OTHER_CPLUSPLUSFLAGS': [
'<@(png_cflags)',
'<@(uv_cflags)',
'<@(curl_cflags)',
'<@(sqlite3_cflags)',
'-I<(boost_root)/include',
]
],
},
'include_dirs':[
'./include'
],
'cflags': [
'<@(png_cflags)',
'<@(uv_cflags)',
'-I<(boost_root)/include',
'include_dirs': [
'./include'
],
'direct_dependent_settings': {
'include_dirs':[
'./include'
'include_dirs': [
'./include',
],
'xcode_settings': {
'OTHER_LDFLAGS': [
'<@(png_libraries)',
'<@(uv_libraries)',
'<@(curl_libraries)',
'<@(sqlite3_libraries)',
],
'cflags': [
'<@(png_cflags)',
'<@(uv_cflags)',
'OTHER_CPLUSPLUSFLAGS': [
'<@(uv_cflags)',
],
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS':[
'<@(png_cflags)',
'<@(uv_cflags)',
]
},
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_LDFLAGS': [
'<@(png_libraries)',
'<@(uv_libraries)',
]
}
}, {
'libraries': [
'<@(png_libraries)',
'<@(uv_libraries)'
]
}]
]
'OTHER_CFLAGS': [
'<@(uv_cflags)',
],
}
}
}
]

View File

@@ -16,12 +16,12 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
# some limited coverage
if [[ ${BUILDTYPE} == "Debug" ]]; then
if [[ ${CXX} == "g++" ]]; then
export CXXFLAGS="-fsanitize=address ${CXXFLAGS}"
export CFLAGS="-fsanitize=address ${CFLAGS}"
export CXXFLAGS="-fsanitize=address -g ${CXXFLAGS}"
export CFLAGS="-fsanitize=address -g ${CFLAGS}"
export LDFLAGS="-fsanitize=address ${LDFLAGS}"
elif [[ ${CXX} == "clang++" ]]; then
export CXXFLAGS="-fsanitize=thread -fPIC ${CXXFLAGS}"
export CFLAGS="-fsanitize=thread ${CFLAGS}"
export CXXFLAGS="-fsanitize=thread -g -fPIC ${CXXFLAGS}"
export CFLAGS="-fsanitize=thread -g ${CFLAGS}"
export LDFLAGS="-fsanitize=thread -pie ${LDFLAGS}"
fi
fi

View File

@@ -53,6 +53,7 @@ NPM=$(which npm)
MP_HASH="e82e6c00ea5a0cda147e4c121a3c7751cae69ff8"
DIR_HASH=$(echo `pwd` | git hash-object --stdin)
if [ ! -d 'mapnik-packaging/' ]; then
git clone https://github.com/mapnik/mapnik-packaging.git
fi
@@ -66,7 +67,7 @@ export CXX11=true
if [ ${UNAME} = 'Darwin' ]; then
if [ ! -z "${TRAVIS:-}" ]; then
if [[ $TRAVIS ]]; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libcpp-x86_64-macosx
@@ -83,30 +84,40 @@ source iPhoneOS.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7-iphoneos/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneOSs.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-armv7s-iphoneoss/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneOS64.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-arm64-iphoneos64/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneSimulator.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-i386-iphonesimulator/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source iPhoneSimulator64.sh
export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-iphonesimulator/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
echo ' ...done'
source MacOSX.sh
@@ -114,6 +125,7 @@ export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libcpp-x86_64-macosx/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -d out/build-cpp11-libcpp-x86_64-macosx/include/boost ] ; then ./scripts/build_boost.sh `pwd`/../../src/ `pwd`/../../include/ `pwd`/../../linux/ `pwd`/../../common/ ; fi
@@ -121,8 +133,10 @@ export LIBUV_VERSION=0.10.28
# setup iOS universal libs
./scripts/make_universal.sh
patch -p0 --forward < patches/curl-ios.diff || true
echo "NOTE: One patch FAILURE is expected. The other should have been applied or ignored."
if [ ! -z "${TRAVIS:-}" ] && [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
tar -zcf out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-universal
aws s3 cp --acl public-read out/build-cpp11-libcpp-ios_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/
tar -zcf out/build-cpp11-libcpp-osx_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libcpp-x86_64-macosx
@@ -138,7 +152,7 @@ cd ../../
elif [ ${UNAME} = 'Linux' ]; then
if [ ! -z "${TRAVIS:-}" ]; then
if [[ $TRAVIS ]]; then
if aws s3 cp s3://${AWS_S3_BUCKET}/dependencies/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz ./out/ ; then
rm -rf out/build-cpp11-libstdcpp-gcc-x86_64-linux
tar -xzf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz
@@ -150,11 +164,12 @@ export LIBUV_VERSION=0.10.28
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libglfw3.a ] ; then ./scripts/build_glfw.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libpng.a ] ; then ./scripts/build_png.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libuv.a ] ; then ./scripts/build_libuv.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libsqlite3.a ] ; then ./scripts/build_sqlite.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libssl.a ] ; then ./scripts/build_openssl.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libcurl.a ] ; then ./scripts/build_curl.sh ; fi
if [ ! -f out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/libboost_regex.a ] ; then ./scripts/build_boost.sh --with-regex ; fi
if [ ! -z "${TRAVIS:-}" ] && [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
if [[ $TRAVIS && $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY ]] ; then
if ! tar --compare -zf out/build-cpp11-libstdcpp-gcc-x86_64-linux.tar.gz ; then
tar -zcf out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz out/build-cpp11-libstdcpp-gcc-x86_64-linux
aws s3 cp --acl public-read out/build-cpp11-libstdcpp-gcc-x86_64-linux_${MP_HASH}_${DIR_HASH}.tar.gz s3://${AWS_S3_BUCKET}/dependencies/

View File

@@ -19,11 +19,10 @@
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/util/filesource.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/log.hpp>
#include <algorithm>
#include <memory>
#include <iostream>
#define _USE_MATH_DEFINES
@@ -34,12 +33,15 @@ using namespace mbgl;
Map::Map(View& view)
: loop(std::make_shared<uv::loop>()),
thread(std::make_unique<uv::thread>()),
async_terminate(new uv_async_t()),
async_render(new uv_async_t()),
async_cleanup(new uv_async_t()),
view(view),
#ifndef NDEBUG
main_thread(uv_thread_self()),
#endif
transform(view),
fileSource(std::make_shared<FileSource>()),
style(std::make_shared<Style>()),
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
glyphStore(std::make_shared<GlyphStore>(fileSource)),
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
texturepool(std::make_shared<Texturepool>()),
painter(*this) {
@@ -53,49 +55,93 @@ Map::Map(View& view)
}
Map::~Map() {
// Clear the style first before the rest of the constructor deletes members of this object.
// This is required because members of the style reference the Map object in their destructors.
style.reset();
if (async) {
stop();
}
// Explicitly reset all pointers.
texturepool.reset();
sprite.reset();
spriteAtlas.reset();
glyphStore.reset();
glyphAtlas.reset();
style.reset();
fileSource.reset();
workers.reset();
uv_run(**loop, UV_RUN_DEFAULT);
}
uv::worker &Map::getWorker() {
if (!workers) {
workers = std::make_unique<uv::worker>(**loop, 4, "Tile Worker");
}
return *workers;
}
void Map::start() {
assert(uv_thread_self() == main_thread);
assert(!async);
// When starting map rendering in another thread, we perform async/continuously
// updated rendering. Only in these cases, we attach the async handlers.
async = true;
// Setup async notifications
async_terminate = new uv_async_t();
uv_async_init(**loop, async_terminate, terminate);
async_terminate->data = **loop;
// Reset the flag.
is_stopped = false;
async_render = new uv_async_t();
uv_async_init(**loop, async_render, render);
// Setup async notifications
uv_async_init(**loop, async_terminate.get(), terminate);
async_terminate->data = this;
uv_async_init(**loop, async_render.get(), render);
async_render->data = this;
async_cleanup = new uv_async_t();
uv_async_init(**loop, async_cleanup, cleanup);
uv_async_init(**loop, async_cleanup.get(), cleanup);
async_cleanup->data = this;
uv_thread_create(*thread, [](void *arg) {
Map *map = static_cast<Map *>(arg);
#ifndef NDEBUG
map->map_thread = uv_thread_self();
#endif
#ifdef __APPLE__
pthread_setname_np("Map");
#endif
map->run();
#ifndef NDEBUG
map->map_thread = -1;
#endif
// Make sure that the stop() function knows when to stop invoking the callback function.
map->is_stopped = true;
map->view.notify();
}, this);
}
void Map::stop() {
if (async_terminate != nullptr) {
uv_async_send(async_terminate);
void Map::stop(stop_callback cb, void *data) {
assert(uv_thread_self() == main_thread);
assert(main_thread != map_thread);
assert(async);
uv_async_send(async_terminate.get());
if (cb) {
// Wait until the render thread stopped. We are using this construct instead of plainly
// relying on the thread_join because the system might need to run things in the current
// thread that is required for the render thread to terminate correctly. This is for example
// the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
// thread (== main thread) is blocked. The callback function should use an efficient waiting
// function to avoid a busy waiting loop.
while (!is_stopped) {
cb(data);
}
}
// If a callback function was provided, this should return immediately because the thread has
// already finished executing.
uv_thread_join(*thread);
// Run the event loop once to make sure our async delete handlers are called.
uv_run(**loop, UV_RUN_ONCE);
async = false;
}
@@ -104,22 +150,35 @@ void Map::delete_async(uv_handle_t *handle, int status) {
}
void Map::run() {
#ifndef NDEBUG
if (!async) {
map_thread = main_thread;
}
#endif
assert(uv_thread_self() == map_thread);
setup();
prepare();
uv_run(**loop, UV_RUN_DEFAULT);
// Run the event loop once more to make sure our async delete handlers are called.
uv_run(**loop, UV_RUN_ONCE);
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
if (!async) {
render();
#ifndef NDEBUG
map_thread = -1;
#endif
}
}
void Map::rerender() {
// We only send render events if we want to continuously update the map
// (== async rendering).
if (async && async_render != nullptr) {
uv_async_send(async_render);
if (async) {
uv_async_send(async_render.get());
}
}
@@ -139,7 +198,7 @@ void Map::swapped() {
void Map::cleanup() {
if (async_cleanup != nullptr) {
uv_async_send(async_cleanup);
uv_async_send(async_cleanup.get());
}
}
@@ -153,9 +212,20 @@ void Map::terminate() {
painter.terminate();
}
void Map::setReachability(bool reachable) {
// Note: This function may be called from *any* thread.
if (reachable) {
if (fileSource) {
fileSource->prepare([&]() {
fileSource->retryAllPending();
});
}
}
}
void Map::render(uv_async_t *async, int status) {
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
if (map->state.hasSize()) {
if (map->is_rendered.test_and_set() == false) {
@@ -175,44 +245,48 @@ void Map::render(uv_async_t *async, int status) {
void Map::terminate(uv_async_t *async, int status) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
uv_loop_t *loop = static_cast<uv_loop_t *>(async->data);
uv_walk(loop, [](uv_handle_t *handle, void */*arg*/) {
if (!uv_is_closing(handle)) {
uv_close(handle, NULL);
}
}, NULL);
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
// Remove all of these to make sure they are destructed in the correct thread.
map->glyphStore.reset();
map->fileSource.reset();
map->style.reset();
map->workers.reset();
map->activeSources.clear();
uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr);
uv_close((uv_handle_t *)map->async_render.get(), nullptr);
uv_close((uv_handle_t *)map->async_terminate.get(), nullptr);
}
#pragma mark - Setup
void Map::setup() {
assert(uv_thread_self() == map_thread);
view.make_active();
painter.setup();
view.make_inactive();
}
void Map::setStyleURL(const std::string &url) {
fileSource->load(ResourceType::JSON, url, [&](platform::Response *res) {
if (res->code == 200) {
// Calculate the base
const size_t pos = url.rfind('/');
std::string base = "";
if (pos != std::string::npos) {
base = url.substr(0, pos + 1);
}
this->setStyleJSON(res->body, base);
} else {
Log::Error(Event::Setup, "loading style failed: %d (%s)", res->code, res->error_message.c_str());
}
}, loop);
// TODO: Make threadsafe.
styleURL = url;
}
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
// TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
sprite.reset();
if (!style) {
style = std::make_shared<Style>();
}
style->loadJSON((const uint8_t *)styleJSON.c_str());
if (!fileSource) {
fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
glyphStore = std::make_shared<GlyphStore>(fileSource);
}
fileSource->setBase(base);
glyphStore->setURL(util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken()));
update();
@@ -223,6 +297,7 @@ std::string Map::getStyleJSON() const {
}
void Map::setAccessToken(std::string access_token) {
// TODO: Make threadsafe.
accessToken.swap(access_token);
}
@@ -230,7 +305,7 @@ std::string Map::getAccessToken() const {
return accessToken;
}
std::shared_ptr<Sprite> Map::getSprite() {
util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || sprite->pixelRatio != pixelRatio) {
@@ -460,8 +535,10 @@ void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
}
void Map::updateSources() {
assert(uv_thread_self() == map_thread);
// First, disable all existing sources.
for (const std::shared_ptr<StyleSource> &source : activeSources) {
for (const util::ptr<StyleSource> &source : activeSources) {
source->enabled = false;
}
@@ -469,7 +546,7 @@ void Map::updateSources() {
updateSources(style->layers);
// Then, construct or destroy the actual source object, depending on enabled state.
for (const std::shared_ptr<StyleSource> &style_source : activeSources) {
for (const util::ptr<StyleSource> &style_source : activeSources) {
if (style_source->enabled) {
if (!style_source->source) {
style_source->source = std::make_shared<Source>(style_source->info);
@@ -481,20 +558,20 @@ void Map::updateSources() {
}
// Finally, remove all sources that are disabled.
util::erase_if(activeSources, [](std::shared_ptr<StyleSource> source){
util::erase_if(activeSources, [](util::ptr<StyleSource> source){
return !source->enabled;
});
}
const std::set<std::shared_ptr<StyleSource>> Map::getActiveSources() const {
const std::set<util::ptr<StyleSource>> Map::getActiveSources() const {
return activeSources;
}
void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
if (!group) {
return;
}
for (const std::shared_ptr<StyleLayer> &layer : group->layers) {
for (const util::ptr<StyleLayer> &layer : group->layers) {
if (!layer) continue;
if (layer->bucket) {
if (layer->bucket->style_source) {
@@ -507,7 +584,7 @@ void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
}
void Map::updateTiles() {
for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->update(*this);
}
}
@@ -515,13 +592,37 @@ void Map::updateTiles() {
void Map::updateRenderState() {
// Update all clipping IDs.
ClipIDGenerator generator;
for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
for (const util::ptr<StyleSource> &source : getActiveSources()) {
generator.update(source->source->getLoadedTiles());
source->source->updateMatrices(painter.projMatrix, state);
}
}
void Map::prepare() {
if (!fileSource) {
fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
glyphStore = std::make_shared<GlyphStore>(fileSource);
}
if (!style) {
style = std::make_shared<Style>();
fileSource->request(ResourceType::JSON, styleURL)->onload([&](const Response &res) {
if (res.code == 200) {
// Calculate the base
const size_t pos = styleURL.rfind('/');
std::string base = "";
if (pos != std::string::npos) {
base = styleURL.substr(0, pos + 1);
}
setStyleJSON(res.data, base);
} else {
Log::Error(Event::Setup, "loading style failed: %ld (%s)", res.code, res.message.c_str());
}
});
}
// Update transform transitions.
animationTime = util::now();
if (transform.needsTransition()) {
@@ -568,7 +669,7 @@ void Map::render() {
// This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->finishRender(painter);
}
@@ -582,7 +683,7 @@ void Map::render() {
view.make_inactive();
}
void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
void Map::renderLayers(util::ptr<StyleLayerGroup> group) {
if (!group) {
// Make sure that we actually do have a layer group.
return;
@@ -625,7 +726,7 @@ void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
}
}
void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
void Map::renderLayer(util::ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
if (layer_desc->type == StyleLayerType::Background) {
// This layer defines a background color/image.

View File

@@ -5,7 +5,7 @@
using namespace mbgl;
RasterTileData::RasterTileData(Tile::ID id, Map &map, const SourceInfo &source)
RasterTileData::RasterTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: TileData(id, map, source),
bucket(map.getTexturepool(), properties) {
}
@@ -25,10 +25,10 @@ void RasterTileData::parse() {
}
}
void RasterTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
void RasterTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
bucket.render(painter, layer_desc, id, matrix);
}
bool RasterTileData::hasData(std::shared_ptr<StyleLayer> /*layer_desc*/) const {
bool RasterTileData::hasData(util::ptr<StyleLayer> /*layer_desc*/) const {
return bucket.hasData();
}

View File

@@ -6,7 +6,7 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/util/filesource.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
@@ -20,7 +20,7 @@
namespace mbgl {
Source::Source(SourceInfo& info)
Source::Source(const util::ptr<SourceInfo>& info)
: info(info)
{
}
@@ -29,32 +29,33 @@ Source::Source(SourceInfo& info)
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
void Source::load(Map& map) {
if (info.url.empty()) {
if (info->url.empty()) {
loaded = true;
return;
}
std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
std::shared_ptr<Source> source = shared_from_this();
std::string url = util::mapbox::normalizeSourceURL(info->url, map.getAccessToken());
util::ptr<Source> source = shared_from_this();
map.getFileSource()->load(ResourceType::JSON, url, [source, &map](platform::Response *res) {
if (res->code != 200) {
map.getFileSource()->request(ResourceType::JSON, url)->onload([source, &map](const Response &res) {
if (res.code != 200) {
Log::Warning(Event::General, "failed to load source TileJSON");
return;
}
rapidjson::Document d;
d.Parse<0>(res->body.c_str());
d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
Log::Warning(Event::General, "invalid source TileJSON");
return;
}
source->info.parseTileJSONProperties(d);
source->info->parseTileJSONProperties(d);
source->loaded = true;
map.update();
});
}
@@ -98,7 +99,7 @@ void Source::drawClippingMasks(Painter &painter) {
}
}
void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) {
gl::group group(std::string("layer: ") + layer_desc->id);
for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
Tile &tile = *pair.second;
@@ -108,7 +109,7 @@ void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc) {
}
}
void Source::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
auto it = tiles.find(id);
if (it != tiles.end() && it->second->data && it->second->data->state == TileData::State::parsed) {
painter.renderTileLayer(*it->second, layer_desc, matrix);
@@ -183,9 +184,9 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) {
if (!new_tile.data) {
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
if (info->type == SourceType::Vector) {
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, info);
} else if (info.type == SourceType::Raster) {
} else if (info->type == SourceType::Raster) {
new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, info);
} else {
throw std::runtime_error("source type not implemented");
@@ -199,7 +200,7 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) {
}
double Source::getZoom(const TransformState& state) const {
double offset = std::log(util::tileSize / info.tile_size) / std::log(2);
double offset = std::log(util::tileSize / info->tile_size) / std::log(2);
offset += (state.getPixelRatio() > 1.0 ? 1 :0);
return state.getZoom() + offset;
}
@@ -211,8 +212,8 @@ int32_t Source::coveringZoomLevel(const TransformState& state) const {
std::forward_list<Tile::ID> Source::coveringTiles(const TransformState& state) const {
int32_t z = coveringZoomLevel(state);
if (z < info.min_zoom) return {{}};
if (z > info.max_zoom) z = info.max_zoom;
if (z < info->min_zoom) return {{}};
if (z > info->max_zoom) z = info->max_zoom;
// Map four viewport corners to pixel coordinates
box points = state.cornersToBox(z);
@@ -285,8 +286,8 @@ bool Source::updateTiles(Map &map) {
std::forward_list<Tile::ID> required = coveringTiles(map.getState());
// Determine the overzooming/underzooming amounts.
int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom);
int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info.min_zoom, info.max_zoom);
int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info->min_zoom, info->max_zoom);
int32_t maxCoveringZoom = util::clamp<int32_t>(zoom + 1, info->min_zoom, info->max_zoom);
// Retain is a list of tiles that we shouldn't delete, even if they are not
// the most ideal tile for the current viewport. This may include tiles like
@@ -333,7 +334,7 @@ bool Source::updateTiles(Map &map) {
// Remove all the expired pointers from the set.
util::erase_if(tile_data, [&retain_data](std::pair<const Tile::ID, std::weak_ptr<TileData>> &pair) {
const std::shared_ptr<TileData> tile = pair.second.lock();
const util::ptr<TileData> tile = pair.second.lock();
if (!tile) {
return true;
}

View File

@@ -5,7 +5,7 @@
#include <string>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/filesource.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
@@ -22,8 +22,8 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t
sdf(sdf) {
}
std::shared_ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource) {
std::shared_ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const util::ptr<FileSource> &fileSource) {
util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
sprite->load(fileSource);
return sprite;
}
@@ -51,7 +51,7 @@ Sprite::operator bool() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
void Sprite::load(const std::shared_ptr<FileSource> &fileSource) {
void Sprite::load(const util::ptr<FileSource> &fileSource) {
if (!valid) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loadedImage = true;
@@ -60,30 +60,30 @@ void Sprite::load(const std::shared_ptr<FileSource> &fileSource) {
return;
}
std::shared_ptr<Sprite> sprite = shared_from_this();
util::ptr<Sprite> sprite = shared_from_this();
fileSource->load(ResourceType::JSON, jsonURL, [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->body.swap(res->body);
fileSource->request(ResourceType::JSON, jsonURL)->onload([sprite](const Response &res) {
if (res.code == 200) {
sprite->body = res.data;
sprite->parseJSON();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res->code, res->error_message.c_str());
Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res.code, res.message.c_str());
if (!sprite->future.valid()) {
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
}
});
fileSource->load(ResourceType::Image, spriteURL, [sprite](platform::Response *res) {
if (res->code == 200) {
sprite->image.swap(res->body);
fileSource->request(ResourceType::Image, spriteURL)->onload([sprite](const Response &res) {
if (res.code == 200) {
sprite->image = res.data;
sprite->parseImage();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res->code, res->error_message.c_str());
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res.code, res.message.c_str());
if (!sprite->future.valid()) {
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message)));
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
}
});

View File

@@ -4,12 +4,12 @@
#include <mbgl/util/token.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/filesource.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/uv_detail.hpp>
using namespace mbgl;
TileData::TileData(Tile::ID id, Map &map, const SourceInfo &source)
TileData::TileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: id(id),
state(State::initial),
map(map),
@@ -29,10 +29,10 @@ const std::string TileData::toString() const {
}
void TileData::request() {
if (source.tiles.empty())
if (source->tiles.empty())
return;
std::string url = source.tiles[(id.x + id.y) % source.tiles.size()];
std::string url = source->tiles[(id.x + id.y) % source->tiles.size()];
url = util::replaceTokens(url, [&](const std::string &token) -> std::string {
if (token == "z") return std::to_string(id.z);
if (token == "x") return std::to_string(id.x);
@@ -45,21 +45,28 @@ void TileData::request() {
// Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
map.getFileSource()->load(ResourceType::Tile, url, [weak_tile, url](platform::Response *res) {
std::shared_ptr<TileData> tile = weak_tile.lock();
req = map.getFileSource()->request(ResourceType::Tile, url);
req->onload([weak_tile, url](const Response &res) {
util::ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
// to drop to zero for destruction.
} else if (res->code == 200) {
return;
}
// Clear the request object.
tile->req.reset();
if (res.code == 200) {
tile->state = State::loaded;
tile->data.swap(res->body);
tile->data = res.data;
// Schedule tile parsing in another thread
tile->reparse();
} else {
#if defined(DEBUG)
fprintf(stderr, "[%s] tile loading failed: %d, %s\n", url.c_str(), res->code, res->error_message.c_str());
fprintf(stderr, "[%s] tile loading failed: %ld, %s\n", url.c_str(), res.code, res.message.c_str());
#endif
}
});
@@ -68,7 +75,7 @@ void TileData::request() {
void TileData::cancel() {
if (state != State::obsolete) {
state = State::obsolete;
platform::cancel_request_http(req.lock());
req.reset();
}
}
@@ -79,12 +86,12 @@ void TileData::reparse() {
// We're creating a new work request. The work request deletes itself after it executed
// the after work handler
new uv::work<std::shared_ptr<TileData>>(
map.getLoop(),
[](std::shared_ptr<TileData> &tile) {
new uv::work<util::ptr<TileData>>(
map.getWorker(),
[](util::ptr<TileData> &tile) {
tile->parse();
},
[](std::shared_ptr<TileData> &tile) {
[](util::ptr<TileData> &tile) {
tile->afterParse();
tile->map.update();
},

View File

@@ -38,19 +38,26 @@ namespace mbgl {
TileParser::~TileParser() = default;
TileParser::TileParser(const std::string &data, VectorTileData &tile,
const std::shared_ptr<const Style> &style,
const std::shared_ptr<GlyphAtlas> &glyphAtlas,
const std::shared_ptr<GlyphStore> &glyphStore,
const std::shared_ptr<SpriteAtlas> &spriteAtlas,
const std::shared_ptr<Sprite> &sprite)
const util::ptr<const Style> &style_,
const util::ptr<GlyphAtlas> &glyphAtlas_,
const util::ptr<GlyphStore> &glyphStore_,
const util::ptr<SpriteAtlas> &spriteAtlas_,
const util::ptr<Sprite> &sprite_)
: vector_data(pbf((const uint8_t *)data.data(), data.size())),
tile(tile),
style(style),
glyphAtlas(glyphAtlas),
glyphStore(glyphStore),
spriteAtlas(spriteAtlas),
sprite(sprite),
collision(std::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)) {
style(style_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
spriteAtlas(spriteAtlas_),
sprite(sprite_),
collision(std::make_unique<Collision>(tile.id.z, 4096, tile.source->tile_size, tile.depth)) {
assert(&tile != nullptr);
assert(style);
assert(glyphAtlas);
assert(glyphStore);
assert(spriteAtlas);
assert(sprite);
assert(collision);
}
void TileParser::parse() {
@@ -59,12 +66,12 @@ void TileParser::parse() {
bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; }
void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
void TileParser::parseStyleLayers(util::ptr<StyleLayerGroup> group) {
if (!group) {
return;
}
for (const std::shared_ptr<StyleLayer> &layer_desc : group->layers) {
for (const util::ptr<StyleLayer> &layer_desc : group->layers) {
// Cancel early when parsing.
if (obsolete()) {
return;
@@ -96,14 +103,14 @@ void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) {
}
}
std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bucket_desc) {
std::unique_ptr<Bucket> TileParser::createBucket(util::ptr<StyleBucket> bucket_desc) {
if (!bucket_desc) {
fprintf(stderr, "missing bucket desc\n");
return nullptr;
}
// Skip this bucket if we are to not render this
if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source.max_zoom) return nullptr;
if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source->max_zoom) return nullptr;
if (tile.id.z >= std::ceil(bucket_desc->max_zoom)) return nullptr;
auto layer_it = vector_data.layers.find(bucket_desc->source_layer);
@@ -157,7 +164,7 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& laye
return obsolete() ? nullptr : std::move(bucket);
}
std::unique_ptr<Bucket> TileParser::createRasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster &raster) {
std::unique_ptr<Bucket> TileParser::createRasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster &raster) {
std::unique_ptr<RasterBucket> bucket = std::make_unique<RasterBucket>(texturepool, raster);
return obsolete() ? nullptr : std::move(bucket);
}

View File

@@ -466,7 +466,7 @@ bool Transform::needsTransition() const {
void Transform::updateTransitions(const timestamp now) {
uv::writelock lock(mtx);
transitions.remove_if([now](const std::shared_ptr<util::transition> &transition) {
transitions.remove_if([now](const util::ptr<util::transition> &transition) {
return transition->update(now) == util::transition::complete;
});
}

View File

@@ -8,13 +8,13 @@
using namespace mbgl;
VectorTileData::VectorTileData(Tile::ID id, Map &map, const SourceInfo &source)
VectorTileData::VectorTileData(Tile::ID id, Map &map, const util::ptr<SourceInfo> &source)
: TileData(id, map, source),
depth(id.z >= source.max_zoom ? map.getMaxZoom() - id.z : 1) {
depth(id.z >= source->max_zoom ? map.getMaxZoom() - id.z : 1) {
}
VectorTileData::~VectorTileData() {
std::shared_ptr<GlyphAtlas> glyphAtlas = map.getGlyphAtlas();
util::ptr<GlyphAtlas> glyphAtlas = map.getGlyphAtlas();
if (glyphAtlas) {
glyphAtlas->removeGlyphs(id.to_uint64());
}
@@ -52,7 +52,7 @@ void VectorTileData::afterParse() {
parser.reset();
}
void VectorTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {
@@ -62,7 +62,7 @@ void VectorTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_
}
}
bool VectorTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const {
bool VectorTileData::hasData(util::ptr<StyleLayer> layer_desc) const {
if (state == State::parsed && layer_desc->bucket) {
auto databucket_it = buckets.find(layer_desc->bucket->name);
if (databucket_it != buckets.end()) {

View File

@@ -1,54 +0,0 @@
#include <mbgl/platform/request.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/uv_detail.hpp>
using namespace mbgl::platform;
Request::Request(const std::string &url,
std::function<void(Response *)> callback,
std::shared_ptr<uv::loop> loop)
: url(url),
res(std::make_unique<Response>(callback)),
cancelled(false),
loop(loop) {
if (loop) {
// Add a check handle without a callback to keep the default loop running.
// We don't have a real handler attached to the default loop right from the
// beginning, because we're using asynchronous messaging to perform the actual
// request in the request thread. Only after the request is complete, we
// create an actual work request that is attached to the default loop.
async = new uv_async_t();
async->data = new std::unique_ptr<Response>();
uv_async_init(**loop, async, &complete);
}
}
Request::~Request() {
}
void Request::complete() {
if (loop) {
// We're scheduling the response callback to be invoked in the event loop.
// Since the Request object will be deleted before the callback is invoked,
// we move over the Response object to be owned by the async handle.
((std::unique_ptr<Response> *)async->data)->swap(res);
uv_async_send(async);
} else {
// We're calling the response callback immediately. We're currently on an
// arbitrary thread, but that's okay.
res->callback(res.get());
}
}
void Request::complete(uv_async_t *async, int status) {
Response *res = static_cast<std::unique_ptr<Response> *>(async->data)->get();
res->callback(res);
// We need to remove our async handle again to allow the main event loop to exit.
uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
delete static_cast<std::unique_ptr<Response> *>(handle->data);
delete (uv_async_t *)handle;
});
}

View File

@@ -14,7 +14,7 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer)
: fontBuffer(fontBuffer) {
}
void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) {
void DebugBucket::render(Painter& painter, util::ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) {
painter.renderDebugText(*this, matrix);
}

View File

@@ -204,7 +204,7 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
void FillBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
void FillBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
painter.renderFill(*this, layer_desc, id, matrix);
}

View File

@@ -341,7 +341,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
}
void LineBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
void LineBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
painter.renderLine(*this, layer_desc, id, matrix);
}

View File

@@ -197,7 +197,7 @@ void Painter::prepareTile(const Tile& tile) {
glStencilFunc(GL_EQUAL, ref, mask);
}
void Painter::renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> layer_desc, const mat4 &matrix) {
void Painter::renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) {
assert(tile.data);
if (tile.data->hasData(layer_desc) || layer_desc->type == StyleLayerType::Raster) {
gl::group group(util::sprintf<32>("render %d/%d/%d\n", tile.id.z, tile.id.y, tile.id.z));
@@ -206,7 +206,7 @@ void Painter::renderTileLayer(const Tile& tile, std::shared_ptr<StyleLayer> laye
}
}
void Painter::renderBackground(std::shared_ptr<StyleLayer> layer_desc) {
void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) {
const BackgroundProperties& properties = layer_desc->getProperties<BackgroundProperties>();
Color color = properties.color;

View File

@@ -6,7 +6,7 @@
using namespace mbgl;
void Painter::drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &sources) {
void Painter::drawClippingMasks(const std::set<util::ptr<StyleSource>> &sources) {
gl::group group("clipping masks");
useProgram(plainShader->program);
@@ -17,7 +17,7 @@ void Painter::drawClippingMasks(const std::set<std::shared_ptr<StyleSource>> &so
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
for (const std::shared_ptr<StyleSource> &source : sources) {
for (const util::ptr<StyleSource> &source : sources) {
source->source->drawClippingMasks(*this);
}

View File

@@ -10,7 +10,7 @@
using namespace mbgl;
void Painter::renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (!bucket.hasData()) return;

View File

@@ -8,7 +8,7 @@
using namespace mbgl;
void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
if (!bucket.hasData()) return;
@@ -62,7 +62,7 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d
bucket.drawPoints(*linejoinShader);
}
const std::shared_ptr<Sprite> &sprite = map.getSprite();
const util::ptr<Sprite> &sprite = map.getSprite();
if (properties.image.size() && sprite) {
SpriteAtlasPosition imagePos = map.getSpriteAtlas()->getPosition(properties.image, *sprite);

View File

@@ -9,7 +9,7 @@
using namespace mbgl;
void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) {
if (pass != RenderPass::Translucent) return;
const RasterProperties &properties = layer_desc->getProperties<RasterProperties>();
@@ -34,7 +34,7 @@ void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> lay
// call updateTiles to get parsed data for sublayers
map.updateTiles();
for (const std::shared_ptr<StyleLayer> &layer : layer_desc->layers->layers) {
for (const util::ptr<StyleLayer> &layer : layer_desc->layers->layers) {
setOpaque();
map.renderLayer(layer, RenderPass::Opaque, &id, &preMatrix);
setTranslucent();

View File

@@ -112,7 +112,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
}
}
void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
void Painter::renderSymbol(SymbolBucket &bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) {
return;

View File

@@ -3,13 +3,13 @@
using namespace mbgl;
RasterBucket::RasterBucket(const std::shared_ptr<Texturepool> &texturepool, const StyleBucketRaster& properties)
RasterBucket::RasterBucket(const util::ptr<Texturepool> &texturepool, const StyleBucketRaster& properties)
: properties(properties),
texture(properties),
raster(texturepool) {
}
void RasterBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
void RasterBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) {
painter.renderRaster(*this, layer_desc, id, matrix);
}

View File

@@ -22,7 +22,7 @@ namespace mbgl {
SymbolBucket::SymbolBucket(const StyleBucketSymbol &properties, Collision &collision)
: properties(properties), collision(collision) {}
void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
void SymbolBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc,
const Tile::ID &id, const mat4 &matrix) {
painter.renderSymbol(*this, layer_desc, id, matrix);
}

View File

@@ -0,0 +1,86 @@
#include <mbgl/storage/base_request.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/request.hpp>
#include <uv.h>
#include <cassert>
namespace mbgl {
template <typename T, typename ...Args>
void invoke(const std::forward_list<std::unique_ptr<Callback>> &list, Args&& ...args) {
for (const std::unique_ptr<Callback> &callback : list) {
assert(callback);
if (callback->is<T>()) {
callback->get<T>()(::std::forward<Args>(args)...);
}
}
}
BaseRequest::BaseRequest(const std::string &path_) : thread_id(uv_thread_self()), path(path_) {
}
// A base request can only be "canceled" by destroying the object. In that case, we'll have to
// notify all cancel callbacks.
BaseRequest::~BaseRequest() {
assert(thread_id == uv_thread_self());
notify();
}
void BaseRequest::retryImmediately() {
// no-op. override in child class.
}
void BaseRequest::notify() {
assert(thread_id == uv_thread_self());
// The parameter exists solely so that any calls to ->remove()
// are not going to cause deallocation of this object while this call is in progress.
util::ptr<BaseRequest> retain = self;
// Swap the lists so that it's safe for callbacks to call ->cancel()
// on the request object, which would modify the list.
const std::forward_list<std::unique_ptr<Callback>> list = std::move(callbacks);
callbacks.clear();
if (response) {
invoke<CompletedCallback>(list, *response);
} else {
invoke<AbortedCallback>(list);
}
self.reset();
}
Callback *BaseRequest::add(Callback &&callback, const util::ptr<BaseRequest> &request) {
assert(thread_id == uv_thread_self());
assert(this == request.get());
if (response) {
// We already have a response. Notify right away.
if (callback.is<CompletedCallback>()) {
callback.get<CompletedCallback>()(*response);
} else {
// We already know that this request was successful. The AbortedCallback will be discarded
// here since it would never be called.
}
return nullptr;
} else {
self = request;
callbacks.push_front(std::unique_ptr<Callback>(new Callback(std::move(callback))));
return callbacks.front().get();
}
}
void BaseRequest::remove(Callback *callback) {
assert(thread_id == uv_thread_self());
callbacks.remove_if([=](const std::unique_ptr<Callback> &cb) {
return cb.get() == callback;
});
if (callbacks.empty()) {
self.reset();
}
}
}

View File

@@ -0,0 +1,37 @@
#include <mbgl/storage/file_request.hpp>
#include <mbgl/storage/file_request_baton.hpp>
#include <mbgl/storage/response.hpp>
#include <uv.h>
#include <cassert>
#include <unistd.h>
namespace mbgl {
FileRequest::FileRequest(const std::string &path_, uv_loop_t *loop)
: BaseRequest(path_), ptr(new FileRequestBaton(this, path, loop)) {
}
void FileRequest::cancel() {
assert(thread_id == uv_thread_self());
if (ptr) {
ptr->cancel();
// When deleting a FileRequest object with a uv_fs_* call is in progress, we are making sure
// that the callback doesn't accidentally reference this object again.
ptr->request = nullptr;
ptr = nullptr;
}
notify();
}
FileRequest::~FileRequest() {
assert(thread_id == uv_thread_self());
cancel();
}
}

View File

@@ -0,0 +1,142 @@
#include <mbgl/storage/file_request_baton.hpp>
#include <mbgl/storage/file_request.hpp>
#include <mbgl/storage/response.hpp>
namespace mbgl {
FileRequestBaton::FileRequestBaton(FileRequest *request_, const std::string &path, uv_loop_t *loop)
: thread_id(uv_thread_self()), request(request_) {
req.data = this;
uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, file_opened);
}
FileRequestBaton::~FileRequestBaton() {
}
void FileRequestBaton::cancel() {
canceled = true;
// uv_cancel fails frequently when the request has already been started.
// In that case, we have to let it complete and check the canceled bool
// instead.
uv_cancel((uv_req_t *)&req);
}
void FileRequestBaton::notify_error(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (ptr->request && req->result < 0 && !ptr->canceled && req->result != UV_ECANCELED) {
ptr->request->response = std::unique_ptr<Response>(new Response);
ptr->request->response->code = req->result == UV_ENOENT ? 404 : 500;
ptr->request->response->message = uv_strerror(int(req->result));
ptr->request->notify();
}
}
void FileRequestBaton::file_opened(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (req->result < 0) {
// Opening failed or was canceled. There isn't much left we can do.
notify_error(req);
cleanup(req);
} else {
const uv_file fd = uv_file(req->result);
// We're going to reuse this handle, so we need to cleanup first.
uv_fs_req_cleanup(req);
if (ptr->canceled || !ptr->request) {
// Either the FileRequest object has been destructed, or the
// request was canceled.
uv_fs_close(req->loop, req, fd, file_closed);
} else {
ptr->fd = fd;
uv_fs_fstat(req->loop, req, fd, file_stated);
}
}
}
void FileRequestBaton::file_stated(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (req->result < 0 || ptr->canceled || !ptr->request) {
// Stating failed or was canceled. We already have an open file handle
// though, which we'll have to close.
notify_error(req);
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, ptr->fd, file_closed);
} else {
if (static_cast<const uv_stat_t *>(req->ptr)->st_size > std::numeric_limits<int>::max()) {
// File is too large for us to open this way because uv_buf's only support unsigned
// ints as maximum size.
if (ptr->request) {
ptr->request->response = std::unique_ptr<Response>(new Response);
ptr->request->response->code = UV_EFBIG;
ptr->request->response->message = uv_strerror(UV_EFBIG);
ptr->request->notify();
}
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, ptr->fd, file_closed);
} else {
const unsigned int size =
(unsigned int)(static_cast<const uv_stat_t *>(req->ptr)->st_size);
ptr->body.resize(size);
ptr->buffer = uv_buf_init(const_cast<char *>(ptr->body.data()), size);
uv_fs_req_cleanup(req);
uv_fs_read(req->loop, req, ptr->fd, &ptr->buffer, 1, 0, file_read);
}
}
}
void FileRequestBaton::file_read(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (req->result < 0 || ptr->canceled || !ptr->request) {
// Stating failed or was canceled. We already have an open file handle
// though, which we'll have to close.
notify_error(req);
} else {
// File was successfully read.
if (ptr->request) {
ptr->request->response = std::unique_ptr<Response>(new Response);
ptr->request->response->code = 200;
ptr->request->response->data = std::move(ptr->body);
ptr->request->notify();
}
}
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, ptr->fd, file_closed);
}
void FileRequestBaton::file_closed(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (req->result < 0) {
// Closing the file failed. But there isn't anything we can do.
}
cleanup(req);
}
void FileRequestBaton::cleanup(uv_fs_t *req) {
FileRequestBaton *ptr = (FileRequestBaton *)req->data;
assert(ptr->thread_id == uv_thread_self());
if (ptr->request) {
ptr->request->ptr = nullptr;
}
uv_fs_req_cleanup(req);
delete ptr;
}
}

105
src/storage/file_source.cpp Normal file
View File

@@ -0,0 +1,105 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/file_request.hpp>
#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/sqlite_store.hpp>
#include <mbgl/util/uv-messenger.h>
#include <uv.h>
namespace mbgl {
FileSource::FileSource(uv_loop_t *loop_, const std::string &path)
: thread_id(uv_thread_self()),
store(!path.empty() ? util::ptr<SQLiteStore>(new SQLiteStore(loop_, path)) : nullptr),
loop(loop_),
queue(new uv_messenger_t) {
uv_messenger_init(loop, queue, [](void *ptr) {
std::unique_ptr<std::function<void()>> fn { reinterpret_cast<std::function<void()> *>(ptr) };
(*fn)();
});
uv_unref((uv_handle_t *)&queue->async);
}
FileSource::~FileSource() {
assert(thread_id == uv_thread_self());
uv_messenger_stop(queue);
// NOTE: We don't need to delete the messenger since it will be deleted by the
// uv_messenger_stop() function.
util::ptr<BaseRequest> request;
// Send a cancel() message to all requests that we are still holding.
for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
if ((request = pair.second.lock())) {
request->cancel();
}
}
}
void FileSource::setBase(const std::string &value) {
assert(thread_id == uv_thread_self());
base = value;
}
const std::string &FileSource::getBase() const {
assert(thread_id == uv_thread_self());
return base;
}
std::unique_ptr<Request> FileSource::request(ResourceType type, const std::string &url) {
assert(thread_id == uv_thread_self());
// Make URL absolute.
const std::string absoluteURL = [&]() -> std::string {
const size_t separator = url.find("://");
if (separator == std::string::npos) {
// Relative URL.
return base + url;
} else {
return url;
}
}();
util::ptr<BaseRequest> request;
// First, try to find an existing Request object.
auto it = pending.find(absoluteURL);
if (it != pending.end()) {
request = it->second.lock();
}
if (!request) {
if (absoluteURL.substr(0, 7) == "file://") {
request = std::make_shared<FileRequest>(absoluteURL.substr(7), loop);
} else {
request = std::make_shared<HTTPRequest>(type, absoluteURL, loop, store);
}
pending.emplace(absoluteURL, request);
}
return std::unique_ptr<Request>(new Request(request));
}
void FileSource::prepare(std::function<void()> fn) {
if (thread_id == uv_thread_self()) {
fn();
} else {
uv_messenger_send(queue, new std::function<void()>(std::move(fn)));
}
}
void FileSource::retryAllPending() {
assert(thread_id == uv_thread_self());
util::ptr<BaseRequest> request;
for (const std::pair<std::string, std::weak_ptr<BaseRequest>> &pair : pending) {
if ((request = pair.second.lock())) {
request->retryImmediately();
}
}
}
}

View File

@@ -0,0 +1,270 @@
#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/sqlite_store.hpp>
#include <mbgl/storage/http_request_baton.hpp>
#include <uv.h>
#include <cassert>
#include <chrono>
namespace mbgl {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wglobal-constructors"
struct CacheRequestBaton {
HTTPRequest *request = nullptr;
std::string path;
util::ptr<SQLiteStore> store;
};
HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path, uv_loop_t *loop_, util::ptr<SQLiteStore> store_)
: BaseRequest(path), thread_id(uv_thread_self()), loop(loop_), store(store_), type(type_) {
if (store) {
startCacheRequest();
} else {
startHTTPRequest(nullptr);
}
}
void HTTPRequest::startCacheRequest() {
assert(uv_thread_self() == thread_id);
cache_baton = new CacheRequestBaton;
cache_baton->request = this;
cache_baton->path = path;
cache_baton->store = store;
store->get(path, [](std::unique_ptr<Response> &&response, void *ptr) {
// Wrap in a unique_ptr, so it'll always get auto-destructed.
std::unique_ptr<CacheRequestBaton> baton((CacheRequestBaton *)ptr);
if (baton->request) {
baton->request->cache_baton = nullptr;
baton->request->handleCacheResponse(std::move(response));
}
}, cache_baton);
}
void HTTPRequest::handleCacheResponse(std::unique_ptr<Response> &&res) {
assert(uv_thread_self() == thread_id);
if (res) {
// This entry was stored in the cache. Now determine if we need to revalidate.
const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
if (res->expires > now) {
response = std::move(res);
notify();
// Note: after calling notify(), the request object may cease to exist.
// This HTTPRequest is completed.
return;
} else {
// TODO: notify with preliminary results.
}
}
startHTTPRequest(std::move(res));
}
void HTTPRequest::startHTTPRequest(std::unique_ptr<Response> &&res) {
assert(uv_thread_self() == thread_id);
assert(!http_baton);
http_baton = std::make_shared<HTTPRequestBaton>(path);
http_baton->request = this;
http_baton->async = new uv_async_t;
http_baton->response = std::move(res);
http_baton->async->data = new util::ptr<HTTPRequestBaton>(http_baton);
uv_async_init(loop, http_baton->async, [](uv_async_t *async) {
util::ptr<HTTPRequestBaton> &http_baton = *(util::ptr<HTTPRequestBaton> *)async->data;
if (http_baton->request) {
HTTPRequest *request = http_baton->request;
request->http_baton.reset();
http_baton->request = nullptr;
request->handleHTTPResponse(http_baton->type, std::move(http_baton->response));
}
delete (util::ptr<HTTPRequestBaton> *)async->data;
uv_close((uv_handle_t *)async, [](uv_handle_t *handle) {
uv_async_t *async = (uv_async_t *)handle;
delete async;
});
});
attempts++;
HTTPRequestBaton::start(http_baton);
}
void HTTPRequest::handleHTTPResponse(HTTPResponseType responseType, std::unique_ptr<Response> &&res) {
assert(uv_thread_self() == thread_id);
assert(!http_baton);
assert(!response);
switch (responseType) {
// This error was caused by a temporary error and it is likely that it will be resolved
// immediately. We are going to try again right away. This is like the TemporaryError,
// except that we will not perform exponential back-off.
case HTTPResponseType::SingularError:
if (attempts >= 4) {
// Report as error after 4 attempts.
response = std::move(res);
notify();
} else if (attempts >= 2) {
// Switch to the back-off algorithm after the second failure.
retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
return;
} else {
startHTTPRequest(std::move(res));
}
break;
// This error might be resolved by waiting some time (e.g. server issues).
// We are going to do an exponential back-off and will try again in a few seconds.
case HTTPResponseType::TemporaryError:
if (attempts >= 4) {
// Report error back after it failed completely.
response = std::move(res);
notify();
} else {
retryHTTPRequest(std::move(res), (1 << attempts) * 1000);
}
break;
// This error might be resolved once the network reachability status changes.
// We are going to watch the network status for changes and will retry as soon as the
// operating system notifies us of a network status change.
case HTTPResponseType::ConnectionError:
if (attempts >= 4) {
// Report error back after it failed completely.
response = std::move(res);
notify();
} else {
// By default, we will retry every 60 seconds.
retryHTTPRequest(std::move(res), 60000);
}
break;
// The request was canceled programatically.
case HTTPResponseType::Canceled:
response.reset();
notify();
break;
// This error probably won't be resolved by retrying anytime soon. We are giving up.
case HTTPResponseType::PermanentError:
response = std::move(res);
notify();
break;
// The request returned data successfully. We retrieved and decoded the data successfully.
case HTTPResponseType::Successful:
if (store) {
store->put(path, type, *res);
}
response = std::move(res);
notify();
break;
// The request confirmed that the data wasn't changed. We already have the data.
case HTTPResponseType::NotModified:
if (store) {
store->updateExpiration(path, res->expires);
}
response = std::move(res);
notify();
break;
default:
assert(!"Response wasn't set");
break;
}
}
using RetryBaton = std::pair<HTTPRequest *, std::unique_ptr<Response>>;
void HTTPRequest::retryHTTPRequest(std::unique_ptr<Response> &&res, uint64_t timeout) {
assert(uv_thread_self() == thread_id);
assert(!backoff_timer);
backoff_timer = new uv_timer_t();
uv_timer_init(loop, backoff_timer);
backoff_timer->data = new RetryBaton(this, std::move(res));
uv_timer_start(backoff_timer, [](uv_timer_t *timer) {
std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(timer->data) };
pair->first->startHTTPRequest(std::move(pair->second));
pair->first->backoff_timer = nullptr;
uv_timer_stop(timer);
uv_close((uv_handle_t *)timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
}, timeout, 0);
}
void HTTPRequest::removeHTTPBaton() {
assert(uv_thread_self() == thread_id);
if (http_baton) {
http_baton->request = nullptr;
HTTPRequestBaton::stop(http_baton);
http_baton.reset();
}
}
void HTTPRequest::removeCacheBaton() {
assert(uv_thread_self() == thread_id);
if (cache_baton) {
// Make sre that this object doesn't accidentally get accessed when it is destructed before
// the callback returned. They are being run in the same thread, so just setting it to
// null is sufficient.
// Note: We don't manually delete the CacheRequestBaton since it'll be deleted by the
// callback.
cache_baton->request = nullptr;
cache_baton = nullptr;
}
}
void HTTPRequest::removeBackoffTimer() {
assert(uv_thread_self() == thread_id);
if (backoff_timer) {
delete static_cast<RetryBaton *>(backoff_timer->data);
uv_timer_stop(backoff_timer);
uv_close((uv_handle_t *)backoff_timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
backoff_timer = nullptr;
}
}
void HTTPRequest::retryImmediately() {
assert(uv_thread_self() == thread_id);
if (!cache_baton && !http_baton) {
if (backoff_timer) {
// Retry immediately.
uv_timer_stop(backoff_timer);
std::unique_ptr<RetryBaton> pair { static_cast<RetryBaton *>(backoff_timer->data) };
assert(pair->first == this);
startHTTPRequest(std::move(pair->second));
uv_close((uv_handle_t *)backoff_timer, [](uv_handle_t *handle) { delete (uv_timer_t *)handle; });
backoff_timer = nullptr;
} else {
assert(!"We should always have a backoff_timer when there are no batons");
}
}
}
void HTTPRequest::cancel() {
assert(uv_thread_self() == thread_id);
removeCacheBaton();
removeHTTPBaton();
removeBackoffTimer();
notify();
}
HTTPRequest::~HTTPRequest() {
assert(uv_thread_self() == thread_id);
cancel();
}
#pragma clang diagnostic pop
}

View File

@@ -0,0 +1,12 @@
#include <mbgl/storage/http_request_baton.hpp>
#include <uv.h>
namespace mbgl {
HTTPRequestBaton::HTTPRequestBaton(const std::string &path_) : thread_id(uv_thread_self()), path(path_) {
}
HTTPRequestBaton::~HTTPRequestBaton() {
}
}

49
src/storage/request.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <mbgl/storage/request.hpp>
#include <mbgl/storage/base_request.hpp>
#include <uv.h>
#include <cassert>
namespace mbgl {
Request::Request(const util::ptr<BaseRequest> &base_)
: thread_id(uv_thread_self()), base(base_) {
}
Request::~Request() {
assert(thread_id == uv_thread_self());
}
void Request::onload(CompletedCallback cb) {
assert(thread_id == uv_thread_self());
if (base) {
Callback *callback = base->add(std::move(cb), base);
if (callback) {
callbacks.push_front(callback);
}
}
}
void Request::oncancel(AbortedCallback cb) {
assert(thread_id == uv_thread_self());
if (base) {
Callback *callback = base->add(std::move(cb), base);
if (callback) {
callbacks.push_front(callback);
}
}
}
void Request::cancel() {
assert(thread_id == uv_thread_self());
if (base) {
for (Callback *callback : callbacks) {
base->remove(callback);
}
base.reset();
}
callbacks.clear();
}
}

22
src/storage/response.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include <mbgl/storage/response.hpp>
#include <chrono>
namespace mbgl {
int64_t Response::parseCacheControl(const char *value) {
if (value) {
uint64_t seconds = 0;
// TODO: cache-control may contain other information as well:
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
if (std::sscanf(value, "max-age=%llu", &seconds) == 1) {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count() +
seconds;
}
}
return -1;
}
}

Some files were not shown because too many files have changed in this diff Show More