Introduce the ThreadContext
mbgl::Thread will keep a ThreadContext for each running instance in a thread_local so we don't need to lookup a man in the Environment every time we need some info about the current thread. This patch is moving the ::currentlyOn check used on Debug build from the Environment class to the ThreadContext.
This commit is contained in:
@@ -62,7 +62,7 @@ std::string unifyMapboxURLs(const std::string &url) {
|
||||
using namespace mapbox::sqlite;
|
||||
|
||||
SQLiteCache::SQLiteCache(const std::string& path_)
|
||||
: thread(std::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
|
||||
: thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"SQLite Cache", util::ThreadType::Unknown, util::ThreadPriority::Low}, path_)) {
|
||||
}
|
||||
|
||||
SQLiteCache::~SQLiteCache() = default;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace mbgl {
|
||||
|
||||
Map::Map(View& view, FileSource& fileSource, MapMode mode)
|
||||
: data(std::make_unique<MapData>(view, mode)),
|
||||
context(std::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data))
|
||||
context(std::make_unique<util::Thread<MapContext>>(util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, *data))
|
||||
{
|
||||
view.initialize(this);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, Map
|
||||
updated(static_cast<UpdateType>(Update::Nothing)),
|
||||
asyncUpdate(std::make_unique<uv::async>(loop, [this] { update(); })),
|
||||
texturePool(std::make_unique<TexturePool>()) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
asyncUpdate->unref();
|
||||
|
||||
@@ -108,7 +108,7 @@ void MapContext::setStyleJSON(const std::string& json, const std::string& base)
|
||||
}
|
||||
|
||||
void MapContext::loadStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
style.reset();
|
||||
style = std::make_unique<Style>(json, base, asyncUpdate->get()->loop, env);
|
||||
@@ -125,13 +125,13 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base)
|
||||
}
|
||||
|
||||
void MapContext::updateTiles() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
style->update(data, transformState, *texturePool);
|
||||
}
|
||||
|
||||
void MapContext::updateAnnotationTiles(const std::unordered_set<TileID, TileID::Hash>& ids) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
data.annotationManager.markStaleTiles(ids);
|
||||
|
||||
@@ -232,7 +232,7 @@ void MapContext::recalculateStyle(TimePoint now) {
|
||||
}
|
||||
|
||||
void MapContext::update() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
const auto now = Clock::now();
|
||||
data.setAnimationTime(now);
|
||||
@@ -303,7 +303,7 @@ void MapContext::renderStill(StillImageCallback fn) {
|
||||
}
|
||||
|
||||
void MapContext::render() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
// Cleanup OpenGL objects that we abandoned since the last render call.
|
||||
env.performCleanup();
|
||||
@@ -335,13 +335,13 @@ void MapContext::render() {
|
||||
}
|
||||
|
||||
double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
const SpritePosition pos = style->sprite->getSpritePosition(symbol);
|
||||
return -pos.height / pos.pixelRatio / 2;
|
||||
}
|
||||
|
||||
void MapContext::setSourceTileCacheSize(size_t size) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
if (size != sourceCacheSize) {
|
||||
sourceCacheSize = size;
|
||||
if (!style) return;
|
||||
@@ -353,7 +353,7 @@ void MapContext::setSourceTileCacheSize(size_t size) {
|
||||
}
|
||||
|
||||
void MapContext::onLowMemory() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
if (!style) return;
|
||||
for (const auto &source : style->sources) {
|
||||
source->onLowMemory();
|
||||
@@ -362,12 +362,12 @@ void MapContext::onLowMemory() {
|
||||
}
|
||||
|
||||
void MapContext::onTileDataChanged() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
if (data.mode == MapMode::Still && callback) {
|
||||
callback(error, nullptr);
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace algo = boost::algorithm;
|
||||
namespace mbgl {
|
||||
|
||||
DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root)
|
||||
: thread(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
|
||||
: thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"FileSource", util::ThreadType::Unknown, util::ThreadPriority::Low}, cache, root)) {
|
||||
}
|
||||
|
||||
DefaultFileSource::~DefaultFileSource() {
|
||||
|
||||
@@ -159,7 +159,7 @@ bool Style::isLoaded() const {
|
||||
}
|
||||
|
||||
void Style::setObserver(Observer* observer_) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
assert(!observer);
|
||||
|
||||
observer = observer_;
|
||||
@@ -206,7 +206,7 @@ void Style::onSpriteLoadingFailed(std::exception_ptr error) {
|
||||
}
|
||||
|
||||
void Style::emitTileDataChanged() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
if (observer) {
|
||||
observer->onTileDataChanged();
|
||||
@@ -214,7 +214,7 @@ void Style::emitTileDataChanged() {
|
||||
}
|
||||
|
||||
void Style::emitResourceLoadingFailed(std::exception_ptr error) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
|
||||
try {
|
||||
if (error) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/thread_context.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -21,16 +22,11 @@ namespace util {
|
||||
// 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.
|
||||
|
||||
enum class ThreadPriority : bool {
|
||||
Regular,
|
||||
Low,
|
||||
};
|
||||
|
||||
template <class Object>
|
||||
class Thread {
|
||||
public:
|
||||
template <class... Args>
|
||||
Thread(const std::string& name, ThreadPriority priority, Args&&... args);
|
||||
Thread(const ThreadContext&, Args&&... args);
|
||||
~Thread();
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread.
|
||||
@@ -81,7 +77,7 @@ private:
|
||||
}
|
||||
|
||||
template <typename P, std::size_t... I>
|
||||
void run(P&& params, std::index_sequence<I...>);
|
||||
void run(ThreadContext, P&& params, std::index_sequence<I...>);
|
||||
|
||||
std::promise<void> running;
|
||||
std::promise<void> joinable;
|
||||
@@ -94,23 +90,21 @@ private:
|
||||
|
||||
template <class Object>
|
||||
template <class... Args>
|
||||
Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&... args) {
|
||||
Thread<Object>::Thread(const ThreadContext& context, 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));
|
||||
pthread_setname_np(context.name.c_str());
|
||||
#endif
|
||||
|
||||
if (priority == ThreadPriority::Low) {
|
||||
if (context.priority == ThreadPriority::Low) {
|
||||
platform::makeThreadLowPriority();
|
||||
}
|
||||
|
||||
run(std::move(params), std::index_sequence_for<Args...>{});
|
||||
run(context, std::move(params), std::index_sequence_for<Args...>{});
|
||||
});
|
||||
|
||||
running.get_future().get();
|
||||
@@ -118,10 +112,12 @@ Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&.
|
||||
|
||||
template <class Object>
|
||||
template <typename P, std::size_t... I>
|
||||
void Thread<Object>::run(P&& params, std::index_sequence<I...>) {
|
||||
void Thread<Object>::run(ThreadContext context, P&& params, std::index_sequence<I...>) {
|
||||
uv::loop l;
|
||||
|
||||
{
|
||||
ThreadContext::current.set(&context);
|
||||
|
||||
RunLoop loop_(l.get());
|
||||
loop = &loop_;
|
||||
|
||||
@@ -133,6 +129,8 @@ void Thread<Object>::run(P&& params, std::index_sequence<I...>) {
|
||||
|
||||
loop = nullptr;
|
||||
object = nullptr;
|
||||
|
||||
ThreadContext::current.set(nullptr);
|
||||
}
|
||||
|
||||
// Run the loop again to ensure that async close callbacks have been called.
|
||||
|
||||
34
src/mbgl/util/thread_context.cpp
Normal file
34
src/mbgl/util/thread_context.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <mbgl/util/thread_context.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
class MainThreadContextRegistrar {
|
||||
public:
|
||||
MainThreadContextRegistrar() : context("Main", ThreadType::Main, ThreadPriority::Regular) {
|
||||
ThreadContext::current.set(&context);
|
||||
}
|
||||
|
||||
~MainThreadContextRegistrar() {
|
||||
ThreadContext::current.set(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadContext context;
|
||||
};
|
||||
|
||||
ThreadContext::ThreadContext(const std::string& name_, ThreadType type_, ThreadPriority priority_)
|
||||
: name(name_),
|
||||
type(type_),
|
||||
priority(priority_) {
|
||||
}
|
||||
|
||||
uv::tls<ThreadContext> ThreadContext::current;
|
||||
|
||||
// Will auto register the main thread context
|
||||
// at startup. Must be instantiated after the
|
||||
// ThreadContext::current object.
|
||||
MainThreadContextRegistrar registrar;
|
||||
|
||||
}
|
||||
}
|
||||
55
src/mbgl/util/thread_context.hpp
Normal file
55
src/mbgl/util/thread_context.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef MBGL_UTIL_THREAD_CONTEXT
|
||||
#define MBGL_UTIL_THREAD_CONTEXT
|
||||
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
enum class ThreadPriority : bool {
|
||||
Regular,
|
||||
Low,
|
||||
};
|
||||
|
||||
enum class ThreadType : uint8_t {
|
||||
Main,
|
||||
Map,
|
||||
Worker,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct ThreadContext {
|
||||
public:
|
||||
ThreadContext(const std::string& name, ThreadType type, ThreadPriority priority);
|
||||
|
||||
static bool currentlyOn(ThreadType type) {
|
||||
return current.get()->type == type;
|
||||
}
|
||||
|
||||
static std::string getName() {
|
||||
return current.get()->name;
|
||||
}
|
||||
|
||||
static ThreadPriority getPriority() {
|
||||
return current.get()->priority;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
ThreadType type;
|
||||
ThreadPriority priority;
|
||||
|
||||
static uv::tls<ThreadContext> current;
|
||||
|
||||
friend class MainThreadContextRegistrar;
|
||||
template <class Object> friend class Thread;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -191,6 +191,10 @@ private:
|
||||
template <class T>
|
||||
class tls : public mbgl::util::noncopyable {
|
||||
public:
|
||||
inline tls(T* val) {
|
||||
tls();
|
||||
set(val);
|
||||
}
|
||||
inline tls() {
|
||||
if (uv_key_create(&key) != 0) {
|
||||
throw std::runtime_error("failed to initialize thread local storage key");
|
||||
|
||||
@@ -18,8 +18,9 @@ public:
|
||||
};
|
||||
|
||||
Worker::Worker(std::size_t count) {
|
||||
util::ThreadContext context = {"Worker", util::ThreadType::Worker, util::ThreadPriority::Low};
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
threads.emplace_back(std::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
|
||||
threads.emplace_back(std::make_unique<util::Thread<Impl>>(context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ TEST(MapContext, DoubleStyleLoad) {
|
||||
DefaultFileSource fileSource(nullptr);
|
||||
MapData data(view, MapMode::Continuous);
|
||||
|
||||
util::Thread<MapContext> context("Map", util::ThreadPriority::Regular, view, fileSource, data);
|
||||
util::Thread<MapContext> context({"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, data);
|
||||
|
||||
context.invokeSync(&MapContext::setStyleJSON, "", "");
|
||||
context.invokeSync(&MapContext::setStyleJSON, "", "");
|
||||
|
||||
@@ -64,7 +64,7 @@ TEST(Thread, invoke) {
|
||||
|
||||
loop.invoke([&] {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
Thread<TestObject> thread("Test", ThreadPriority::Regular, tid);
|
||||
Thread<TestObject> thread({"Test", ThreadType::Map, ThreadPriority::Regular}, tid);
|
||||
|
||||
thread.invoke(&TestObject::fn1, 1);
|
||||
thread.invokeWithResult<int>(&TestObject::fn2, [&] (int result) {
|
||||
|
||||
@@ -133,7 +133,7 @@ void MockFileSource::Impl::dispatchPendingRequests() {
|
||||
}
|
||||
|
||||
MockFileSource::MockFileSource(Type type, const std::string& match)
|
||||
: thread_(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, type, match)) {
|
||||
: thread_(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"FileSource", util::ThreadType::Unknown, util::ThreadPriority::Low}, type, match)) {
|
||||
}
|
||||
|
||||
void MockFileSource::setOnRequestDelayedCallback(std::function<void(void)> callback) {
|
||||
|
||||
@@ -117,7 +117,7 @@ void runTestCase(MockFileSource::Type type,
|
||||
|
||||
std::unique_ptr<util::Thread<MockMapContext>> context(
|
||||
std::make_unique<util::Thread<MockMapContext>>(
|
||||
"Map", util::ThreadPriority::Regular, view, fileSource, callback));
|
||||
util::ThreadContext{"Map", util::ThreadType::Map, util::ThreadPriority::Regular}, view, fileSource, callback));
|
||||
|
||||
uv_run(loop.get(), UV_RUN_DEFAULT);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user