diff options
author | Jason Wray <jason@kulturny.com> | 2015-04-27 17:49:24 -0400 |
---|---|---|
committer | Jason Wray <jason@kulturny.com> | 2015-04-27 17:49:24 -0400 |
commit | 4d631623c7d29e8d40720e521e78c3299995b674 (patch) | |
tree | f8495149badf9b0fc76a51d5bde63c8bc375a709 /src/mbgl/util | |
parent | 7ac01660d7efd8ce7939a7934a6f5546e26f6c86 (diff) | |
parent | 99ab9c3c6debdf492aff7a751d82400eba1b1cdf (diff) | |
download | qtlocation-mapboxgl-4d631623c7d29e8d40720e521e78c3299995b674.tar.gz |
Merge master @ 353e5661de
Diffstat (limited to 'src/mbgl/util')
-rw-r--r-- | src/mbgl/util/channel.hpp | 39 | ||||
-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.hpp | 20 | ||||
-rw-r--r-- | src/mbgl/util/io.cpp | 10 | ||||
-rw-r--r-- | src/mbgl/util/io.hpp | 9 | ||||
-rw-r--r-- | src/mbgl/util/queue.h | 92 | ||||
-rw-r--r-- | src/mbgl/util/run_loop.cpp | 39 | ||||
-rw-r--r-- | src/mbgl/util/run_loop.hpp | 89 | ||||
-rw-r--r-- | src/mbgl/util/thread.hpp | 121 | ||||
-rw-r--r-- | src/mbgl/util/tile_cover.cpp | 93 | ||||
-rw-r--r-- | src/mbgl/util/tile_cover.hpp | 15 | ||||
-rw-r--r-- | src/mbgl/util/uv-channel.c | 69 | ||||
-rw-r--r-- | src/mbgl/util/uv-channel.h | 29 | ||||
-rw-r--r-- | src/mbgl/util/uv-messenger.c | 86 | ||||
-rw-r--r-- | src/mbgl/util/uv-worker.c | 170 | ||||
-rw-r--r-- | src/mbgl/util/uv-worker.h | 41 | ||||
-rw-r--r-- | src/mbgl/util/uv.cpp | 22 | ||||
-rw-r--r-- | src/mbgl/util/uv_detail.hpp | 89 | ||||
-rw-r--r-- | src/mbgl/util/worker.cpp | 73 | ||||
-rw-r--r-- | src/mbgl/util/worker.hpp | 44 |
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 |