#ifndef MBGL_UTIL_THREAD #define MBGL_UTIL_THREAD #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 { // 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 std::string& name, Args&&... args); ~Thread(); // Invoke object->fn(args...) in the runloop thread. template void invoke(Fn fn, Args&&... args) { loop->invoke(std::bind(fn, object, 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, args...), callback); } // 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, args...), callback); } // Invoke object->fn(args...) in the runloop thread, and wait for the result. template R invokeSync(Fn fn, Args&&... args) { std::packaged_task task(std::bind(fn, object, args...)); std::future future = task.get_future(); loop->invoke(std::move(task)); return future.get(); } // Invoke object->fn(args...) in the runloop thread, and wait for it to complete. template void invokeSync(Fn fn, Args&&... args) { std::packaged_task task(std::bind(fn, object, args...)); std::future future = task.get_future(); loop->invoke(std::move(task)); return future.get(); } // Join the thread, but call the given function repeatedly in the current thread // while waiting for the join to finish. This should be immediately followed by // destroying the Thread. void pumpingStop(std::function); private: Thread(const Thread&) = delete; Thread(Thread&&) = delete; Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; template void run(P&& params, index_sequence); std::promise running; std::promise joinable; std::thread thread; Object* object; RunLoop* loop; }; template template Thread::Thread(const std::string& name, 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([&] { #ifdef __APPLE__ pthread_setname_np(name.c_str()); #else (void(name)); #endif constexpr auto seq = typename integer_sequence::type(); run(std::move(params), seq); }); running.get_future().get(); } template template void Thread::run(P&& params, index_sequence) { RunLoop loop_; loop = &loop_; { Object object_(loop_.get(), std::get(std::forward

(params))...); object = &object_; running.set_value(); loop_.run(); object = nullptr; } // Run the loop again to ensure that async close callbacks have been called. loop_.run(); joinable.get_future().get(); } template Thread::~Thread() { loop->stop(); joinable.set_value(); thread.join(); } } } #endif