#pragma once #include #include #include #include #include #include #include #include #include namespace mbgl { namespace util { typedef void * LOOP_HANDLE; class RunLoop : private util::noncopyable { public: enum class Type : uint8_t { Default, New, }; enum class Event : uint8_t { None = 0, Read = 1, Write = 2, ReadWrite = Read | Write, }; RunLoop(Type type = Type::Default); ~RunLoop(); static RunLoop* Get(); static LOOP_HANDLE getLoopHandle(); void run(); void runOnce(); void stop(); // So far only needed by the libcurl backend. void addWatch(int fd, Event, std::function&& callback); void removeWatch(int fd); // Invoke fn(args...) on this RunLoop. template void invoke(Fn&& fn, Args&&... args) { auto tuple = std::make_tuple(std::move(args)...); auto task = std::make_shared>( std::move(fn), std::move(tuple)); push(task); } // Post the cancellable work fn(args...) to this RunLoop. template std::unique_ptr invokeCancellable(Fn&& fn, Args&&... args) { auto flag = std::make_shared>(); *flag = false; auto tuple = std::make_tuple(std::move(args)...); auto task = std::make_shared>( std::move(fn), std::move(tuple), flag); push(task); return std::make_unique(task); } // Invoke fn(args...) on this RunLoop, then invoke callback(results...) on the current RunLoop. template std::unique_ptr invokeWithCallback(Fn&& fn, Cb&& callback, Args&&... args) { auto flag = std::make_shared>(); *flag = false; // Create a lambda L1 that invokes another lambda L2 on the current RunLoop R, that calls // the callback C. Both lambdas check the flag before proceeding. L1 needs to check the flag // because if the request was cancelled, then R might have been destroyed. L2 needs to check // the flag because the request may have been cancelled after L2 was invoked but before it // began executing. auto after = [flag, current = RunLoop::Get(), callback1 = std::move(callback)] (auto&&... results1) { if (!*flag) { current->invoke([flag, callback2 = std::move(callback1)] (auto&&... results2) { if (!*flag) { callback2(std::move(results2)...); } }, std::move(results1)...); } }; auto tuple = std::make_tuple(std::move(args)..., after); auto task = std::make_shared>( std::move(fn), std::move(tuple), flag); push(task); return std::make_unique(task); } class Impl; private: MBGL_STORE_THREAD(tid) template class Invoker : public WorkTask { public: Invoker(F&& f, P&& p, std::shared_ptr> canceled_ = nullptr) : canceled(std::move(canceled_)), func(std::move(f)), params(std::move(p)) { } void operator()() override { // Lock the mutex while processing so that cancel() will block. std::lock_guard lock(mutex); if (!canceled || !*canceled) { invoke(std::make_index_sequence::value>{}); } } // If the task has not yet begun, this will cancel it. // If the task is in progress, this will block until it completed. (Currently // necessary because of shared state, but should be removed.) It will also // cancel the after callback. // If the task has completed, but the after callback has not executed, this // will cancel the after callback. // If the task has completed and the after callback has executed, this will // do nothing. void cancel() override { std::lock_guard lock(mutex); *canceled = true; } private: template void invoke(std::index_sequence) { func(std::move(std::get(std::forward

(params)))...); } std::recursive_mutex mutex; std::shared_ptr> canceled; F func; P params; }; using Queue = std::queue>; void push(std::shared_ptr); void withMutex(std::function&& fn) { std::lock_guard lock(mutex); fn(); } void process() { Queue queue_; withMutex([&] { queue_.swap(queue); }); while (!queue_.empty()) { (*(queue_.front()))(); queue_.pop(); } } Queue queue; std::mutex mutex; std::unique_ptr impl; }; } // namespace util } // namespace mbgl