summaryrefslogtreecommitdiff
path: root/src/mbgl/util
diff options
context:
space:
mode:
authorJason Wray <jason@kulturny.com>2015-04-27 17:49:24 -0400
committerJason Wray <jason@kulturny.com>2015-04-27 17:49:24 -0400
commit4d631623c7d29e8d40720e521e78c3299995b674 (patch)
treef8495149badf9b0fc76a51d5bde63c8bc375a709 /src/mbgl/util
parent7ac01660d7efd8ce7939a7934a6f5546e26f6c86 (diff)
parent99ab9c3c6debdf492aff7a751d82400eba1b1cdf (diff)
downloadqtlocation-mapboxgl-4d631623c7d29e8d40720e521e78c3299995b674.tar.gz
Merge master @ 353e5661de
Diffstat (limited to 'src/mbgl/util')
-rw-r--r--src/mbgl/util/channel.hpp39
-rw-r--r--src/mbgl/util/clip_id.cpp (renamed from src/mbgl/util/clip_ids.cpp)6
-rw-r--r--src/mbgl/util/clip_id.hpp (renamed from src/mbgl/util/clip_ids.hpp)24
-rw-r--r--src/mbgl/util/error.hpp20
-rw-r--r--src/mbgl/util/io.cpp10
-rw-r--r--src/mbgl/util/io.hpp9
-rw-r--r--src/mbgl/util/queue.h92
-rw-r--r--src/mbgl/util/run_loop.cpp39
-rw-r--r--src/mbgl/util/run_loop.hpp89
-rw-r--r--src/mbgl/util/thread.hpp121
-rw-r--r--src/mbgl/util/tile_cover.cpp93
-rw-r--r--src/mbgl/util/tile_cover.hpp15
-rw-r--r--src/mbgl/util/uv-channel.c69
-rw-r--r--src/mbgl/util/uv-channel.h29
-rw-r--r--src/mbgl/util/uv-messenger.c86
-rw-r--r--src/mbgl/util/uv-worker.c170
-rw-r--r--src/mbgl/util/uv-worker.h41
-rw-r--r--src/mbgl/util/uv.cpp22
-rw-r--r--src/mbgl/util/uv_detail.hpp89
-rw-r--r--src/mbgl/util/worker.cpp73
-rw-r--r--src/mbgl/util/worker.hpp44
21 files changed, 620 insertions, 560 deletions
diff --git a/src/mbgl/util/channel.hpp b/src/mbgl/util/channel.hpp
new file mode 100644
index 0000000000..a48156f120
--- /dev/null
+++ b/src/mbgl/util/channel.hpp
@@ -0,0 +1,39 @@
+#ifndef MBGL_UTIL_CHANNEL
+#define MBGL_UTIL_CHANNEL
+
+#include <mbgl/util/noncopyable.hpp>
+
+#include <mutex>
+#include <condition_variable>
+#include <queue>
+
+namespace mbgl {
+
+template <class T>
+class Channel : public mbgl::util::noncopyable {
+public:
+ void send(const T& t) {
+ std::unique_lock<std::mutex> lock(mutex);
+ queue.push(t);
+ condition.notify_one();
+ }
+
+ T receive() {
+ std::unique_lock<std::mutex> lock(mutex);
+ condition.wait(lock, [&](){ return !queue.empty(); });
+
+ T t = queue.front();
+ queue.pop();
+
+ return t;
+ }
+
+private:
+ std::mutex mutex;
+ std::condition_variable condition;
+ std::queue<T> queue;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/util/clip_ids.cpp b/src/mbgl/util/clip_id.cpp
index e7833b679f..be359c18c3 100644
--- a/src/mbgl/util/clip_ids.cpp
+++ b/src/mbgl/util/clip_id.cpp
@@ -1,8 +1,8 @@
-#include <mbgl/util/clip_ids.hpp>
-#include <mbgl/map/tile.hpp>
+#include <mbgl/util/clip_id.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/math.hpp>
+#include <mbgl/map/tile.hpp>
#include <list>
#include <vector>
@@ -15,7 +15,7 @@ namespace mbgl {
ClipIDGenerator::Leaf::Leaf(Tile &tile_) : tile(tile_) {}
-void ClipIDGenerator::Leaf::add(const Tile::ID &p) {
+void ClipIDGenerator::Leaf::add(const TileID &p) {
if (p.isChildOf(tile.id)) {
// Ensure that no already present child is a parent of the new p.
for (const auto& child : children) {
diff --git a/src/mbgl/util/clip_ids.hpp b/src/mbgl/util/clip_id.hpp
index 5855b16af7..3940e60524 100644
--- a/src/mbgl/util/clip_ids.hpp
+++ b/src/mbgl/util/clip_id.hpp
@@ -1,7 +1,10 @@
#ifndef MBGL_UTIL_CLIP_IDS
#define MBGL_UTIL_CLIP_IDS
-#include <mbgl/map/tile.hpp>
+#include <mbgl/map/tile_id.hpp>
+
+#include <bitset>
+#include <string>
#include <list>
#include <set>
#include <vector>
@@ -10,15 +13,30 @@
namespace mbgl {
+class Tile;
+class TileID;
+
+struct ClipID {
+ inline ClipID() {}
+ inline ClipID(const std::string &mask_, const std::string &reference_) : mask(mask_), reference(reference_) {}
+
+ std::bitset<8> mask;
+ std::bitset<8> reference;
+
+ inline bool operator==(const ClipID &other) const {
+ return mask == other.mask && reference == other.reference;
+ }
+};
+
class ClipIDGenerator {
private:
struct Leaf {
Leaf(Tile &tile);
- void add(const Tile::ID &p);
+ void add(const TileID &p);
bool operator==(const Leaf &other) const;
Tile &tile;
- std::forward_list<Tile::ID> children;
+ std::forward_list<TileID> children;
};
typedef std::vector<Leaf> Pool;
diff --git a/src/mbgl/util/error.hpp b/src/mbgl/util/error.hpp
deleted file mode 100644
index 09fa8d3e21..0000000000
--- a/src/mbgl/util/error.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef MBGL_UTIL_ERROR
-#define MBGL_UTIL_ERROR
-
-#include <stdexcept>
-#include <string>
-
-namespace mbgl {
-namespace error {
-
-struct style_parse : std::exception {
- inline style_parse(size_t offset_, const char *msg_) : offset(offset_), msg(msg_) {}
- inline const char* what() const noexcept { return msg.c_str(); }
- const size_t offset;
- const std::string msg;
-};
-}
-
-}
-
-#endif \ No newline at end of file
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index 76f7c35ade..bb4c3595c3 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -4,7 +4,8 @@
#include <iostream>
#include <sstream>
#include <fstream>
-#include <stdexcept>
+
+#include <unistd.h>
namespace mbgl {
namespace util {
@@ -30,5 +31,12 @@ std::string read_file(const std::string &filename) {
}
}
+void deleteFile(const std::string& filename) {
+ const int ret = unlink(filename.c_str());
+ if (ret == -1) {
+ throw IOException(errno, "failed to unlink file");
+ }
+}
+
}
}
diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp
index e95fc16d9d..bf15253ee4 100644
--- a/src/mbgl/util/io.hpp
+++ b/src/mbgl/util/io.hpp
@@ -2,13 +2,22 @@
#define MBGL_UTIL_IO
#include <string>
+#include <stdexcept>
namespace mbgl {
namespace util {
+struct IOException : std::runtime_error {
+ inline IOException(int err, const char* msg) : std::runtime_error(msg), code(err) {
+ }
+ const int code = 0;
+};
+
void write_file(const std::string &filename, const std::string &data);
std::string read_file(const std::string &filename);
+void deleteFile(const std::string& filename);
+
}
}
diff --git a/src/mbgl/util/queue.h b/src/mbgl/util/queue.h
deleted file mode 100644
index fe02b454ea..0000000000
--- a/src/mbgl/util/queue.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* 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_ */
diff --git a/src/mbgl/util/run_loop.cpp b/src/mbgl/util/run_loop.cpp
new file mode 100644
index 0000000000..fd9ad43060
--- /dev/null
+++ b/src/mbgl/util/run_loop.cpp
@@ -0,0 +1,39 @@
+#include <mbgl/util/run_loop.hpp>
+
+namespace mbgl {
+namespace util {
+
+uv::tls<RunLoop> RunLoop::current;
+
+RunLoop::RunLoop()
+ : async(*loop, std::bind(&RunLoop::process, this)) {
+}
+
+void RunLoop::withMutex(std::function<void()>&& fn) {
+ std::lock_guard<std::mutex> lock(mutex);
+ fn();
+}
+
+void RunLoop::process() {
+ Queue queue_;
+ withMutex([&] { queue_.swap(queue); });
+
+ while (!queue_.empty()) {
+ (*(queue_.front()))();
+ queue_.pop();
+ }
+}
+
+void RunLoop::run() {
+ assert(!current.get());
+ current.set(this);
+ loop.run();
+ current.set(nullptr);
+}
+
+void RunLoop::stop() {
+ invoke([&] { async.unref(); });
+}
+
+}
+}
diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp
new file mode 100644
index 0000000000..d785854e79
--- /dev/null
+++ b/src/mbgl/util/run_loop.hpp
@@ -0,0 +1,89 @@
+#ifndef MBGL_UTIL_RUN_LOOP
+#define MBGL_UTIL_RUN_LOOP
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/uv_detail.hpp>
+
+#include <functional>
+#include <queue>
+#include <mutex>
+
+namespace mbgl {
+namespace util {
+
+class RunLoop : private util::noncopyable {
+public:
+ RunLoop();
+
+ void run();
+ void stop();
+
+ // Invoke fn() in the runloop thread.
+ template <class Fn>
+ void invoke(Fn&& fn) {
+ auto invokable = util::make_unique<Invoker<Fn>>(std::move(fn));
+ withMutex([&] { queue.push(std::move(invokable)); });
+ async.send();
+ }
+
+ // Invoke fn() in the runloop thread, then invoke callback(result) in the current thread.
+ template <class Fn, class R>
+ void invokeWithResult(Fn&& fn, std::function<void (R)> callback) {
+ RunLoop* outer = current.get();
+ assert(outer);
+
+ invoke([fn, callback, outer] {
+ /*
+ With C++14, we could write:
+
+ outer->invoke([callback, result = std::move(fn())] () mutable {
+ callback(std::move(result));
+ });
+
+ Instead we're using a workaround with std::bind
+ to obtain move-capturing semantics with C++11:
+ http://stackoverflow.com/a/12744730/52207
+ */
+ outer->invoke(std::bind([callback] (R& result) {
+ callback(std::move(result));
+ }, std::move(fn())));
+ });
+ }
+
+ uv_loop_t* get() { return *loop; }
+
+private:
+ // A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable
+ // things (like std::function<>, or the result of a movable-only std::bind()) in the queue.
+ // Source: http://stackoverflow.com/a/29642072/331379
+ struct Message {
+ virtual void operator()() = 0;
+ virtual ~Message() = default;
+ };
+
+ template <class F>
+ struct Invoker : Message {
+ Invoker(F&& f) : func(std::move(f)) {}
+ void operator()() override { func(); }
+ F func;
+ };
+
+ using Queue = std::queue<std::unique_ptr<Message>>;
+
+ static uv::tls<RunLoop> current;
+
+ void withMutex(std::function<void()>&&);
+ void process();
+
+ Queue queue;
+ std::mutex mutex;
+
+ uv::loop loop;
+ uv::async async;
+};
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
new file mode 100644
index 0000000000..4831b9efc2
--- /dev/null
+++ b/src/mbgl/util/thread.hpp
@@ -0,0 +1,121 @@
+#ifndef MBGL_UTIL_THREAD
+#define MBGL_UTIL_THREAD
+
+#include <future>
+#include <thread>
+#include <functional>
+
+#include <mbgl/util/run_loop.hpp>
+
+namespace {
+
+template <::std::size_t...>
+struct index_sequence {};
+
+template <::std::size_t N, ::std::size_t... I>
+struct integer_sequence : integer_sequence<N - 1, N - 1, I...> {};
+
+template <::std::size_t... I>
+struct integer_sequence<0, I...> {
+ using type = index_sequence<I...>;
+};
+
+}
+
+namespace mbgl {
+namespace util {
+
+// Manages a thread with Object.
+
+// Upon creation of this object, it launches a thread, creates an object of type Object in that
+// thread, and then calls .start(); on that object. When the Thread<> object is destructed, the
+// Object's .stop() function is called, and the destructor waits for thread termination. The
+// Thread<> constructor blocks until the thread and the Object are fully created, so after the
+// object creation, it's safe to obtain the Object stored in this thread.
+
+template <class Object>
+class Thread {
+public:
+ template <class... Args>
+ Thread(const std::string& name, Args&&... args);
+ ~Thread();
+
+ // Invoke object->fn(args...) in the runloop thread.
+ template <typename Fn, class... Args>
+ void invoke(Fn fn, Args&&... args) {
+ loop->invoke(std::bind(fn, object, args...));
+ }
+
+ // Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
+ template <typename Fn, class R, class... Args>
+ void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
+ loop->invokeWithResult(std::bind(fn, object, args...), callback);
+ }
+
+ uv_loop_t* get() { return loop->get(); }
+
+private:
+ Thread(const Thread&) = delete;
+ Thread(Thread&&) = delete;
+ Thread& operator=(const Thread&) = delete;
+ Thread& operator=(Thread&&) = delete;
+
+ template <typename P, std::size_t... I>
+ void run(P&& params, index_sequence<I...>);
+
+ std::promise<void> running;
+ std::promise<void> joinable;
+
+ std::thread thread;
+
+ Object* object;
+ RunLoop* loop;
+};
+
+template <class Object>
+template <class... Args>
+Thread<Object>::Thread(const std::string& name, Args&&... args) {
+ // Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug
+ // when expanding parameters packs captured in lambdas.
+ std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
+
+ thread = std::thread([&] {
+ #ifdef __APPLE__
+ pthread_setname_np(name.c_str());
+ #else
+ (void(name));
+ #endif
+
+ constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type();
+ run(std::move(params), seq);
+ });
+
+ running.get_future().get();
+}
+
+template <class Object>
+template <typename P, std::size_t... I>
+void Thread<Object>::run(P&& params, index_sequence<I...>) {
+ Object object_(std::get<I>(std::forward<P>(params))...);
+ object = &object_;
+
+ RunLoop loop_;
+ loop = &loop_;
+
+ running.set_value();
+ loop_.run();
+
+ joinable.get_future().get();
+}
+
+template <class Object>
+Thread<Object>::~Thread() {
+ loop->stop();
+ joinable.set_value();
+ thread.join();
+}
+
+}
+}
+
+#endif
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
new file mode 100644
index 0000000000..5185e78d92
--- /dev/null
+++ b/src/mbgl/util/tile_cover.cpp
@@ -0,0 +1,93 @@
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/vec.hpp>
+#include <mbgl/util/box.hpp>
+
+namespace mbgl {
+
+// Taken from polymaps src/Layer.js
+// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
+
+struct edge {
+ double x0 = 0, y0 = 0;
+ double x1 = 0, y1 = 0;
+ double dx = 0, dy = 0;
+
+ edge(vec2<double> a, vec2<double> b) {
+ if (a.y > b.y) { std::swap(a, b); }
+ x0 = a.x;
+ y0 = a.y;
+ x1 = b.x;
+ y1 = b.y;
+ dx = b.x - a.x;
+ dy = b.y - a.y;
+ }
+};
+
+typedef const std::function<void(int32_t x0, int32_t x1, int32_t y)> ScanLine;
+
+// scan-line conversion
+static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
+ double y0 = std::fmax(ymin, std::floor(e1.y0));
+ double y1 = std::fmin(ymax, std::ceil(e1.y1));
+
+ // sort edges by x-coordinate
+ if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ?
+ (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) :
+ (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) {
+ std::swap(e0, e1);
+ }
+
+ // scan lines!
+ double m0 = e0.dx / e0.dy;
+ double m1 = e1.dx / e1.dy;
+ double d0 = e0.dx > 0; // use y + 1 to compute x0
+ double d1 = e1.dx < 0; // use y + 1 to compute x1
+ for (int32_t y = y0; y < y1; y++) {
+ double x0 = m0 * std::fmax(0, std::fmin(e0.dy, y + d0 - e0.y0)) + e0.x0;
+ double x1 = m1 * std::fmax(0, std::fmin(e1.dy, y + d1 - e1.y0)) + e1.x0;
+ scanLine(std::floor(x1), std::ceil(x0), y);
+ }
+}
+
+// scan-line conversion
+static void scanTriangle(const mbgl::vec2<double> a, const mbgl::vec2<double> b, const mbgl::vec2<double> c, int32_t ymin, int32_t ymax, ScanLine& scanLine) {
+ edge ab = edge(a, b);
+ edge bc = edge(b, c);
+ edge ca = edge(c, a);
+
+ // sort edges by y-length
+ if (ab.dy > bc.dy) { std::swap(ab, bc); }
+ if (ab.dy > ca.dy) { std::swap(ab, ca); }
+ if (bc.dy > ca.dy) { std::swap(bc, ca); }
+
+ // scan span! scan span!
+ if (ab.dy) scanSpans(ca, ab, ymin, ymax, scanLine);
+ if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine);
+}
+
+std::forward_list<TileID> tileCover(int8_t z, const mbgl::box &bounds) {
+ int32_t tiles = 1 << z;
+ std::forward_list<mbgl::TileID> t;
+
+ auto scanLine = [&](int32_t x0, int32_t x1, int32_t y) {
+ int32_t x;
+ if (y >= 0 && y <= tiles) {
+ for (x = x0; x < x1; x++) {
+ t.emplace_front(z, x, y);
+ }
+ }
+ };
+
+ // Divide the screen up in two triangles and scan each of them:
+ // \---+
+ // | \ |
+ // +---\.
+ scanTriangle(bounds.tl, bounds.tr, bounds.br, 0, tiles, scanLine);
+ scanTriangle(bounds.br, bounds.bl, bounds.tl, 0, tiles, scanLine);
+
+ t.unique();
+
+ return t;
+}
+
+}
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
new file mode 100644
index 0000000000..78121a30ba
--- /dev/null
+++ b/src/mbgl/util/tile_cover.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_UTIL_TILE_COVER
+#define MBGL_UTIL_TILE_COVER
+
+#include <mbgl/map/tile_id.hpp>
+#include <mbgl/util/box.hpp>
+
+#include <forward_list>
+
+namespace mbgl {
+
+std::forward_list<TileID> tileCover(int8_t z, const box& bounds);
+
+}
+
+#endif
diff --git a/src/mbgl/util/uv-channel.c b/src/mbgl/util/uv-channel.c
deleted file mode 100644
index 4e3b9fa5ff..0000000000
--- a/src/mbgl/util/uv-channel.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include <mbgl/util/uv-channel.h>
-#include <mbgl/util/queue.h>
-
-#include <stdlib.h>
-
-// Taken from http://navaneeth.github.io/blog/2013/08/02/channels-in-libuv/
-
-typedef struct {
- void *data;
- void *active_queue[2];
-} uv__chan_item_t;
-
-int uv_chan_init(uv_chan_t *chan) {
- int r = uv_mutex_init(&chan->mutex);
- if (r == -1)
- return r;
-
- QUEUE_INIT(&chan->q);
-
- return uv_cond_init(&chan->cond);
-}
-
-void uv_chan_send(uv_chan_t *chan, void *data) {
- uv__chan_item_t *item = (uv__chan_item_t *)malloc(sizeof(uv__chan_item_t));
- item->data = data;
-
- uv_mutex_lock(&chan->mutex);
- QUEUE_INSERT_TAIL(&chan->q, &item->active_queue);
- uv_cond_signal(&chan->cond);
- uv_mutex_unlock(&chan->mutex);
-}
-
-void *uv_chan_receive(uv_chan_t *chan) {
- uv__chan_item_t *item;
- QUEUE *head;
- void *data = NULL;
-
- uv_mutex_lock(&chan->mutex);
- while (QUEUE_EMPTY(&chan->q)) {
- uv_cond_wait(&chan->cond, &chan->mutex);
- }
-
- head = QUEUE_HEAD(&chan->q);
- item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
- data = item->data;
- QUEUE_REMOVE(head);
- free(item);
- uv_mutex_unlock(&chan->mutex);
- return data;
-}
-
-void uv_chan_clear(uv_chan_t *chan) {
- uv_mutex_lock(&chan->mutex);
- uv__chan_item_t *item = NULL;
- QUEUE *head = NULL;
- while (!QUEUE_EMPTY(&chan->q)) {
- head = QUEUE_HEAD(&chan->q);
- item = QUEUE_DATA(head, uv__chan_item_t, active_queue);
- QUEUE_REMOVE(head);
- free(item);
- }
- uv_mutex_unlock(&chan->mutex);
-}
-
-void uv_chan_destroy(uv_chan_t *chan) {
- uv_chan_clear(chan);
- uv_cond_destroy(&chan->cond);
- uv_mutex_destroy(&chan->mutex);
-}
diff --git a/src/mbgl/util/uv-channel.h b/src/mbgl/util/uv-channel.h
deleted file mode 100644
index 0a0d706477..0000000000
--- a/src/mbgl/util/uv-channel.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MBGL_UTIL_UV_CHANNEL
-#define MBGL_UTIL_UV_CHANNEL
-
-#include <uv.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// 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
diff --git a/src/mbgl/util/uv-messenger.c b/src/mbgl/util/uv-messenger.c
deleted file mode 100644
index 935b6f1c41..0000000000
--- a/src/mbgl/util/uv-messenger.c
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <mbgl/util/uv-messenger.h>
-#include <mbgl/util/queue.h>
-
-#include <stdlib.h>
-#include <assert.h>
-
-typedef struct {
- void *data;
- void *queue[2];
-} uv__messenger_item_t;
-
-#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-void uv__messenger_callback(uv_async_t *async, int status) {
-#pragma clang diagnostic pop
-#else
-void uv__messenger_callback(uv_async_t *async) {
-#endif
- uv_messenger_t *msgr = (uv_messenger_t *)async->data;
-
- uv__messenger_item_t *item;
- QUEUE *head;
-
- while (1) {
- uv_mutex_lock(&msgr->mutex);
- if (QUEUE_EMPTY(&msgr->queue)) {
- uv_mutex_unlock(&msgr->mutex);
- break;
- }
-
- head = QUEUE_HEAD(&msgr->queue);
- item = QUEUE_DATA(head, uv__messenger_item_t, queue);
- QUEUE_REMOVE(head);
- uv_mutex_unlock(&msgr->mutex);
-
- msgr->callback(item->data);
-
- free(item);
- }
-}
-
-int uv_messenger_init(uv_loop_t *loop, uv_messenger_t *msgr, uv_messenger_cb callback) {
- int ret = uv_mutex_init(&msgr->mutex);
- if (ret < 0) {
- return ret;
- }
-
- msgr->callback = callback;
- msgr->stop_callback = NULL;
-
- QUEUE_INIT(&msgr->queue);
-
- msgr->async.data = msgr;
- return uv_async_init(loop, &msgr->async, uv__messenger_callback);
-}
-
-void uv_messenger_send(uv_messenger_t *msgr, void *data) {
- uv__messenger_item_t *item = (uv__messenger_item_t *)malloc(sizeof(uv__messenger_item_t));
- item->data = data;
-
- uv_mutex_lock(&msgr->mutex);
- QUEUE_INSERT_TAIL(&msgr->queue, &item->queue);
- uv_mutex_unlock(&msgr->mutex);
-
- uv_async_send(&msgr->async);
-}
-
-void uv_messenger_ref(uv_messenger_t *msgr) {
- uv_ref((uv_handle_t *)&msgr->async);
-}
-
-void uv_messenger_unref(uv_messenger_t *msgr) {
- uv_unref((uv_handle_t *)&msgr->async);
-}
-
-void uv__messenger_stop_callback(uv_handle_t *handle) {
- uv_messenger_t *msgr = (uv_messenger_t *)handle->data;
- msgr->stop_callback(msgr);
-}
-
-void uv_messenger_stop(uv_messenger_t *msgr, uv_messenger_stop_cb stop_callback) {
- assert(!msgr->stop_callback);
- msgr->stop_callback = stop_callback;
- uv_close((uv_handle_t *)&msgr->async, uv__messenger_stop_callback);
-}
diff --git a/src/mbgl/util/uv-worker.c b/src/mbgl/util/uv-worker.c
deleted file mode 100644
index ec8eae461c..0000000000
--- a/src/mbgl/util/uv-worker.c
+++ /dev/null
@@ -1,170 +0,0 @@
-#include <mbgl/util/uv-worker.h>
-#include <mbgl/util/uv-messenger.h>
-
-#include <stdio.h>
-#include <assert.h>
-
-typedef struct uv__worker_item_s uv__worker_item_t;
-struct uv__worker_item_s {
- uv_worker_t *worker;
- void *data;
- uv_worker_cb work_cb;
- uv_worker_after_cb after_work_cb;
-};
-
-typedef struct uv__worker_thread_s uv__worker_thread_t;
-struct uv__worker_thread_s {
- uv_worker_t *worker;
- uv_thread_t thread;
-};
-
-void uv__worker_free_messenger(uv_messenger_t *msgr) {
- free(msgr);
-}
-
-void uv__worker_thread_finished(uv__worker_thread_t *worker_thread) {
- uv_worker_t *worker = worker_thread->worker;
-
-#ifdef DEBUG
- assert(uv_thread_self() == worker->thread_id);
-#endif
-
- // This should at most block very briefly. We are sending the termination
- // notification as the last thing in the worker thread, so by now the thread
- // has probably terminated already. If not, the waiting time should be
- // extremely short.
- uv_thread_join(&worker_thread->thread);
-
- assert(worker->count > 0);
- worker->count--;
- if (worker->count == 0) {
- uv_chan_destroy(&worker->chan);
- uv_messenger_stop(worker->msgr, uv__worker_free_messenger);
- if (worker->close_cb) {
- worker->close_cb(worker);
- }
- }
-}
-
-void uv__worker_after(void *ptr) {
- uv__worker_item_t *item = (uv__worker_item_t *)ptr;
-
- if (item->work_cb) {
- // We are finishing a regular work request.
- if (item->after_work_cb) {
- assert(item->after_work_cb);
- item->after_work_cb(item->data);
- }
- uv_worker_t *worker = item->worker;
- assert(worker->active_items > 0);
- if (--worker->active_items == 0) {
- uv_messenger_unref(worker->msgr);
- }
- } else {
- // This is a worker thread termination.
- uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)item->data;
- uv__worker_thread_finished(worker_thread);
- free(worker_thread);
- }
-
- free(item);
-}
-
-void uv__worker_thread_loop(void *ptr) {
- uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)ptr;
- uv_worker_t *worker = worker_thread->worker;
-
-#ifdef __APPLE__
- if (worker->name) {
- pthread_setname_np(worker->name);
- }
-#endif
-
- uv__worker_item_t *item = NULL;
- while ((item = (uv__worker_item_t *)uv_chan_receive(&worker->chan)) != NULL) {
- assert(item->work_cb);
- item->work_cb(item->data);
-
- // Trigger the after callback in the main thread.
- uv_messenger_send(worker->msgr, item);
- }
-
- // Make sure to close all other workers too.
- uv_chan_send(&worker->chan, NULL);
-
- // Create a new worker item that acts as a terminate flag for this thread.
- item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
- item->data = worker_thread;
- item->work_cb = NULL;
- item->after_work_cb = NULL;
- uv_messenger_send(worker->msgr, item);
-}
-
-int uv_worker_init(uv_worker_t *worker, uv_loop_t *loop, int count, const char *name) {
-#ifdef DEBUG
- worker->thread_id = uv_thread_self();
-#endif
- worker->loop = loop;
- worker->name = name;
- worker->count = 0;
- worker->close_cb = NULL;
- worker->active_items = 0;
- worker->msgr = (uv_messenger_t *)malloc(sizeof(uv_messenger_t));
- int ret = uv_messenger_init(loop, worker->msgr, uv__worker_after);
- if (ret < 0) {
- free(worker->msgr);
- return ret;
- }
- uv_messenger_unref(worker->msgr);
- ret = uv_chan_init(&worker->chan);
- if (ret < 0) return ret;
-
- // Initialize all worker threads.
- int i;
- for (i = 0; i < count; i++) {
- uv__worker_thread_t *worker_thread = (uv__worker_thread_t *)malloc(sizeof(uv__worker_thread_t));
- worker_thread->worker = worker;
- ret = uv_thread_create(&worker_thread->thread, uv__worker_thread_loop, worker_thread);
- if (ret < 0) return ret;
- worker->count++;
- }
-
- return 0;
-}
-
-void uv_worker_send(uv_worker_t *worker, void *data, uv_worker_cb work_cb,
- uv_worker_after_cb after_work_cb) {
-#ifdef DEBUG
- assert(uv_thread_self() == worker->thread_id);
-#endif
-
- // It doesn't make sense to not provide a work callback. On the other hand, the after_work_cb
- // may be NULL. In that case, there will be no callback called in the current thread and the
- // worker item will instead be freed in the worker thread.
- assert(work_cb);
-
- uv__worker_item_t *item = (uv__worker_item_t *)malloc(sizeof(uv__worker_item_t));
- item->worker = worker;
- item->work_cb = work_cb;
- item->after_work_cb = after_work_cb;
- item->data = data;
- uv_chan_send(&worker->chan, item);
- if (worker->active_items++ == 0) {
- uv_messenger_ref(worker->msgr);
- }
-}
-
-void uv_worker_close(uv_worker_t *worker, uv_worker_close_cb close_cb) {
-#ifdef DEBUG
- assert(uv_thread_self() == worker->thread_id);
-#endif
-
- // Prevent double calling.
- assert(worker->close_cb == NULL);
-
- worker->close_cb = close_cb;
- uv_chan_send(&worker->chan, NULL);
- if (worker->active_items++ == 0) {
- uv_messenger_ref(worker->msgr);
- }
-}
diff --git a/src/mbgl/util/uv-worker.h b/src/mbgl/util/uv-worker.h
deleted file mode 100644
index 65ad42edb8..0000000000
--- a/src/mbgl/util/uv-worker.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef MBGL_UTIL_UV_WORKER
-#define MBGL_UTIL_UV_WORKER
-
-#include <stdlib.h>
-
-#include <mbgl/util/uv-messenger.h>
-#include <mbgl/util/uv-channel.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-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 {
-#ifdef DEBUG
- 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
diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp
index d465dfd963..5dae34ebd0 100644
--- a/src/mbgl/util/uv.cpp
+++ b/src/mbgl/util/uv.cpp
@@ -3,6 +3,28 @@
#include <uv.h>
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+
+int uv_key_create(uv_key_t* key) {
+ return -pthread_key_create(key, NULL);
+}
+
+void uv_key_delete(uv_key_t* key) {
+ if (pthread_key_delete(*key))
+ abort();
+}
+
+void* uv_key_get(uv_key_t* key) {
+ return pthread_getspecific(*key);
+}
+
+void uv_key_set(uv_key_t* key, void* value) {
+ if (pthread_setspecific(*key, value))
+ abort();
+}
+
+#endif
+
namespace uv {
std::string cwd() {
diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp
index 6ae3713e09..96d5442462 100644
--- a/src/mbgl/util/uv_detail.hpp
+++ b/src/mbgl/util/uv_detail.hpp
@@ -2,7 +2,6 @@
#define MBGL_UTIL_UV_DETAIL
#include <mbgl/util/uv.hpp>
-#include <mbgl/util/uv-worker.h>
#include <mbgl/util/noncopyable.hpp>
#include <uv.h>
@@ -12,6 +11,21 @@
#include <memory>
#include <string>
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+
+// Add thread local storage to libuv API:
+// https://github.com/joyent/libuv/commit/5d2434bf71e47802841bad218d521fa254d1ca2d
+
+typedef pthread_key_t uv_key_t;
+
+UV_EXTERN int uv_key_create(uv_key_t* key);
+UV_EXTERN void uv_key_delete(uv_key_t* key);
+UV_EXTERN void* uv_key_get(uv_key_t* key);
+UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
+
+#endif
+
+
namespace uv {
template <class T>
@@ -42,10 +56,19 @@ public:
uv_loop_close(l);
delete l;
#endif
+ }
+
+ inline void run() {
+ uv_run(l, UV_RUN_DEFAULT);
+ }
+ inline uv_loop_t* operator*() {
+ return l;
}
- inline uv_loop_t *operator*() { return l; }
+ inline uv_loop_t* get() {
+ return l;
+ }
private:
uv_loop_t *l = nullptr;
@@ -73,6 +96,14 @@ public:
}
}
+ inline void ref() {
+ uv_ref(reinterpret_cast<uv_handle_t*>(a.get()));
+ }
+
+ inline void unref() {
+ uv_unref(reinterpret_cast<uv_handle_t*>(a.get()));
+ }
+
private:
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
static void async_cb(uv_async_t* a, int) {
@@ -117,54 +148,20 @@ private:
uv_rwlock_t mtx;
};
-class worker : public mbgl::util::noncopyable {
-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 mbgl::util::noncopyable {
+template <class T>
+class tls : public mbgl::util::noncopyable {
public:
- typedef std::function<void (T&)> work_callback;
- typedef std::function<void (T&)> after_work_callback;
-
- template<typename... 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_) {
- worker.add(this, do_work, after_work);
- }
-
-private:
- static void do_work(void *data) {
- work<T> *w = reinterpret_cast<work<T> *>(data);
- w->work_cb(w->data);
- }
-
- static void after_work(void *data) {
- work<T> *w = reinterpret_cast<work<T> *>(data);
- w->after_work_cb(w->data);
- delete w;
+ inline tls() {
+ if (uv_key_create(&key) != 0) {
+ throw std::runtime_error("failed to initialize thread local storage key");
+ }
}
+ inline ~tls() { uv_key_delete(&key); }
+ inline T* get() { return reinterpret_cast<T*>(uv_key_get(&key)); }
+ inline void set(T* val) { uv_key_set(&key, val); }
private:
- T data;
- work_callback work_cb;
- after_work_callback after_work_cb;
+ uv_key_t key;
};
}
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
new file mode 100644
index 0000000000..3559cdd71f
--- /dev/null
+++ b/src/mbgl/util/worker.cpp
@@ -0,0 +1,73 @@
+#include <mbgl/util/worker.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+Worker::Worker(uv_loop_t* loop, std::size_t count)
+ : queue(new Queue(loop, [this](Fn after) { afterWork(after); }))
+{
+ queue->unref();
+
+ for (std::size_t i = 0; i < count; i++) {
+ threads.emplace_back(&Worker::workLoop, this);
+ }
+}
+
+Worker::~Worker() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (active++ == 0) {
+ queue->ref();
+ }
+
+ channel.send(Work());
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ queue->stop();
+}
+
+void Worker::send(Fn work, Fn after) {
+ MBGL_VERIFY_THREAD(tid);
+ assert(work);
+
+ if (active++ == 0) {
+ queue->ref();
+ }
+
+ channel.send({work, after});
+}
+
+void Worker::workLoop() {
+#ifdef __APPLE__
+ pthread_setname_np("Worker");
+#endif
+
+ while (true) {
+ Work item = channel.receive();
+
+ if (!item.work)
+ break;
+
+ item.work();
+ queue->send(std::move(item.after));
+ }
+
+ // Make sure to close all other workers too.
+ channel.send(Work());
+}
+
+void Worker::afterWork(Fn after) {
+ if (after) {
+ after();
+ }
+
+ if (--active == 0) {
+ queue->unref();
+ }
+}
+
+}
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
new file mode 100644
index 0000000000..86c2e6acf4
--- /dev/null
+++ b/src/mbgl/util/worker.hpp
@@ -0,0 +1,44 @@
+#ifndef MBGL_UTIL_WORKER
+#define MBGL_UTIL_WORKER
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/async_queue.hpp>
+#include <mbgl/util/channel.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <thread>
+#include <functional>
+
+namespace mbgl {
+
+class Worker : public mbgl::util::noncopyable {
+public:
+ using Fn = std::function<void ()>;
+
+ Worker(uv_loop_t* loop, std::size_t count);
+ ~Worker();
+
+ void send(Fn work, Fn after);
+
+private:
+ void workLoop();
+ void afterWork(Fn after);
+
+ struct Work {
+ Fn work;
+ Fn after;
+ };
+
+ using Queue = util::AsyncQueue<std::function<void ()>>;
+
+ std::size_t active = 0;
+ Queue* queue = nullptr;
+ Channel<Work> channel;
+ std::vector<std::thread> threads;
+
+ MBGL_STORE_THREAD(tid)
+};
+
+}
+
+#endif