summaryrefslogtreecommitdiff
path: root/include/mbgl/storage/default/http_context.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/mbgl/storage/default/http_context.hpp')
-rw-r--r--include/mbgl/storage/default/http_context.hpp117
1 files changed, 117 insertions, 0 deletions
diff --git a/include/mbgl/storage/default/http_context.hpp b/include/mbgl/storage/default/http_context.hpp
new file mode 100644
index 0000000000..18f17ef0cb
--- /dev/null
+++ b/include/mbgl/storage/default/http_context.hpp
@@ -0,0 +1,117 @@
+#ifndef MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
+#define MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
+
+#include <mbgl/storage/network_status.hpp>
+#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>
+#include <set>
+
+namespace mbgl {
+
+class HTTPRequest;
+
+// 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.
+
+template <typename Context>
+class HTTPContext : 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:
+ HTTPContext(uv_loop_t *loop);
+ ~HTTPContext();
+
+ void addRequest(HTTPRequest *request);
+ void removeRequest(HTTPRequest *baton);
+
+public:
+ uv_loop_t *loop;
+
+ // Will be fired when the network status becomes reachable.
+ uv_async_t *reachability = nullptr;
+
+ // A list of all pending HTTPRequestImpls that we need to notify when the network status
+ // changes.
+ std::set<HTTPRequest *> requests;
+};
+
+template <typename Context>
+Context *HTTPContext<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 HTTPContext 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>
+HTTPContext<Context>::HTTPContext(uv_loop_t *loop_) : loop(loop_) {
+ reachability = new uv_async_t;
+ reachability->data = this;
+ uv_async_init(loop, reachability, [](uv_async_t *async, int) {
+ 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(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