diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 729d3f7da..220e25d35 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 da1c04648..f85ca0333 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 d7649a348..4f5483d79 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/src/mbgl/util/logging.cpp b/src/mbgl/util/logging.cpp index 939f1def6..0552eb36c 100644 --- a/src/mbgl/util/logging.cpp +++ b/src/mbgl/util/logging.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp deleted file mode 100644 index 4afaab6b7..000000000 --- a/src/mbgl/util/thread.hpp +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -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 Thread { -public: - Thread(const Thread&) = delete; - Thread(Thread&&) = delete; - Thread& operator=(const Thread&) = delete; - Thread& operator=(Thread&&) = delete; - - template - Thread(const ThreadContext&, Args&&... args); - ~Thread(); - - // Invoke object->fn(args...) asynchronously. - template - void invoke(Fn fn, Args&&... args) { - loop->invoke(bind(fn), std::forward(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 - std::unique_ptr - invokeWithCallback(Fn fn, Args&&... args) { - return loop->invokeWithCallback(bind(fn), std::forward(args)...); - } - - // Invoke object->fn(args...) asynchronously, but wait for the result. - template - auto invokeSync(Fn fn, Args&&... args) { - assert(!paused); - - using R = std::result_of_t; - std::packaged_task task(std::bind(fn, object, args...)); - std::future future = task.get_future(); - loop->invoke(std::move(task)); - return future.get(); - } - - void pause() { - MBGL_VERIFY_THREAD(tid); - - assert(!paused); - - paused = std::make_unique>(); - resumed = std::make_unique>(); - - 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 - auto bind(Fn fn) { - return [fn, this] (auto &&... args) { - return (object->*fn)(std::forward(args)...); - }; - } - - template - void run(P&& params, std::index_sequence); - - std::promise running; - std::promise joinable; - - std::unique_ptr> paused; - std::unique_ptr> resumed; - - std::thread thread; - - Object* object = nullptr; - RunLoop* loop = nullptr; -}; - -template -template -Thread::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 params = std::forward_as_tuple(::std::forward(args)...); - - thread = std::thread([&] { - platform::setCurrentThreadName(context.name); - - if (context.priority == ThreadPriority::Low) { - platform::makeThreadLowPriority(); - } - - run(std::move(params), std::index_sequence_for{}); - }); - - running.get_future().get(); -} - -template -template -void Thread::run(P&& params, std::index_sequence) { - RunLoop loop_(RunLoop::Type::New); - loop = &loop_; - - Object object_(std::get(std::forward

(params))...); - object = &object_; - - running.set_value(); - loop_.run(); - - loop = nullptr; - object = nullptr; - - joinable.get_future().get(); -} - -template -Thread::~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 fe64c2a68..000000000 --- a/src/mbgl/util/thread_context.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -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 a51dede40..000000000 --- a/src/mbgl/util/thread_context.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -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 972bddf38..000000000 --- a/test/util/thread.test.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#include -#include - -#include - -#include - -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 cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(1); - } - - void transferIn(std::unique_ptr val) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - } - - void transferOut(std::function)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_unique(1)); - } - - void transferInOut(std::unique_ptr val, std::function)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - cb(std::move(val)); - } - - void transferInShared(std::shared_ptr val) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*val, 1); - } - - void transferOutShared(std::function)> cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - cb(std::make_shared(1)); - } - - void transferString(const std::string& string, std::function cb) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(string, "test"); - cb(string); - } - - void checkContext(std::function 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> requests; - - loop.invoke([&] { - EXPECT_EQ(tid, std::this_thread::get_id()); - Thread 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(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, std::make_unique(1), [&] (std::unique_ptr result) { - EXPECT_EQ(tid, std::this_thread::get_id()); - EXPECT_EQ(*result, 1); - })); - - thread.invoke(&TestObject::transferInShared, std::make_shared(1)); - requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr 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> requests; - - loop.invoke([&] { - Thread 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 fn, std::function cb) { - fn(); - cb(); - } -}; - -TEST(Thread, ExecutesAfter) { - RunLoop loop; - Thread 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 thread({"Test"}); - - std::promise 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 thread({"Test"}); - - std::promise 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 thread({"Test"}); - - std::promise 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>(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 thread1({"Test1"}); - thread1.pause(); - - Thread 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 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 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(); -}