diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 5cde95a53..c89b8bd79 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -48,6 +48,15 @@ public: void runOnce(); void stop(); + using PlatformCallback = std::function; + + // Platform integration callback for platforms that do not have full + // run loop integration or don't want to block at the Mapbox GL Native + // loop. It will be called from any thread and is up to the platform + // to, after receiving the callback, call RunLoop::runOnce() from the + // same thread as the Map object lives. + void setPlatformCallback(PlatformCallback&& callback) { platformCallback = std::move(callback); } + // So far only needed by the libcurl backend. void addWatch(int fd, Event, std::function&& callback); void removeWatch(int fd); @@ -95,6 +104,7 @@ private: defaultQueue.emplace(std::move(task)); } wake(); + platformCallback(); } void process() { @@ -117,6 +127,8 @@ private: } } + PlatformCallback platformCallback = [] {}; + Queue defaultQueue; Queue highPriorityQueue; std::mutex mutex; diff --git a/test/util/run_loop.test.cpp b/test/util/run_loop.test.cpp index 4d2c70442..62bfbb0fc 100644 --- a/test/util/run_loop.test.cpp +++ b/test/util/run_loop.test.cpp @@ -3,6 +3,10 @@ #include +#include +#include +#include + using namespace mbgl::util; TEST(RunLoop, Stop) { @@ -64,3 +68,48 @@ TEST(RunLoop, Priorities) { EXPECT_EQ((std::vector{ 2, 4, 1, 3 }), order); } + +TEST(RunLoop, PlatformIntegration) { + std::atomic count1(0); + + // No need to be atomic, it will run + // on the main thread. + int count2(0); + + std::thread::id mainThread = std::this_thread::get_id(); + + RunLoop loop; + + std::mutex mutex; + std::condition_variable cv; + + loop.setPlatformCallback([&] { + EXPECT_NE(mainThread, std::this_thread::get_id()); + count1++; + cv.notify_one(); + }); + + auto threadBody = [&]() { + for (unsigned i = 0; i < 100000; ++i) { + loop.invoke([&] { + EXPECT_EQ(mainThread, std::this_thread::get_id()); + count2++; + }); + } + }; + + std::thread thread1(threadBody); + std::thread thread2(threadBody); + + while (count2 < 200000) { + std::unique_lock lock(mutex); + cv.wait(lock); + loop.runOnce(); + } + + EXPECT_EQ(count1, 200000); + EXPECT_EQ(count2, 200000); + + thread1.join(); + thread2.join(); +}