diff options
author | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-06-14 16:30:47 +0300 |
---|---|---|
committer | Thiago Marcos P. Santos <tmpsantos@gmail.com> | 2017-06-21 14:30:09 +0300 |
commit | 1427628c56d336a21c93ef0a1c57fbfabd98dc5e (patch) | |
tree | d2bff72ce0e07cd140436a61bbd464f7fdfcdc2d | |
parent | bfdc4ef64655bf1bbfd216072500e701a9af6d27 (diff) | |
download | qtlocation-mapboxgl-1427628c56d336a21c93ef0a1c57fbfabd98dc5e.tar.gz |
[core] Remove util::Thread
Fixes #6425
-rw-r--r-- | cmake/core-files.cmake | 3 | ||||
-rw-r--r-- | cmake/test-files.cmake | 1 | ||||
-rw-r--r-- | platform/default/online_file_source.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/util/logging.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/util/thread.hpp | 173 | ||||
-rw-r--r-- | src/mbgl/util/thread_context.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/util/thread_context.hpp | 22 | ||||
-rw-r--r-- | test/util/thread.test.cpp | 296 |
8 files changed, 1 insertions, 510 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 729d3f7da3..220e25d35e 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -603,9 +603,6 @@ set(MBGL_CORE_FILES src/mbgl/util/stopwatch.cpp src/mbgl/util/stopwatch.hpp src/mbgl/util/string.cpp - src/mbgl/util/thread.hpp - src/mbgl/util/thread_context.cpp - src/mbgl/util/thread_context.hpp src/mbgl/util/thread_local.hpp src/mbgl/util/threaded_object.hpp src/mbgl/util/throttler.cpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index da1c046485..f85ca03333 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -131,7 +131,6 @@ set(MBGL_TEST_FILES test/util/projection.test.cpp test/util/run_loop.test.cpp test/util/text_conversions.test.cpp - test/util/thread.test.cpp test/util/threaded_object.test.cpp test/util/thread_local.test.cpp test/util/tile_cover.test.cpp diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index d7649a348c..4f5483d794 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -6,7 +6,6 @@ #include <mbgl/util/logging.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/thread.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/chrono.hpp> diff --git a/src/mbgl/util/logging.cpp b/src/mbgl/util/logging.cpp index 939f1def64..0552eb36cb 100644 --- a/src/mbgl/util/logging.cpp +++ b/src/mbgl/util/logging.cpp @@ -1,6 +1,6 @@ #include <mbgl/util/logging.hpp> #include <mbgl/util/enum.hpp> -#include <mbgl/util/thread.hpp> +#include <mbgl/util/platform.hpp> #include <cstdio> #include <cstdarg> diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp deleted file mode 100644 index 4afaab6b7a..0000000000 --- a/src/mbgl/util/thread.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include <cassert> -#include <future> -#include <thread> -#include <atomic> -#include <utility> -#include <functional> - -#include <mbgl/util/run_loop.hpp> -#include <mbgl/util/thread_context.hpp> -#include <mbgl/util/platform.hpp> -#include <mbgl/util/util.hpp> - -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: - Thread(const Thread&) = delete; - Thread(Thread&&) = delete; - Thread& operator=(const Thread&) = delete; - Thread& operator=(Thread&&) = delete; - - template <class... Args> - Thread(const ThreadContext&, Args&&... args); - ~Thread(); - - // Invoke object->fn(args...) asynchronously. - template <typename Fn, class... Args> - void invoke(Fn fn, Args&&... args) { - loop->invoke(bind(fn), std::forward<Args>(args)...); - } - - // Invoke object->fn(args...) asynchronously. The final argument to fn must be a callback. - // The provided callback is wrapped such that it is invoked, in the current thread (which - // must have a RunLoop), once for each time the invocation of fn invokes the wrapper, each - // time forwarding the passed arguments, until such time as the AsyncRequest is cancelled. - template <typename Fn, class... Args> - std::unique_ptr<AsyncRequest> - invokeWithCallback(Fn fn, Args&&... args) { - return loop->invokeWithCallback(bind(fn), std::forward<Args>(args)...); - } - - // Invoke object->fn(args...) asynchronously, but wait for the result. - template <typename Fn, class... Args> - auto invokeSync(Fn fn, Args&&... args) { - assert(!paused); - - using R = std::result_of_t<Fn(Object, Args&&...)>; - std::packaged_task<R ()> task(std::bind(fn, object, args...)); - std::future<R> future = task.get_future(); - loop->invoke(std::move(task)); - return future.get(); - } - - void pause() { - MBGL_VERIFY_THREAD(tid); - - assert(!paused); - - paused = std::make_unique<std::promise<void>>(); - resumed = std::make_unique<std::promise<void>>(); - - auto pausing = paused->get_future(); - - loop->invoke([this] { - auto resuming = resumed->get_future(); - paused->set_value(); - resuming.get(); - }); - - pausing.get(); - } - - void resume() { - MBGL_VERIFY_THREAD(tid); - - assert(paused); - - resumed->set_value(); - - resumed.reset(); - paused.reset(); - } - -private: - MBGL_STORE_THREAD(tid); - - template <typename Fn> - auto bind(Fn fn) { - return [fn, this] (auto &&... args) { - return (object->*fn)(std::forward<decltype(args)>(args)...); - }; - } - - template <typename P, std::size_t... I> - void run(P&& params, std::index_sequence<I...>); - - std::promise<void> running; - std::promise<void> joinable; - - std::unique_ptr<std::promise<void>> paused; - std::unique_ptr<std::promise<void>> resumed; - - std::thread thread; - - Object* object = nullptr; - RunLoop* loop = nullptr; -}; - -template <class Object> -template <class... 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([&] { - platform::setCurrentThreadName(context.name); - - if (context.priority == ThreadPriority::Low) { - platform::makeThreadLowPriority(); - } - - run(std::move(params), std::index_sequence_for<Args...>{}); - }); - - running.get_future().get(); -} - -template <class Object> -template <typename P, std::size_t... I> -void Thread<Object>::run(P&& params, std::index_sequence<I...>) { - RunLoop loop_(RunLoop::Type::New); - loop = &loop_; - - Object object_(std::get<I>(std::forward<P>(params))...); - object = &object_; - - running.set_value(); - loop_.run(); - - loop = nullptr; - object = nullptr; - - joinable.get_future().get(); -} - -template <class Object> -Thread<Object>::~Thread() { - MBGL_VERIFY_THREAD(tid); - - if (paused) { - resume(); - } - - loop->stop(); - joinable.set_value(); - thread.join(); -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/thread_context.cpp b/src/mbgl/util/thread_context.cpp deleted file mode 100644 index fe64c2a686..0000000000 --- a/src/mbgl/util/thread_context.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <mbgl/util/thread_context.hpp> -#include <utility> - -namespace mbgl { -namespace util { - -ThreadContext::ThreadContext(std::string name_, ThreadPriority priority_) - : name(std::move(name_)), - priority(priority_) { -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/thread_context.hpp b/src/mbgl/util/thread_context.hpp deleted file mode 100644 index a51dede404..0000000000 --- a/src/mbgl/util/thread_context.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <string> - -namespace mbgl { -namespace util { - -enum class ThreadPriority : bool { - Regular, - Low, -}; - -struct ThreadContext { -public: - ThreadContext(std::string name, ThreadPriority priority = ThreadPriority::Regular); - - std::string name; - ThreadPriority priority; -}; - -} // namespace util -} // namespace mbgl diff --git a/test/util/thread.test.cpp b/test/util/thread.test.cpp deleted file mode 100644 index 972bddf383..0000000000 --- a/test/util/thread.test.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#include <mbgl/util/thread.hpp> -#include <mbgl/util/run_loop.hpp> - -#include <mbgl/test/util.hpp> - -#include <atomic> - -using namespace mbgl::util; - -class TestObject { -public: - TestObject(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); - } - - void fn2(std::function<void (int)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(1); - } - - void transferIn(std::unique_ptr<int> val) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - } - - void transferOut(std::function<void (std::unique_ptr<int>)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_unique<int>(1)); - } - - void transferInOut(std::unique_ptr<int> val, std::function<void (std::unique_ptr<int>)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - cb(std::move(val)); - } - - void transferInShared(std::shared_ptr<int> val) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - } - - void transferOutShared(std::function<void (std::shared_ptr<int>)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_shared<int>(1)); - } - - void transferString(const std::string& string, std::function<void (std::string)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(string, "test"); - cb(string); - } - - void checkContext(std::function<void (bool)> cb) const { - cb(tid == std::this_thread::get_id()); - } - - const std::thread::id tid; -}; - -TEST(Thread, invoke) { - const std::thread::id tid = std::this_thread::get_id(); - - RunLoop loop; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; - - loop.invoke([&] { - EXPECT_EQ(tid, std::this_thread::get_id()); - Thread<TestObject> thread({"Test"}, tid); - - thread.invoke(&TestObject::fn1, 1); - requests.push_back(thread.invokeWithCallback(&TestObject::fn2, [&] (int result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(result, 1); - })); - - thread.invoke(&TestObject::transferIn, std::make_unique<int>(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr<int> result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, std::make_unique<int>(1), [&] (std::unique_ptr<int> result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr<int> result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - // Cancelled request - thread.invokeWithCallback(&TestObject::fn2, [&] (int) { - ADD_FAILURE(); - }); - - std::string test("test"); - requests.push_back(thread.invokeWithCallback(&TestObject::transferString, test, [&] (std::string result){ - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(result, "test"); - loop.stop(); - })); - test.clear(); - }); - - loop.run(); -} - -TEST(Thread, context) { - const std::thread::id tid = std::this_thread::get_id(); - - RunLoop loop; - std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; - - loop.invoke([&] { - Thread<TestObject> thread({"Test"}, tid); - - requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) { - EXPECT_EQ(inTestThreadContext, true); - loop.stop(); - })); - }); - - loop.run(); -} - -class TestWorker { -public: - TestWorker() = default; - - void send(std::function<void ()> fn, std::function<void ()> cb) { - fn(); - cb(); - } -}; - -TEST(Thread, ExecutesAfter) { - RunLoop loop; - Thread<TestWorker> thread({"Test"}); - - bool didWork = false; - bool didAfter = false; - - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - didWork = true; - }, [&] { - didAfter = true; - loop.stop(); - }); - - loop.run(); - - EXPECT_TRUE(didWork); - EXPECT_TRUE(didAfter); -} - -TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { - RunLoop loop; - - Thread<TestWorker> thread({"Test"}); - - std::promise<void> started; - bool didWork = false; - - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - started.set_value(); - usleep(10000); - didWork = true; - }, [&] {}); - - started.get_future().get(); - request.reset(); - EXPECT_TRUE(didWork); -} - -TEST(Thread, WorkRequestDeletionCancelsAfter) { - RunLoop loop; - Thread<TestWorker> thread({"Test"}); - - std::promise<void> started; - bool didAfter = false; - - auto request = thread.invokeWithCallback(&TestWorker::send, [&] { - started.set_value(); - }, [&] { - didAfter = true; - }); - - started.get_future().get(); - request.reset(); - loop.runOnce(); - EXPECT_FALSE(didAfter); -} - -TEST(Thread, WorkRequestDeletionCancelsImmediately) { - RunLoop loop; - Thread<TestWorker> thread({"Test"}); - - std::promise<void> started; - - auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] { - usleep(10000); - started.set_value(); - }, [&] {}); - - auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] { - ADD_FAILURE() << "Second work item should not be invoked"; - }, [&] {}); - request2.reset(); - - started.get_future().get(); - request1.reset(); -} - -TEST(Thread, DeletePausedThread) { - RunLoop loop; - - std::atomic_bool flag(false); - - auto thread = std::make_unique<Thread<TestWorker>>(ThreadContext{"Test"}); - thread->pause(); - thread->invoke(&TestWorker::send, [&] { flag = true; }, [] {}); - - // Should not hang. - thread.reset(); - - // Should process the queue before destruction. - ASSERT_TRUE(flag); -} - -TEST(Thread, Pause) { - RunLoop loop; - - std::atomic_bool flag(false); - - Thread<TestWorker> thread1({"Test1"}); - thread1.pause(); - - Thread<TestWorker> thread2({"Test2"}); - - for (unsigned i = 0; i < 100; ++i) { - thread1.invoke(&TestWorker::send, [&] { flag = true; }, [] {}); - thread2.invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); }, [] {}); - } - - // Queue a message at the end of thread2 queue. - thread2.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); - loop.run(); -} - -TEST(Thread, Resume) { - RunLoop loop; - - std::atomic_bool flag(false); - - Thread<TestWorker> thread({"Test"}); - thread.pause(); - - for (unsigned i = 0; i < 100; ++i) { - thread.invoke(&TestWorker::send, [&] { flag = true; }, [] {}); - } - - // Thread messages are ondered, when we resume, this is going - // to me the last thing to run on the message queue. - thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); - - // This test will be flaky if the thread doesn't get paused. - ASSERT_FALSE(flag); - - thread.resume(); - loop.run(); - - ASSERT_TRUE(flag); -} - -TEST(Thread, PauseResume) { - RunLoop loop; - - Thread<TestWorker> thread({"Test"}); - - // Test if multiple pause/resume work. - for (unsigned i = 0; i < 100; ++i) { - thread.pause(); - thread.resume(); - } - - thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {}); - loop.run(); -} |