#ifndef MBGL_UTIL_THREAD #define MBGL_UTIL_THREAD #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...) in the runloop thread. template void invoke(Fn fn, Args&&... 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 std::unique_ptr invokeWithCallback(Fn fn, Cb&& callback, Args&&... args) { return loop->invokeWithCallback(bind(fn), callback, std::forward(args)...); } // 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)); future.get(); } private: 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(ThreadContext, P&& params, std::index_sequence); std::promise running; std::promise joinable; 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([&] { #if defined( __APPLE__) pthread_setname_np(context.name.c_str()); #elif defined(__linux__) pthread_setname_np(pthread_self(), context.name.c_str()); #endif if (context.priority == ThreadPriority::Low) { platform::makeThreadLowPriority(); } run(context, std::move(params), std::index_sequence_for{}); }); running.get_future().get(); } template template void Thread::run(ThreadContext context, P&& params, std::index_sequence) { ThreadContext::Set(&context); 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; ThreadContext::Set(nullptr); joinable.get_future().get(); } template Thread::~Thread() { loop->stop(); joinable.set_value(); thread.join(); } } // namespace util } // namespace mbgl #endif