diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2015-04-28 14:31:00 -0400 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2015-04-28 19:26:35 -0400 |
commit | 481fa0e69ff8d84202e91a1643c72636200f1b80 (patch) | |
tree | a2679fd559046d2c7e6f64cb5c4a67e29e16bf66 | |
parent | 2c3952af66d6907f8a892d42d0034e91126b073a (diff) | |
download | qtlocation-mapboxgl-481fa0e69ff8d84202e91a1643c72636200f1b80.tar.gz |
Restructure *Request and Context
The previous implementation, based on thread-local storage, did not
ensure that the context was destructed before the FileSource run loop.
This resulted in implementations attempting to uv_close handles for a
loop that had already been destroyed.
This change also fixes #1262.
-rw-r--r-- | platform/darwin/http_request_nsurl.mm | 140 | ||||
-rw-r--r-- | platform/default/asset_request_fs.cpp | 106 | ||||
-rw-r--r-- | platform/default/asset_request_zip.cpp | 138 | ||||
-rw-r--r-- | platform/default/http_request_curl.cpp | 151 | ||||
-rw-r--r-- | src/mbgl/storage/asset_context.hpp | 23 | ||||
-rw-r--r-- | src/mbgl/storage/asset_request.hpp | 27 | ||||
-rw-r--r-- | src/mbgl/storage/default_file_source.cpp | 12 | ||||
-rw-r--r-- | src/mbgl/storage/default_file_source_impl.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/storage/http_context.cpp | 30 | ||||
-rw-r--r-- | src/mbgl/storage/http_context.hpp | 72 | ||||
-rw-r--r-- | src/mbgl/storage/http_request.hpp | 28 | ||||
-rw-r--r-- | src/mbgl/storage/request_base.hpp | 1 | ||||
-rw-r--r-- | src/mbgl/storage/thread_context.hpp | 78 | ||||
-rw-r--r-- | src/mbgl/util/uv_detail.hpp | 4 |
14 files changed, 311 insertions, 503 deletions
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm index f5e7888f57..e3f0b5dad6 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -1,9 +1,8 @@ -#include <mbgl/storage/http_request.hpp> #include <mbgl/storage/http_context.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> -#include <mbgl/util/uv.hpp> +#include <mbgl/util/std.hpp> #include <mbgl/util/time.hpp> #include <mbgl/util/parsedate.h> @@ -46,25 +45,29 @@ enum class ResponseStatus : uint8_t { class HTTPNSURLContext; -class HTTPRequestImpl { +class HTTPRequest : public RequestBase { public: - HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response); - ~HTTPRequestImpl(); + HTTPRequest(HTTPNSURLContext*, + const Resource&, + Callback, + uv_loop_t*, + std::shared_ptr<const Response>); + ~HTTPRequest(); - void cancel(); - void cancelTimer(); + void cancel() override; + void retry() override; +private: + void cancelTimer(); void start(); void handleResult(NSData *data, NSURLResponse *res, NSError *error); void handleResponse(); - void retry(uint64_t timeout); - void retryImmediately(); + static void restart(uv_timer_t *timer, int); -private: HTTPNSURLContext *context = nullptr; - HTTPRequest *request = nullptr; + bool cancelled = false; NSURLSessionDataTask *task = nullptr; std::unique_ptr<Response> response; const std::shared_ptr<const Response> existingResponse; @@ -79,18 +82,20 @@ private: // ------------------------------------------------------------------------------------------------- -class HTTPNSURLContext : public HTTPContext<HTTPNSURLContext> { +class HTTPNSURLContext : public HTTPContext { public: HTTPNSURLContext(uv_loop_t *loop); ~HTTPNSURLContext(); + RequestBase* createRequest(const Resource&, + RequestBase::Callback, + uv_loop_t*, + std::shared_ptr<const Response>) override; + NSURLSession *session = nil; NSString *userAgent = nil; }; -template<> pthread_key_t ThreadContext<HTTPNSURLContext>::key{}; -template<> pthread_once_t ThreadContext<HTTPNSURLContext>::once = PTHREAD_ONCE_INIT; - HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) { @autoreleasepool { NSURLSessionConfiguration *sessionConfig = @@ -116,34 +121,51 @@ HTTPNSURLContext::~HTTPNSURLContext() { userAgent = nullptr; } +RequestBase* HTTPNSURLContext::createRequest(const Resource& resource, + RequestBase::Callback callback, + uv_loop_t* loop, + std::shared_ptr<const Response> response) { + return new HTTPRequest(this, resource, callback, loop, response); +} + // ------------------------------------------------------------------------------------------------- -HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, - std::shared_ptr<const Response> existingResponse_) - : context(HTTPNSURLContext::Get(loop)), - request(request_), +HTTPRequest::HTTPRequest(HTTPNSURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t *loop, std::shared_ptr<const Response> existingResponse_) + : RequestBase(resource_, callback_), + context(context_), existingResponse(existingResponse_), async(new uv_async_t) { - assert(request); - context->addRequest(request); + context->addRequest(this); async->data = this; uv_async_init(loop, async, [](uv_async_t *as, int) { - auto impl = reinterpret_cast<HTTPRequestImpl *>(as->data); + auto impl = reinterpret_cast<HTTPRequest *>(as->data); impl->handleResponse(); }); start(); } -void HTTPRequestImpl::start() { +HTTPRequest::~HTTPRequest() { + assert(!task); + assert(async); + + // Stop the backoff timer to avoid re-triggering this request. + cancelTimer(); + + uv::close(async); + + context->removeRequest(this); +} + +void HTTPRequest::start() { assert(!task); attempts++; @autoreleasepool { - NSMutableString *url = [[NSMutableString alloc] initWithString:@(request->resource.url.c_str())]; + NSMutableString *url = [[NSMutableString alloc] initWithString:@(resource.url.c_str())]; if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] == nil) { if ([url rangeOfString:@"?"].location == NSNotFound) { [url appendString:@"?"]; @@ -175,13 +197,13 @@ void HTTPRequestImpl::start() { } } -void HTTPRequestImpl::handleResponse() { +void HTTPRequest::handleResponse() { if (task) { [task release]; task = nullptr; } - if (request) { + if (!cancelled) { if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) { strategy = ExponentialBackoff; return retry((1 << (attempts - 1)) * 1000); @@ -194,23 +216,18 @@ void HTTPRequestImpl::handleResponse() { // Actually return the response. if (status == ResponseStatus::NotModified) { - request->notify(std::move(response), FileCache::Hint::Refresh); + notify(std::move(response), FileCache::Hint::Refresh); } else { - request->notify(std::move(response), FileCache::Hint::Full); + notify(std::move(response), FileCache::Hint::Full); } - - context->removeRequest(request); - request->impl = nullptr; - delete request; - request = nullptr; } delete this; } -void HTTPRequestImpl::cancel() { - context->removeRequest(request); - request = nullptr; +void HTTPRequest::cancel() { + context->removeRequest(this); + cancelled = true; // Stop the backoff timer to avoid re-triggering this request. cancelTimer(); @@ -224,7 +241,7 @@ void HTTPRequestImpl::cancel() { } } -void HTTPRequestImpl::cancelTimer() { +void HTTPRequest::cancelTimer() { if (timer) { uv_timer_stop(timer); uv::close(timer); @@ -232,21 +249,6 @@ void HTTPRequestImpl::cancelTimer() { } } -HTTPRequestImpl::~HTTPRequestImpl() { - assert(!task); - assert(async); - - // Stop the backoff timer to avoid re-triggering this request. - cancelTimer(); - - uv::close(async); - - if (request) { - context->removeRequest(request); - request->impl = nullptr; - } -} - int64_t parseCacheControl(const char *value) { if (value) { unsigned long long seconds = 0; @@ -262,7 +264,7 @@ int64_t parseCacheControl(const char *value) { return 0; } -void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *error) { +void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error) { if (error) { if ([error code] == NSURLErrorCancelled) { status = ResponseStatus::Canceled; @@ -365,7 +367,7 @@ void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *er uv_async_send(async); } -void HTTPRequestImpl::retry(uint64_t timeout) { +void HTTPRequest::retry(uint64_t timeout) { response.reset(); assert(!timer); @@ -375,7 +377,7 @@ void HTTPRequestImpl::retry(uint64_t timeout) { uv_timer_start(timer, restart, timeout, 0); } -void HTTPRequestImpl::retryImmediately() { +void HTTPRequest::retry() { // All batons get notified when the network status changed, but some of them // might not actually wait for the network to become available again. if (timer && strategy == PreemptImmediately) { @@ -385,9 +387,9 @@ void HTTPRequestImpl::retryImmediately() { } } -void HTTPRequestImpl::restart(uv_timer_t *timer, int) { +void HTTPRequest::restart(uv_timer_t *timer, int) { // Restart the request. - auto impl = reinterpret_cast<HTTPRequestImpl *>(timer->data); + auto impl = reinterpret_cast<HTTPRequest *>(timer->data); // Get rid of the timer. impl->timer = nullptr; @@ -396,28 +398,8 @@ void HTTPRequestImpl::restart(uv_timer_t *timer, int) { impl->start(); } -// ------------------------------------------------------------------------------------------------- - -HTTPRequest::HTTPRequest(const Resource& resource, Callback callback, - uv_loop_t* loop, std::shared_ptr<const Response> response) - : RequestBase(resource, callback) - , impl(new HTTPRequestImpl(this, loop, response)) { -} - -HTTPRequest::~HTTPRequest() { - if (impl) { - impl->cancel(); - } -} - -void HTTPRequest::retryImmediately() { - if (impl) { - impl->retryImmediately(); - } -} - -void HTTPRequest::cancel() { - delete this; +std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) { + return util::make_unique<HTTPNSURLContext>(loop); } } diff --git a/platform/default/asset_request_fs.cpp b/platform/default/asset_request_fs.cpp index 87e4320622..8fc56eb16b 100644 --- a/platform/default/asset_request_fs.cpp +++ b/platform/default/asset_request_fs.cpp @@ -1,4 +1,4 @@ -#include <mbgl/storage/asset_request.hpp> +#include <mbgl/storage/asset_context.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/std.hpp> @@ -7,27 +7,19 @@ #include <uv.h> -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#endif -#include <boost/algorithm/string.hpp> -#pragma GCC diagnostic pop - #include <cassert> - - -namespace algo = boost::algorithm; +#include <limits> namespace mbgl { -class AssetRequestImpl { +class AssetRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - AssetRequestImpl(AssetRequest*, uv_loop_t*, const std::string& assetRoot); - ~AssetRequestImpl(); + AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot); + ~AssetRequest(); + + void cancel() override; static void fileOpened(uv_fs_t *req); static void fileStated(uv_fs_t *req); @@ -36,7 +28,6 @@ public: static void notifyError(uv_fs_t *req); static void cleanup(uv_fs_t *req); - AssetRequest *request = nullptr; std::string assetRoot; bool canceled = false; uv_fs_t req; @@ -45,20 +36,25 @@ public: std::unique_ptr<Response> response; }; -AssetRequestImpl::~AssetRequestImpl() { - MBGL_VERIFY_THREAD(tid); - - if (request) { - request->impl = nullptr; +class AssetFSContext : public AssetContext { + RequestBase* createRequest(const Resource& resource, + RequestBase::Callback callback, + uv_loop_t* loop, + const std::string& assetRoot) override { + return new AssetRequest(resource, callback, loop, assetRoot); } +}; + +AssetRequest::~AssetRequest() { + MBGL_VERIFY_THREAD(tid); } -AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, const std::string& assetRoot_) - : request(request_) - , assetRoot(assetRoot_) { +AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, uv_loop_t* loop, const std::string& assetRoot_) + : RequestBase(resource_, callback_), + assetRoot(assetRoot_) { req.data = this; - const auto &url = request->resource.url; + const auto &url = resource.url; std::string path; if (url.size() <= 8 || url[8] == '/') { // This is an empty or absolute path. @@ -71,9 +67,9 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, cons uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened); } -void AssetRequestImpl::fileOpened(uv_fs_t *req) { +void AssetRequest::fileOpened(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); if (req->result < 0) { @@ -96,9 +92,9 @@ void AssetRequestImpl::fileOpened(uv_fs_t *req) { } } -void AssetRequestImpl::fileStated(uv_fs_t *req) { +void AssetRequest::fileStated(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); if (req->result != 0 || self->canceled) { @@ -124,9 +120,7 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) { #else response->message = uv_strerror(UV_EFBIG); #endif - assert(self->request); - self->request->notify(std::move(response), FileCache::Hint::No); - delete self->request; + self->notify(std::move(response), FileCache::Hint::No); uv_fs_req_cleanup(req); uv_fs_close(req->loop, req, self->fd, fileClosed); @@ -151,9 +145,9 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) { } } -void AssetRequestImpl::fileRead(uv_fs_t *req) { +void AssetRequest::fileRead(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); if (req->result < 0 || self->canceled) { @@ -163,18 +157,16 @@ void AssetRequestImpl::fileRead(uv_fs_t *req) { } else { // File was successfully read. self->response->status = Response::Successful; - assert(self->request); - self->request->notify(std::move(self->response), FileCache::Hint::No); - delete self->request; + self->notify(std::move(self->response), FileCache::Hint::No); } uv_fs_req_cleanup(req); uv_fs_close(req->loop, req, self->fd, fileClosed); } -void AssetRequestImpl::fileClosed(uv_fs_t *req) { +void AssetRequest::fileClosed(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); (void(self)); // Silence unused variable error in Release mode @@ -185,51 +177,37 @@ void AssetRequestImpl::fileClosed(uv_fs_t *req) { cleanup(req); } -void AssetRequestImpl::notifyError(uv_fs_t *req) { +void AssetRequest::notifyError(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) { auto response = util::make_unique<Response>(); response->status = Response::Error; response->message = uv::getFileRequestError(req); - assert(self->request); - self->request->notify(std::move(response), FileCache::Hint::No); - delete self->request; + self->notify(std::move(response), FileCache::Hint::No); } } -void AssetRequestImpl::cleanup(uv_fs_t *req) { +void AssetRequest::cleanup(uv_fs_t *req) { assert(req->data); - auto self = reinterpret_cast<AssetRequestImpl *>(req->data); + auto self = reinterpret_cast<AssetRequest *>(req->data); MBGL_VERIFY_THREAD(self->tid); uv_fs_req_cleanup(req); delete self; } -// ------------------------------------------------------------------------------------------------- - -AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, - uv_loop_t* loop, const std::string& assetRoot) - : RequestBase(resource_, callback_) - , impl(new AssetRequestImpl(this, loop, assetRoot)) { - assert(algo::starts_with(resource.url, "asset://")); - // Note: the AssetRequestImpl deletes itself. -} - -AssetRequest::~AssetRequest() { - if (impl) { - impl->request = nullptr; - } -} - void AssetRequest::cancel() { - impl->canceled = true; + 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. The cancelation callback will delete the AssetRequest object. - uv_cancel((uv_req_t *)&impl->req); + uv_cancel((uv_req_t *)&req); +} + +std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t*) { + return util::make_unique<AssetFSContext>(); } } diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp index 9f7ed6d9b7..7666000d00 100644 --- a/platform/default/asset_request_zip.cpp +++ b/platform/default/asset_request_zip.cpp @@ -1,46 +1,40 @@ -#include <mbgl/storage/asset_request.hpp> -#include <mbgl/storage/thread_context.hpp> +#include <mbgl/storage/asset_context.hpp> #include <mbgl/android/jni.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/util/std.hpp> +#include <mbgl/util/util.hpp> +#include <mbgl/util/uv.hpp> +#include <uv.h> #include "uv_zip.h" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#endif -#include <boost/algorithm/string.hpp> -#pragma GCC diagnostic pop - +#include <map> +#include <cassert> #include <forward_list> -namespace algo = boost::algorithm; - namespace mbgl { -class AssetZipContext : public ThreadContext<AssetZipContext> { +class AssetZipContext : public AssetContext { public: AssetZipContext(uv_loop_t *loop); ~AssetZipContext(); + RequestBase* createRequest(const Resource& resource, + RequestBase::Callback callback, + uv_loop_t* loop, + const std::string& assetRoot) override; + uv_zip_t *getHandle(const std::string &path); void returnHandle(const std::string &path, uv_zip_t *zip); -private: // A list of resuable uv_zip handles to avoid creating and destroying them all the time. std::map<std::string, std::forward_list<uv_zip_t *>> handles; + uv_loop_t *loop; }; -// ------------------------------------------------------------------------------------------------- - -template<> pthread_key_t ThreadContext<AssetZipContext>::key{}; -template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_INIT; - -AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) { +AssetZipContext::AssetZipContext(uv_loop_t *loop_) : loop(loop_) { } uv_zip_t *AssetZipContext::getHandle(const std::string &path) { @@ -71,20 +65,18 @@ AssetZipContext::~AssetZipContext() { handles.clear(); } -// ------------------------------------------------------------------------------------------------- - -class AssetRequestImpl { +class AssetRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - AssetRequestImpl(AssetRequest*, uv_loop_t*, const std::string& assetRoot); - ~AssetRequestImpl(); + AssetRequest(AssetZipContext&, const Resource&, Callback, const std::string& assetRoot); + ~AssetRequest(); - void cancel(); + void cancel() override; private: AssetZipContext &context; - AssetRequest *request = nullptr; + bool cancelled = false; const std::string root; const std::string path; std::unique_ptr<Response> response; @@ -102,21 +94,18 @@ private: void notifyError(const char *message); }; -// ------------------------------------------------------------------------------------------------- - -AssetRequestImpl::~AssetRequestImpl() { - MBGL_VERIFY_THREAD(tid); - - if (request) { - request->impl = nullptr; - } +RequestBase* AssetZipContext::createRequest(const Resource& resource, + RequestBase::Callback callback, + uv_loop_t*, + const std::string& assetRoot) { + return new AssetRequest(*this, resource, callback, assetRoot); } -AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, const std::string& assetRoot_) - : context(*AssetZipContext::Get(loop)), - request(request_), +AssetRequest::AssetRequest(AssetZipContext& context_, const Resource& resource_, Callback callback_, const std::string& assetRoot_) + : RequestBase(resource_, callback_), + context(context_), root(assetRoot_), - path(std::string { "assets/" } + request->resource.url.substr(8)) { + path(std::string { "assets/" } + resource.url.substr(8)) { auto zip = context.getHandle(root); if (zip) { archiveOpened(zip); @@ -125,17 +114,19 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest* request_, uv_loop_t* loop, cons } } -void AssetRequestImpl::openZipArchive() { +AssetRequest::~AssetRequest() { + MBGL_VERIFY_THREAD(tid); +} + +void AssetRequest::openZipArchive() { uv_fs_t *req = new uv_fs_t(); req->data = this; - assert(request); - // We're using uv_fs_open first to obtain a file descriptor. Then, uv_zip_fdopen will operate // on a read-only file. uv_fs_open(context.loop, req, root.c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) { if (fsReq->result < 0) { - auto impl = reinterpret_cast<AssetRequestImpl *>(fsReq->data); + auto impl = reinterpret_cast<AssetRequest *>(fsReq->data); impl->notifyError(uv::getFileRequestError(fsReq)); delete impl; } else { @@ -143,7 +134,7 @@ void AssetRequestImpl::openZipArchive() { uv_zip_init(zip); zip->data = fsReq->data; uv_zip_fdopen(fsReq->loop, zip, uv_file(fsReq->result), 0, [](uv_zip_t *openZip) { - auto impl = reinterpret_cast<AssetRequestImpl *>(openZip->data); + auto impl = reinterpret_cast<AssetRequest *>(openZip->data); if (openZip->result < 0) { impl->notifyError(openZip->message); delete openZip; @@ -163,20 +154,20 @@ void AssetRequestImpl::openZipArchive() { #define INVOKE_MEMBER(name) \ [](uv_zip_t *zip_) { \ assert(zip_->data); \ - reinterpret_cast<AssetRequestImpl *>(zip_->data)->name(zip_); \ + reinterpret_cast<AssetRequest *>(zip_->data)->name(zip_); \ } -void AssetRequestImpl::archiveOpened(uv_zip_t *zip) { +void AssetRequest::archiveOpened(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); zip->data = this; uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated)); } -void AssetRequestImpl::fileStated(uv_zip_t *zip) { +void AssetRequest::fileStated(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); - if (!request || zip->result < 0) { + if (cancelled || zip->result < 0) { // Stat failed, probably because the file doesn't exist. if (zip->result < 0) { notifyError(zip->message); @@ -206,14 +197,14 @@ void AssetRequestImpl::fileStated(uv_zip_t *zip) { } } -void AssetRequestImpl::fileOpened(uv_zip_t *zip) { +void AssetRequest::fileOpened(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); if (zip->result < 0) { // Opening failed. notifyError(zip->message); cleanup(zip); - } else if (!request) { + } else if (cancelled) { // The request was canceled. Close the file again. uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); } else { @@ -221,23 +212,21 @@ void AssetRequestImpl::fileOpened(uv_zip_t *zip) { } } -void AssetRequestImpl::fileRead(uv_zip_t *zip) { +void AssetRequest::fileRead(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); if (zip->result < 0) { // Reading failed. We still have an open file handle though. notifyError(zip->message); - } else if (request) { + } else if (!cancelled) { response->status = Response::Successful; - request->notify(std::move(response), FileCache::Hint::No); - delete request; - assert(request == nullptr); + notify(std::move(response), FileCache::Hint::No); } uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); } -void AssetRequestImpl::fileClosed(uv_zip_t *zip) { +void AssetRequest::fileClosed(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); if (zip->result < 0) { @@ -247,53 +236,32 @@ void AssetRequestImpl::fileClosed(uv_zip_t *zip) { cleanup(zip); } -void AssetRequestImpl::cleanup(uv_zip_t *zip) { +void AssetRequest::cleanup(uv_zip_t *zip) { MBGL_VERIFY_THREAD(tid); context.returnHandle(root, zip); delete this; } -void AssetRequestImpl::notifyError(const char *message) { +void AssetRequest::notifyError(const char *message) { MBGL_VERIFY_THREAD(tid); - if (request) { + if (!cancelled) { response = util::make_unique<Response>(); response->status = Response::Error; response->message = message; - request->notify(std::move(response), FileCache::Hint::No); - delete request; - assert(request == nullptr); + notify(std::move(response), FileCache::Hint::No); } else { // The request was already canceled and deleted. } } -void AssetRequestImpl::cancel() { - request = nullptr; -} - -// ------------------------------------------------------------------------------------------------- - -AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, - uv_loop_t* loop, const std::string& assetRoot) - : RequestBase(resource_, callback_) - , impl(new AssetRequestImpl(this, loop, assetRoot)) { - assert(algo::starts_with(resource.url, "asset://")); -} - -AssetRequest::~AssetRequest() { - if (impl) { - impl->cancel(); - } -} - void AssetRequest::cancel() { - if (impl) { - impl->cancel(); - } + cancelled = true; +} - delete this; +std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t* loop) { + return util::make_unique<AssetZipContext>(loop); } } diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp index 95cc882114..27e57aab81 100644 --- a/platform/default/http_request_curl.cpp +++ b/platform/default/http_request_curl.cpp @@ -1,4 +1,3 @@ -#include <mbgl/storage/http_request.hpp> #include <mbgl/storage/http_context.hpp> #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> @@ -6,6 +5,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/util/time.hpp> +#include <mbgl/util/util.hpp> #include <curl/curl.h> @@ -54,14 +54,20 @@ enum class ResponseStatus : int8_t { NotModified, }; -class HTTPRequestImpl; +class HTTPRequest; -class HTTPCURLContext : public HTTPContext<HTTPCURLContext> { +class HTTPCURLContext : public HTTPContext { + MBGL_STORE_THREAD(tid) public: HTTPCURLContext(uv_loop_t *loop); ~HTTPCURLContext(); + RequestBase* createRequest(const Resource&, + RequestBase::Callback, + uv_loop_t*, + std::shared_ptr<const Response>) override; + static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp); static void perform(uv_poll_t *req, int status, int events); static int startTimeout(CURLM *multi, long timeout_ms, void *userp); @@ -75,7 +81,8 @@ public: void returnHandle(CURL *handle); void checkMultiInfo(); -public: + uv_loop_t *loop = nullptr; + // Used as the CURL timer function to periodically check for socket updates. uv_timer_t *timeout = nullptr; @@ -91,17 +98,21 @@ public: std::queue<CURL *> handles; }; - -class HTTPRequestImpl { +class HTTPRequest : public RequestBase { MBGL_STORE_THREAD(tid) public: - HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response); - ~HTTPRequestImpl(); + HTTPRequest(HTTPCURLContext*, + const Resource&, + Callback, + uv_loop_t*, + std::shared_ptr<const Response>); + ~HTTPRequest(); + + void cancel() override; + void retry() override; void handleResult(CURLcode code); - void abandon(); - void retryImmediately(); private: static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp); @@ -116,9 +127,8 @@ private: void finish(ResponseStatus status); void start(); -private: HTTPCURLContext *context = nullptr; - HTTPRequest *request = nullptr; + bool cancelled = false; // Will store the current response. std::unique_ptr<Response> response; @@ -176,10 +186,9 @@ private: // ------------------------------------------------------------------------------------------------- -template<> pthread_key_t ThreadContext<HTTPCURLContext>::key{}; -template<> pthread_once_t ThreadContext<HTTPCURLContext>::once = PTHREAD_ONCE_INIT; - -HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) { +HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) + : HTTPContext(loop_), + loop(loop_) { if (curl_global_init(CURL_GLOBAL_ALL)) { throw std::runtime_error("Could not init cURL"); } @@ -208,6 +217,13 @@ HTTPCURLContext::~HTTPCURLContext() { uv::close(timeout); } +RequestBase* HTTPCURLContext::createRequest(const Resource& resource, + RequestBase::Callback callback, + uv_loop_t* loop_, + std::shared_ptr<const Response> response) { + return new HTTPRequest(this, resource, callback, loop_, response); +} + CURL *HTTPCURLContext::getHandle() { if (!handles.empty()) { auto handle = handles.front(); @@ -231,7 +247,7 @@ void HTTPCURLContext::checkMultiInfo() { while ((message = curl_multi_info_read(multi, &pending))) { switch (message->msg) { case CURLMSG_DONE: { - HTTPRequestImpl *baton = nullptr; + HTTPRequest *baton = nullptr; curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton); assert(baton); baton->handleResult(message->data.result); @@ -426,13 +442,12 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm } #endif -HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::shared_ptr<const Response> response_) - : context(HTTPCURLContext::Get(loop)), - request(request_), +HTTPRequest::HTTPRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_) + : RequestBase(resource_, callback_), + context(context_), existingResponse(response_), handle(context->getHandle()) { - assert(request); - context->addRequest(request); + context->addRequest(this); // Zero out the error buffer. memset(error, 0, sizeof(error)); @@ -463,7 +478,7 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt")); #endif handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1)); - handleError(curl_easy_setopt(handle, CURLOPT_URL, request->resource.url.c_str())); + handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str())); handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback)); handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this)); handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback)); @@ -475,28 +490,10 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh start(); } -void HTTPRequestImpl::abandon() { - if (request) { - context->removeRequest(request); - request = nullptr; - } -} - -void HTTPRequestImpl::start() { - // Count up the attempts. - attempts++; - - // Start requesting the information. - handleError(curl_multi_add_handle(context->multi, handle)); -} - -HTTPRequestImpl::~HTTPRequestImpl() { +HTTPRequest::~HTTPRequest() { MBGL_VERIFY_THREAD(tid); - if (request) { - context->removeRequest(request); - request->impl = nullptr; - } + context->removeRequest(this); handleError(curl_multi_remove_handle(context->multi, handle)); context->returnHandle(handle); @@ -515,11 +512,23 @@ HTTPRequestImpl::~HTTPRequestImpl() { } } +void HTTPRequest::cancel() { + delete this; +} + +void HTTPRequest::start() { + // Count up the attempts. + attempts++; + + // Start requesting the information. + handleError(curl_multi_add_handle(context->multi, handle)); +} + // 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 HTTPRequestImpl::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) { +size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) { assert(userp); - auto impl = reinterpret_cast<HTTPRequestImpl *>(userp); + auto impl = reinterpret_cast<HTTPRequest *>(userp); MBGL_VERIFY_THREAD(impl->tid); if (!impl->response) { @@ -561,9 +570,9 @@ int64_t parseCacheControl(const char *value) { return 0; } -size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) { +size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) { assert(userp); - auto baton = reinterpret_cast<HTTPRequestImpl *>(userp); + auto baton = reinterpret_cast<HTTPRequest *>(userp); MBGL_VERIFY_THREAD(baton->tid); if (!baton->response) { @@ -590,8 +599,7 @@ size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, co return length; } - -void HTTPRequestImpl::retry(uint64_t timeout) { +void HTTPRequest::retry(uint64_t timeout) { handleError(curl_multi_remove_handle(context->multi, handle)); response.reset(); @@ -603,7 +611,7 @@ void HTTPRequestImpl::retry(uint64_t timeout) { uv_timer_start(timer, restart, timeout, 0); } -void HTTPRequestImpl::retryImmediately() { +void HTTPRequest::retry() { // All batons get notified when the network status changed, but some of them // might not actually wait for the network to become available again. if (timer && strategy == PreemptImmediately) { @@ -614,12 +622,12 @@ void HTTPRequestImpl::retryImmediately() { } #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -void HTTPRequestImpl::restart(uv_timer_t *timer, int) { +void HTTPRequest::restart(uv_timer_t *timer, int) { #else -void HTTPRequestImpl::restart(uv_timer_t *timer) { +void HTTPRequest::restart(uv_timer_t *timer) { #endif // Restart the request. - auto baton = reinterpret_cast<HTTPRequestImpl *>(timer->data); + auto baton = reinterpret_cast<HTTPRequest *>(timer->data); // Get rid of the timer. baton->timer = nullptr; @@ -628,7 +636,7 @@ void HTTPRequestImpl::restart(uv_timer_t *timer) { baton->start(); } -void HTTPRequestImpl::finish(ResponseStatus status) { +void HTTPRequest::finish(ResponseStatus status) { if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) { strategy = ExponentialBackoff; return retry((1 << (attempts - 1)) * 1000); @@ -641,19 +649,18 @@ void HTTPRequestImpl::finish(ResponseStatus status) { // Actually return the response. if (status == ResponseStatus::NotModified) { - request->notify(std::move(response), FileCache::Hint::Refresh); + notify(std::move(response), FileCache::Hint::Refresh); } else { - request->notify(std::move(response), FileCache::Hint::Full); + notify(std::move(response), FileCache::Hint::Full); } - delete request; delete this; } -void HTTPRequestImpl::handleResult(CURLcode code) { +void HTTPRequest::handleResult(CURLcode code) { MBGL_VERIFY_THREAD(tid); - if (!request) { + if (cancelled) { // In this case, it doesn't make sense to even process the response even further since // the request was canceled anyway. delete this; @@ -721,30 +728,8 @@ void HTTPRequestImpl::handleResult(CURLcode code) { throw std::runtime_error("Response hasn't been handled"); } -// ------------------------------------------------------------------------------------------------- - -HTTPRequest::HTTPRequest(const Resource& resource_, Callback callback_, - uv_loop_t* loop, std::shared_ptr<const Response> response) - : RequestBase(resource_, callback_) - , impl(new HTTPRequestImpl(this, loop, response)) { -} - -HTTPRequest::~HTTPRequest() { - if (impl) { - impl->abandon(); - } -} - -void HTTPRequest::retryImmediately() { - if (impl) { - impl->retryImmediately(); - } -} - -void HTTPRequest::cancel() { - delete impl; - impl = nullptr; - delete this; +std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) { + return util::make_unique<HTTPCURLContext>(loop); } } diff --git a/src/mbgl/storage/asset_context.hpp b/src/mbgl/storage/asset_context.hpp new file mode 100644 index 0000000000..44ebdba4ff --- /dev/null +++ b/src/mbgl/storage/asset_context.hpp @@ -0,0 +1,23 @@ +#ifndef MBGL_STORAGE_DEFAULT_ASSET_CONTEXT +#define MBGL_STORAGE_DEFAULT_ASSET_CONTEXT + +#include <mbgl/storage/request_base.hpp> + +typedef struct uv_loop_s uv_loop_t; + +namespace mbgl { + +class AssetContext { +public: + static std::unique_ptr<AssetContext> createContext(uv_loop_t*); + + virtual ~AssetContext() = default; + virtual RequestBase* createRequest(const Resource&, + RequestBase::Callback, + uv_loop_t*, + const std::string& assetRoot) = 0; +}; + +} + +#endif diff --git a/src/mbgl/storage/asset_request.hpp b/src/mbgl/storage/asset_request.hpp deleted file mode 100644 index 3f9b018b54..0000000000 --- a/src/mbgl/storage/asset_request.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST -#define MBGL_STORAGE_DEFAULT_ASSET_REQUEST - -#include "request_base.hpp" - -typedef struct uv_loop_s uv_loop_t; - -namespace mbgl { - -class AssetRequestImpl; - -class AssetRequest : public RequestBase { -public: - AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot); - - void cancel() override; - -private: - ~AssetRequest(); - - AssetRequestImpl* impl; - friend class AssetRequestImpl; -}; - -} - -#endif diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp index b652e5e716..7ddfa08d93 100644 --- a/src/mbgl/storage/default_file_source.cpp +++ b/src/mbgl/storage/default_file_source.cpp @@ -1,7 +1,7 @@ #include <mbgl/storage/default_file_source_impl.hpp> #include <mbgl/storage/request.hpp> -#include <mbgl/storage/asset_request.hpp> -#include <mbgl/storage/http_request.hpp> +#include <mbgl/storage/asset_context.hpp> +#include <mbgl/storage/http_context.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/platform/platform.hpp> @@ -64,7 +64,9 @@ void DefaultFileSource::cancel(Request *req) { DefaultFileSource::Impl::Impl(uv_loop_t* loop_, FileCache* cache_, const std::string& root) : loop(loop_), cache(cache_), - assetRoot(root.empty() ? platform::assetRoot() : root) { + assetRoot(root.empty() ? platform::assetRoot() : root), + assetContext(AssetContext::createContext(loop_)), + httpContext(HTTPContext::createContext(loop_)) { } DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) { @@ -130,9 +132,9 @@ void DefaultFileSource::Impl::startRealRequest(const Resource& resource, std::sh }; if (algo::starts_with(resource.url, "asset://")) { - request->request = new AssetRequest(resource, callback, loop, assetRoot); + request->request = assetContext->createRequest(resource, callback, loop, assetRoot); } else { - request->request = new HTTPRequest(resource, callback, loop, response); + request->request = httpContext->createRequest(resource, callback, loop, response); } } diff --git a/src/mbgl/storage/default_file_source_impl.hpp b/src/mbgl/storage/default_file_source_impl.hpp index 1d42f474ff..ed2d248d0a 100644 --- a/src/mbgl/storage/default_file_source_impl.hpp +++ b/src/mbgl/storage/default_file_source_impl.hpp @@ -2,6 +2,8 @@ #define MBGL_STORAGE_DEFAULT_DEFAULT_FILE_SOURCE_IMPL #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/asset_context.hpp> +#include <mbgl/storage/http_context.hpp> #include <set> #include <unordered_map> @@ -39,6 +41,8 @@ private: uv_loop_t* loop = nullptr; FileCache* cache = nullptr; const std::string assetRoot; + std::unique_ptr<AssetContext> assetContext; + std::unique_ptr<HTTPContext> httpContext; }; } diff --git a/src/mbgl/storage/http_context.cpp b/src/mbgl/storage/http_context.cpp new file mode 100644 index 0000000000..c747490804 --- /dev/null +++ b/src/mbgl/storage/http_context.cpp @@ -0,0 +1,30 @@ +#include <mbgl/storage/http_context.hpp> + +namespace mbgl { + +HTTPContext::HTTPContext(uv_loop_t* loop_) + : reachability(loop_, [this] { retryRequests(); }) { + NetworkStatus::Subscribe(reachability.get()); + reachability.unref(); +} + +HTTPContext::~HTTPContext() { + assert(requests.empty()); + NetworkStatus::Unsubscribe(reachability.get()); +} + +void HTTPContext::addRequest(RequestBase* request) { + requests.insert(request); +} + +void HTTPContext::removeRequest(RequestBase* request) { + requests.erase(request); +} + +void HTTPContext::retryRequests() { + for (auto request : requests) { + request->retry(); + } +} + +} diff --git a/src/mbgl/storage/http_context.hpp b/src/mbgl/storage/http_context.hpp index 6b9518dab3..64a8afa6dd 100644 --- a/src/mbgl/storage/http_context.hpp +++ b/src/mbgl/storage/http_context.hpp @@ -1,76 +1,40 @@ #ifndef MBGL_STORAGE_DEFAULT_HTTP_CONTEXT #define MBGL_STORAGE_DEFAULT_HTTP_CONTEXT -#include "thread_context.hpp" +#include <mbgl/storage/request_base.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/uv_detail.hpp> #include <set> namespace mbgl { -class HTTPRequest; +class HTTPContext { +public: + static std::unique_ptr<HTTPContext> createContext(uv_loop_t*); -// This is a template class that provides a per-thread Context object. It can be used by HTTP -// implementations to store global state. It also implements the NetworkStatus mechanism and -// triggers immediate retries on all requests waiting for network status changes. + HTTPContext(uv_loop_t*); + virtual ~HTTPContext(); -template <typename Context> -class HTTPContext : public ThreadContext<Context> { -public: - HTTPContext(uv_loop_t *loop); - ~HTTPContext(); + virtual RequestBase* createRequest(const Resource&, + RequestBase::Callback, + uv_loop_t*, + std::shared_ptr<const Response>) = 0; - void addRequest(HTTPRequest *request); - void removeRequest(HTTPRequest *baton); + void addRequest(RequestBase*); + void removeRequest(RequestBase*); + +private: + void retryRequests(); -public: // Will be fired when the network status becomes reachable. - uv_async_t *reachability = nullptr; + uv::async reachability; // A list of all pending HTTPRequestImpls that we need to notify when the network status // changes. - std::set<HTTPRequest *> requests; + std::set<RequestBase*> requests; }; -template <typename Context> -HTTPContext<Context>::HTTPContext(uv_loop_t *loop_) - : ThreadContext<Context>(loop_) { - reachability = new uv_async_t; - reachability->data = this; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_async_init(loop_, reachability, [](uv_async_t *async, int) { -#else - uv_async_init(loop_, reachability, [](uv_async_t *async) { -#endif - for (auto request : reinterpret_cast<Context *>(async->data)->requests) { - request->retryImmediately(); - } - }); - // Allow the loop to quit even though this handle is still active. - uv_unref(reinterpret_cast<uv_handle_t *>(reachability)); - NetworkStatus::Subscribe(reachability); -} - -template <typename Context> -HTTPContext<Context>::~HTTPContext() { - MBGL_VERIFY_THREAD(HTTPContext<Context>::tid); - - assert(requests.empty()); - - NetworkStatus::Unsubscribe(reachability); - uv::close(reachability); -} - -template <typename Context> -void HTTPContext<Context>::addRequest(HTTPRequest *request) { - requests.insert(request); -} - -template <typename Context> -void HTTPContext<Context>::removeRequest(HTTPRequest *request) { - requests.erase(request); -} - } #endif diff --git a/src/mbgl/storage/http_request.hpp b/src/mbgl/storage/http_request.hpp deleted file mode 100644 index bbe1265c6d..0000000000 --- a/src/mbgl/storage/http_request.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST -#define MBGL_STORAGE_DEFAULT_HTTP_REQUEST - -#include "request_base.hpp" - -typedef struct uv_loop_s uv_loop_t; - -namespace mbgl { - -class HTTPRequestImpl; - -class HTTPRequest : public RequestBase { -public: - HTTPRequest(const Resource&, Callback, uv_loop_t*, std::shared_ptr<const Response> = nullptr); - - void cancel() override; - void retryImmediately(); - -private: - ~HTTPRequest(); - - HTTPRequestImpl* impl; - friend class HTTPRequestImpl; -}; - -} - -#endif diff --git a/src/mbgl/storage/request_base.hpp b/src/mbgl/storage/request_base.hpp index e4ea7117c1..77c1d94229 100644 --- a/src/mbgl/storage/request_base.hpp +++ b/src/mbgl/storage/request_base.hpp @@ -23,6 +23,7 @@ public: virtual ~RequestBase() = default; virtual void cancel() = 0; + virtual void retry() {}; protected: const Resource& resource; diff --git a/src/mbgl/storage/thread_context.hpp b/src/mbgl/storage/thread_context.hpp deleted file mode 100644 index 763c83a25b..0000000000 --- a/src/mbgl/storage/thread_context.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef MBGL_STORAGE_DEFAULT_THREAD_CONTEXT -#define MBGL_STORAGE_DEFAULT_THREAD_CONTEXT - -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/std.hpp> -#include <mbgl/util/util.hpp> -#include <mbgl/util/uv.hpp> - -#include <uv.h> -#include <pthread.h> - -#include <map> -#include <cassert> - -namespace mbgl { - -// This is a template class that provides a per-thread and per-loop Context object. It can be used -// by implementations to store global state. - -template <typename Context> -class ThreadContext : private util::noncopyable { -protected: - MBGL_STORE_THREAD(tid) - using Map = std::map<uv_loop_t *, std::unique_ptr<Context>>; - -public: - static Context *Get(uv_loop_t *loop); - -private: - static pthread_key_t key; - static pthread_once_t once; - -public: - ThreadContext(uv_loop_t *loop); - ~ThreadContext(); - -public: - uv_loop_t *loop; -}; - -template <typename Context> -Context *ThreadContext<Context>::Get(uv_loop_t *loop) { - pthread_once(&once, []() { - pthread_key_create(&key, [](void *ptr) { - assert(ptr); - delete reinterpret_cast<Map *>(ptr); - }); - }); - auto contexts = reinterpret_cast<Map *>(pthread_getspecific(key)); - if (!contexts) { - contexts = new Map(); - pthread_setspecific(key, contexts); - } - - // Now find a ThreadContext that matches the requested loop. - auto it = contexts->find(loop); - if (it == contexts->end()) { - auto result = contexts->emplace(loop, util::make_unique<Context>(loop)); - assert(result.second); // Make sure it was actually inserted. - return result.first->second.get(); - } else { - return it->second.get(); - } -} - -template <typename Context> -ThreadContext<Context>::ThreadContext(uv_loop_t *loop_) : loop(loop_) { -} - -template <typename Context> -ThreadContext<Context>::~ThreadContext() { - MBGL_VERIFY_THREAD(tid); -} - - -} - -#endif diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp index 96d5442462..fbc0ec5aa0 100644 --- a/src/mbgl/util/uv_detail.hpp +++ b/src/mbgl/util/uv_detail.hpp @@ -104,6 +104,10 @@ public: uv_unref(reinterpret_cast<uv_handle_t*>(a.get())); } + inline uv_async_t* get() { + return a.get(); + } + private: #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 static void async_cb(uv_async_t* a, int) { |