#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: 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); Thread(const Thread&) = delete; Thread(Thread&&) = delete; Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; 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