diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp index ed2b364ce..068215a04 100644 --- a/src/mbgl/util/run_loop.hpp +++ b/src/mbgl/util/run_loop.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -19,37 +20,38 @@ public: void stop(); - // Invoke fn() in the runloop thread. - template - void invoke(Fn&& fn) { - auto invokable = util::make_unique>(std::move(fn)); + // Invoke fn(args...) on this RunLoop. + template + void invoke(Fn&& fn, Args&&... args) { + auto tuple = std::make_tuple(std::move(args)...); + auto invokable = util::make_unique>(std::move(fn), std::move(tuple)); withMutex([&] { queue.push(std::move(invokable)); }); async.send(); } - // Invoke fn() in the runloop thread, then invoke callback(result) in the current thread. - template - void invokeWithResult(Fn&& fn, std::function&& callback) { - RunLoop* outer = current.get(); - assert(outer); - - invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable { - outer->invoke([callback = std::move(callback), result = std::move(fn())] () mutable { - callback(std::move(result)); - }); - }); + // Return a function that invokes the given function on this RunLoop. + template + auto bind(std::function fn) { + return [this, fn = std::move(fn)] (Args&&... args) { + invoke(std::move(fn), std::move(args)...); + }; } - // Invoke fn() in the runloop thread, then invoke callback() in the current thread. - template - void invokeWithResult(Fn&& fn, std::function&& callback) { - RunLoop* outer = current.get(); - assert(outer); + // Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop. + template + void invokeWithResult(Fn&& fn, std::function callback, Args&&... args) { + invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable { + callback(fn(std::forward(a)...)); + }, std::forward(args)...); + } - invoke([fn = std::move(fn), callback = std::move(callback), outer] () mutable { - fn(); - outer->invoke(std::move(callback)); - }); + // Invoke fn(args...) on this RunLoop, then invoke callback() on the current RunLoop. + template + void invokeWithResult(Fn&& fn, std::function callback, Args&&... args) { + invoke([fn = std::move(fn), callback = current.get()->bind(callback)] (Args&&... a) mutable { + fn(std::forward(a)...); + callback(); + }, std::forward(args)...); } uv_loop_t* get() { return async.get()->loop; } @@ -65,11 +67,24 @@ private: virtual ~Message() = default; }; - template + template struct Invoker : Message { - Invoker(F&& f) : func(std::move(f)) {} - void operator()() override { func(); } + Invoker(F&& f, P&& p) + : func(std::move(f)), + params(std::move(p)) { + } + + void operator()() override { + invoke(std::index_sequence_for{}); + } + + template + void invoke(std::index_sequence) { + func(std::forward(std::get(params))...); + } + F func; + P params; }; using Queue = std::queue>; diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index a2ad95864..f3a9baa6f 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -4,26 +4,12 @@ #include #include #include +#include #include #include #include -namespace { - -template <::std::size_t...> -struct index_sequence {}; - -template <::std::size_t N, ::std::size_t... I> -struct integer_sequence : integer_sequence {}; - -template <::std::size_t... I> -struct integer_sequence<0, I...> { - using type = index_sequence; -}; - -} - namespace mbgl { namespace util { @@ -50,19 +36,19 @@ public: // Invoke object->fn(args...) in the runloop thread. template void invoke(Fn fn, Args&&... args) { - loop->invoke(std::bind(fn, object, args...)); + loop->invoke(bind(fn), std::forward(args)...); } // Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread. - template - void invokeWithResult(Fn fn, std::function&& callback, Args&&... args) { - loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback)); + template + void invokeWithResult(Fn fn, std::function callback, Args&&... args) { + loop->invokeWithResult(bind(fn), callback, std::forward(args)...); } // Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread. template - void invokeWithResult(Fn fn, std::function&& callback, Args&&... args) { - loop->invokeWithResult(std::bind(fn, object, std::move(args)...), std::move(callback)); + void invokeWithResult(Fn fn, std::function callback, Args&&... args) { + loop->invokeWithResult(bind(fn), callback, std::forward(args)...); } // Invoke object->fn(args...) in the runloop thread, and wait for the result. @@ -89,8 +75,13 @@ private: Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; + template + auto bind(Fn fn) { + return [fn, this] (Args&&... a) { return (object->*fn)(std::forward(a)...); }; + } + template - void run(P&& params, index_sequence); + void run(P&& params, std::index_sequence); std::promise running; std::promise joinable; @@ -119,8 +110,7 @@ Thread::Thread(const std::string& name, ThreadPriority priority, Args&&. platform::makeThreadLowPriority(); } - constexpr auto seq = typename integer_sequence::type(); - run(std::move(params), seq); + run(std::move(params), std::index_sequence_for{}); }); running.get_future().get(); @@ -128,7 +118,7 @@ Thread::Thread(const std::string& name, ThreadPriority priority, Args&&. template template -void Thread::run(P&& params, index_sequence) { +void Thread::run(P&& params, std::index_sequence) { uv::loop l; { diff --git a/test/miscellaneous/thread.cpp b/test/miscellaneous/thread.cpp new file mode 100644 index 000000000..b0dd2210e --- /dev/null +++ b/test/miscellaneous/thread.cpp @@ -0,0 +1,102 @@ +#include +#include + +#include "../fixtures/util.hpp" + +using namespace mbgl::util; + +class TestObject { +public: + TestObject(uv_loop_t*, std::thread::id otherTid) + : tid(std::this_thread::get_id()) { + EXPECT_NE(tid, otherTid); + } + + void fn1(int val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(val, 1); + } + + int fn2() { + EXPECT_EQ(tid, std::this_thread::get_id()); + return 1; + } + + void transferIn(std::unique_ptr val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + } + + std::unique_ptr transferOut() { + EXPECT_EQ(tid, std::this_thread::get_id()); + return std::make_unique(1); + } + + std::unique_ptr transferInOut(std::unique_ptr val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + return std::move(val); + } + + void transferInShared(std::shared_ptr val) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*val, 1); + } + + std::shared_ptr transferOutShared() { + EXPECT_EQ(tid, std::this_thread::get_id()); + return std::make_shared(1); + } + + std::string transferString(const std::string& string) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(string, "test"); + return string; + } + + const std::thread::id tid; +}; + +TEST(Thread, invoke) { + const std::thread::id tid = std::this_thread::get_id(); + + RunLoop loop(uv_default_loop()); + + loop.invoke([&] { + EXPECT_EQ(tid, std::this_thread::get_id()); + Thread thread("Test", ThreadPriority::Regular, tid); + + thread.invoke(&TestObject::fn1, 1); + thread.invokeWithResult(&TestObject::fn2, [&] (int result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(result, 1); + }); + + thread.invoke(&TestObject::transferIn, std::make_unique(1)); + thread.invokeWithResult>(&TestObject::transferOut, [&] (std::unique_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + }); + + thread.invokeWithResult>(&TestObject::transferInOut, [&] (std::unique_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + }, std::make_unique(1)); + + thread.invoke(&TestObject::transferInShared, std::make_shared(1)); + thread.invokeWithResult>(&TestObject::transferOutShared, [&] (std::shared_ptr result) { + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(*result, 1); + }); + + std::string test("test"); + thread.invokeWithResult(&TestObject::transferString, [&] (std::string result){ + EXPECT_EQ(tid, std::this_thread::get_id()); + EXPECT_EQ(result, "test"); + loop.stop(); + }, test); + test.clear(); + }); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); +} diff --git a/test/test.gypi b/test/test.gypi index 75301751c..926a899cf 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -53,8 +53,9 @@ 'miscellaneous/rotation_range.cpp', 'miscellaneous/style_parser.cpp', 'miscellaneous/text_conversions.cpp', - 'miscellaneous/transform.cpp', + 'miscellaneous/thread.cpp', 'miscellaneous/tile.cpp', + 'miscellaneous/transform.cpp', 'miscellaneous/variant.cpp', 'miscellaneous/worker.cpp',