From fc64e97bce3e082b0396e68bd6493457defe4ec7 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 11 Jun 2015 19:03:19 -0400 Subject: [PATCH] Simplify RunLoop Instead of transforming between return value and callback, pass the wrapped callback on to the invoked function as the last argument. This eliminates the need for multiple invokeWithArgument overloads and permits invoked functions to be asynchronous. --- platform/default/sqlite_cache.cpp | 10 +- platform/default/sqlite_cache_impl.hpp | 2 +- src/mbgl/map/source.cpp | 14 +-- src/mbgl/util/run_loop.hpp | 165 ++++++------------------- src/mbgl/util/thread.hpp | 21 ++-- src/mbgl/util/worker.cpp | 29 +++-- test/miscellaneous/thread.cpp | 61 ++++----- test/storage/database.cpp | 63 ++++++---- 8 files changed, 149 insertions(+), 216 deletions(-) diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp index 3c23d167a..c1b1daf23 100644 --- a/platform/default/sqlite_cache.cpp +++ b/platform/default/sqlite_cache.cpp @@ -131,10 +131,10 @@ std::unique_ptr SQLiteCache::get(const Resource &resource, Callback // Will try to load the URL from the SQLite database and call the callback when done. // Note that the callback is probably going to invoked from another thread, so the caller // must make sure that it can run in that thread. - return thread->invokeWithResult(&Impl::get, std::move(callback), resource); + return thread->invokeWithCallback(&Impl::get, callback, resource); } -std::unique_ptr SQLiteCache::Impl::get(const Resource &resource) { +void SQLiteCache::Impl::get(const Resource &resource, Callback callback) { try { // This is called in the SQLite event loop. if (!db) { @@ -167,14 +167,14 @@ std::unique_ptr SQLiteCache::Impl::get(const Resource &resource) { if (getStmt->get(5)) { // == compressed response->data = util::decompress(response->data); } - return std::move(response); + callback(std::move(response)); } else { // There is no data. - return nullptr; + callback(nullptr); } } catch (mapbox::sqlite::Exception& ex) { Log::Error(Event::Database, ex.code, ex.what()); - return nullptr; + callback(nullptr); } } diff --git a/platform/default/sqlite_cache_impl.hpp b/platform/default/sqlite_cache_impl.hpp index e01400a58..86c7c07bd 100644 --- a/platform/default/sqlite_cache_impl.hpp +++ b/platform/default/sqlite_cache_impl.hpp @@ -19,7 +19,7 @@ public: Impl(uv_loop_t*, const std::string &path = ":memory:"); ~Impl(); - std::unique_ptr get(const Resource&); + void get(const Resource&, Callback); void put(const Resource& resource, std::shared_ptr response); void refresh(const Resource& resource, int64_t expires); diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 55761eb26..f468ab33e 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -233,7 +233,9 @@ bool Source::handlePartialTile(const TileID& id, Worker&) { return true; } - util::ptr data = it->second.lock(); + // Note: this uses a raw pointer; we don't want the callback binding to have a + // shared pointer. + VectorTileData* data = static_cast(it->second.lock().get()); if (!data) { return true; } @@ -241,14 +243,12 @@ bool Source::handlePartialTile(const TileID& id, Worker&) { // The signal is only emitted if there was an actual change on the tile. The // tile can be in a "partial" state waiting for resources and get reparsed on // the arrival of new resources that were needed by another tile. - size_t bucketCount = static_cast(data.get())->countBuckets(); - auto callback = [this, data, bucketCount]() { - if (static_cast(data.get())->countBuckets() > bucketCount) { + size_t bucketCount = data->countBuckets(); + return data->reparse([this, data, bucketCount]() { + if (data->countBuckets() > bucketCount) { emitTileLoaded(false); } - }; - - return data->reparse(callback); + }); } TileData::State Source::addTile(MapData& data, diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp index 14021ef42..c0a56e956 100644 --- a/src/mbgl/util/run_loop.hpp +++ b/src/mbgl/util/run_loop.hpp @@ -25,7 +25,7 @@ public: template void invoke(Fn&& fn, Args&&... args) { auto tuple = std::make_tuple(std::move(args)...); - auto task = std::make_shared>( + auto task = std::make_shared>( std::move(fn), std::move(tuple)); @@ -33,44 +33,24 @@ public: async.send(); } - // Invoke fn(args...) on this RunLoop, then invoke callback() on the current RunLoop. - template + // Invoke fn(args...) on this RunLoop, then invoke callback(results...) on the current RunLoop. + template std::unique_ptr - invokeWithResult(Fn&& fn, std::function callback, Args&&... args) { - auto tuple = std::make_tuple(std::move(args)...); - auto task = std::make_shared>( + invokeWithCallback(Fn&& fn, Cb&& callback, Args&&... args) { + auto flag = std::make_shared(); + *flag = false; + + auto after = RunLoop::current.get()->bind([flag, callback] (auto&&... results) { + if (!*flag) { + callback(std::move(results)...); + } + }); + + auto tuple = std::make_tuple(std::move(args)..., after); + auto task = std::make_shared>( std::move(fn), - std::move(tuple)); - - // `task` is a shared pointer with ownership in the following three places: - // 1. In the `queue` of pending invocations. - // 2. In the `WorkRequest` result. - // 3. In the lambda binding of the callback to be executed on the invoking - // RunLoop. This last shared ownership is necessary in the case where - // callback execution has been scheduled (queued on the invoking RunLoop), - // but the other two places have released ownership -- i.e. the task was - // cancelled after the work is completed, but before the callback is - // executed. In this case, the lambda binding checks the cancellation flag - // and does not execute the original callback. - - task->bind(callback); - - withMutex([&] { queue.push(task); }); - async.send(); - - return std::make_unique(task); - } - - // Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop. - template - std::unique_ptr - invokeWithResult(Fn&& fn, std::function callback, Args&&... args) { - auto tuple = std::make_tuple(std::move(args)...); - auto task = std::make_shared>( - std::move(fn), - std::move(tuple)); - - task->bind(callback); + std::move(tuple), + flag); withMutex([&] { queue.push(task); }); async.send(); @@ -79,10 +59,11 @@ public: } // Return a function that invokes the given function on this RunLoop. - template - auto bind(std::function fn) { - return [this, fn = std::move(fn)] (Args&&... args) { - invoke(std::move(fn), std::move(args)...); + template + auto bind(Fn&& fn) { + return [this, fn = std::move(fn)] (auto&&... args) { + // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 + this->invoke(std::move(fn), std::move(args)...); }; } @@ -91,111 +72,47 @@ public: static uv::tls current; private: - template - class Invoker : public WorkTask, public std::enable_shared_from_this> { + template + class Invoker : public WorkTask { public: - Invoker(F&& f, P&& p) - : func(std::move(f)), + Invoker(F&& f, P&& p, std::shared_ptr canceled_ = nullptr) + : canceled(canceled_), + func(std::move(f)), params(std::move(p)) { } - using C = std::function; - - void bind(C after) { - auto task = this->shared_from_this(); - callback = RunLoop::current.get()->bind(C([task, after] { - if (!task->canceled) { - after(); - } - })); - } - void operator()() override { - // We are only running the task when there's an after callback to call. This means that an - // empty after callback will be treated as a cancelled request. The mutex will be locked while - // processing so that the cancel() callback will block. + // Lock the mutex while processing so that cancel() will block. std::lock_guard lock(mutex); - if (!canceled) { - invoke(std::index_sequence_for{}); + 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 { - // Remove the after callback to indicate that this callback has been canceled. The mutex will - // block when the task is currently in progres. When the task has not begun yet, the runTask() - // method will not do anything. When the task has been completed already, and the after callback - // was run as well, this will also do nothing. std::lock_guard lock(mutex); - canceled = true; + *canceled = true; } private: template void invoke(std::index_sequence) { - func(std::forward(std::get(params))...); - if (callback) { - callback(); - } + func(std::get(std::forward

(params))...); } std::mutex mutex; - bool canceled = false; + std::shared_ptr canceled; F func; P params; - C callback; - }; - - template - class InvokerWithResult : public WorkTask, public std::enable_shared_from_this> { - public: - InvokerWithResult(F&& f, P&& p) - : func(std::move(f)), - params(std::move(p)) { - } - - using C = std::function; - - void bind(C after) { - auto task = this->shared_from_this(); - callback = RunLoop::current.get()->bind(C([task, after] (R result) { - if (!task->canceled) { - after(std::move(result)); - } - })); - } - - void operator()() override { - // We are only running the task when there's an after callback to call. This means that an - // empty after callback will be treated as a cancelled request. The mutex will be locked while - // processing so that the cancel() callback will block. - std::lock_guard lock(mutex); - if (!canceled) { - invoke(std::index_sequence_for{}); - } - } - - void cancel() override { - // Remove the after callback to indicate that this callback has been canceled. The mutex will - // block when the task is currently in progres. When the task has not begun yet, the runTask() - // method will not do anything. When the task has been completed already, and the after callback - // was run as well, this will also do nothing. - std::lock_guard lock(mutex); - canceled = true; - } - - private: - template - void invoke(std::index_sequence) { - callback(func(std::forward(std::get(params))...)); - } - - std::mutex mutex; - bool canceled = false; - - F func; - P params; - C callback; }; using Queue = std::queue>; diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index 16bb81db1..88a709cfa 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -32,21 +32,14 @@ public: // Invoke object->fn(args...) in the runloop thread. template void invoke(Fn fn, Args&&... args) { - loop->invoke(bind(fn), std::forward(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 + template std::unique_ptr - invokeWithResult(Fn fn, std::function callback, Args&&... args) { - return loop->invokeWithResult(bind(fn), callback, std::forward(args)...); - } - - // Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread. - template - std::unique_ptr - invokeWithResult(Fn fn, std::function callback, Args&&... args) { - return loop->invokeWithResult(bind(fn), callback, std::forward(args)...); + 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. @@ -73,9 +66,11 @@ private: Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; - template + template auto bind(Fn fn) { - return [fn, this] (Args&&... a) { return (object->*fn)(std::forward(a)...); }; + return [fn, this] (auto &&... args) { + return (object->*fn)(std::forward(args)...); + }; } template diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp index 0d83bc49f..cc9fa1566 100644 --- a/src/mbgl/util/worker.cpp +++ b/src/mbgl/util/worker.cpp @@ -20,25 +20,30 @@ public: util::ThreadContext::setFileSource(fs); } - bool parseRasterTile(RasterBucket* bucket, std::string data) { - return bucket->setImage(data); + void parseRasterTile(RasterBucket* bucket, std::string data, std::function callback) { + callback(bucket->setImage(data)); } - TileParseResult parseVectorTile(TileWorker* worker, std::string data) { + void parseVectorTile(TileWorker* worker, std::string data, std::function callback) { try { pbf tilePBF(reinterpret_cast(data.data()), data.size()); - return worker->parse(VectorTile(tilePBF)); + callback(worker->parse(VectorTile(tilePBF))); } catch (const std::exception& ex) { - return TileParseResult(ex.what()); + callback(TileParseResult(ex.what())); } } - TileParseResult parseLiveTile(TileWorker* worker, const LiveTile* tile) { - return worker->parse(*tile); + void parseLiveTile(TileWorker* worker, const LiveTile* tile, std::function callback) { + try { + callback(worker->parse(*tile)); + } catch (const std::exception& ex) { + callback(TileParseResult(ex.what())); + } } - void redoPlacement(TileWorker* worker, float angle, bool collisionDebug) { + void redoPlacement(TileWorker* worker, float angle, bool collisionDebug, std::function callback) { worker->redoPlacement(angle, collisionDebug); + callback(); } }; @@ -53,22 +58,22 @@ Worker::~Worker() = default; std::unique_ptr Worker::parseRasterTile(RasterBucket& bucket, std::string data, std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithResult(&Worker::Impl::parseRasterTile, callback, &bucket, data); + return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, &bucket, data); } std::unique_ptr Worker::parseVectorTile(TileWorker& worker, std::string data, std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithResult(&Worker::Impl::parseVectorTile, callback, &worker, data); + return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker, data); } std::unique_ptr Worker::parseLiveTile(TileWorker& worker, const LiveTile& tile, std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithResult(&Worker::Impl::parseLiveTile, callback, &worker, &tile); + return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, &tile); } std::unique_ptr Worker::redoPlacement(TileWorker& worker, float angle, bool collisionDebug, std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithResult(&Worker::Impl::redoPlacement, callback, &worker, angle, collisionDebug); + return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, collisionDebug); } } // end namespace mbgl diff --git a/test/miscellaneous/thread.cpp b/test/miscellaneous/thread.cpp index 68f8ddccd..78e6ffee6 100644 --- a/test/miscellaneous/thread.cpp +++ b/test/miscellaneous/thread.cpp @@ -17,9 +17,9 @@ public: EXPECT_EQ(val, 1); } - int fn2() { + void fn2(std::function cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return 1; + cb(1); } void transferIn(std::unique_ptr val) { @@ -27,15 +27,15 @@ public: EXPECT_EQ(*val, 1); } - std::unique_ptr transferOut() { + void transferOut(std::function)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return std::make_unique(1); + cb(std::make_unique(1)); } - std::unique_ptr transferInOut(std::unique_ptr val) { + void transferInOut(std::unique_ptr val, std::function)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*val, 1); - return std::move(val); + cb(std::move(val)); } void transferInShared(std::shared_ptr val) { @@ -43,21 +43,21 @@ public: EXPECT_EQ(*val, 1); } - std::shared_ptr transferOutShared() { + void transferOutShared(std::function)> cb) { EXPECT_EQ(tid, std::this_thread::get_id()); - return std::make_shared(1); + cb(std::make_shared(1)); } - std::string transferString(const std::string& string) { + void transferString(const std::string& string, std::function cb) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(string, "test"); - return string; + cb(string); } - bool checkContext() const { - return ThreadContext::currentlyOn(ThreadType::Worker) + void checkContext(std::function cb) const { + cb(ThreadContext::currentlyOn(ThreadType::Worker) && ThreadContext::getName() == "Test" - && ThreadContext::getPriority() == ThreadPriority::Low; + && ThreadContext::getPriority() == ThreadPriority::Low); } const std::thread::id tid; @@ -74,35 +74,35 @@ TEST(Thread, invoke) { Thread thread({"Test", ThreadType::Map, ThreadPriority::Regular}, tid); thread.invoke(&TestObject::fn1, 1); - requests.push_back(thread.invokeWithResult(&TestObject::fn2, [&] (int result) { + requests.push_back(thread.invokeWithCallback(&TestObject::fn2, [&] (int result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(result, 1); })); thread.invoke(&TestObject::transferIn, std::make_unique(1)); - requests.push_back(thread.invokeWithResult>(&TestObject::transferOut, [&] (std::unique_ptr result) { + requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*result, 1); })); - requests.push_back(thread.invokeWithResult>(&TestObject::transferInOut, [&] (std::unique_ptr result) { + requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, [&] (std::unique_ptr result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*result, 1); }, std::make_unique(1))); thread.invoke(&TestObject::transferInShared, std::make_shared(1)); - requests.push_back(thread.invokeWithResult>(&TestObject::transferOutShared, [&] (std::shared_ptr result) { + requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr result) { EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(*result, 1); })); // Cancelled request - thread.invokeWithResult(&TestObject::fn1, [&] { + thread.invokeWithCallback(&TestObject::fn2, [&] (int) { ADD_FAILURE(); - }, 1); + }); std::string test("test"); - requests.push_back(thread.invokeWithResult(&TestObject::transferString, [&] (std::string result){ + requests.push_back(thread.invokeWithCallback(&TestObject::transferString, [&] (std::string result){ EXPECT_EQ(tid, std::this_thread::get_id()); EXPECT_EQ(result, "test"); loop.stop(); @@ -128,7 +128,7 @@ TEST(Thread, context) { loop.invoke([&] { Thread thread({"Test", ThreadType::Worker, ThreadPriority::Low}, tid); - requests.push_back(thread.invokeWithResult(&TestObject::checkContext, [&] (bool inTestThreadContext) { + requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) { EXPECT_EQ(inTestThreadContext, true); loop.stop(); })); @@ -139,8 +139,13 @@ TEST(Thread, context) { class TestWorker { public: - TestWorker(uv_loop_t*) {} - void send(std::function fn) { fn(); } + TestWorker(uv_loop_t*) { + } + + void send(std::function fn, std::function cb) { + fn(); + cb(); + } }; TEST(Thread, ExecutesAfter) { @@ -150,7 +155,7 @@ TEST(Thread, ExecutesAfter) { bool didWork = false; bool didAfter = false; - auto request = thread.invokeWithResult(&TestWorker::send, [&] { + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { didAfter = true; loop.stop(); }, [&] { @@ -170,7 +175,7 @@ TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) { std::promise started; bool didWork = false; - auto request = thread.invokeWithResult(&TestWorker::send, [&] {}, [&] { + auto request = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { started.set_value(); usleep(10000); didWork = true; @@ -188,7 +193,7 @@ TEST(Thread, WorkRequestDeletionCancelsAfter) { std::promise started; bool didAfter = false; - auto request = thread.invokeWithResult(&TestWorker::send, [&] { + auto request = thread.invokeWithCallback(&TestWorker::send, [&] { didAfter = true; }, [&] { started.set_value(); @@ -206,12 +211,12 @@ TEST(Thread, WorkRequestDeletionCancelsImmediately) { std::promise started; - auto request1 = thread.invokeWithResult(&TestWorker::send, [&] {}, [&] { + auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { usleep(10000); started.set_value(); }); - auto request2 = thread.invokeWithResult(&TestWorker::send, [&] {}, [&] { + auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] {}, [&] { ADD_FAILURE() << "Second work item should not be invoked"; }); request2.reset(); diff --git a/test/storage/database.cpp b/test/storage/database.cpp index b54b69a13..a7560262f 100644 --- a/test/storage/database.cpp +++ b/test/storage/database.cpp @@ -15,8 +15,9 @@ TEST_F(Storage, DatabaseDoesNotExist) { SQLiteCache::Impl cache(nullptr, "test/fixtures/404/cache.db"); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); EXPECT_EQ(1ul, dynamic_cast(observer.get())->count({ EventSeverity::Error, Event::Database, 14, "unable to open database file" })); @@ -56,8 +57,9 @@ TEST_F(Storage, DatabaseCreate) { SQLiteCache::Impl cache(nullptr, "test/fixtures/database/cache.db"); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); Log::removeObserver(); } @@ -115,8 +117,9 @@ TEST_F(Storage, DatabaseLockedRead) { // First request should fail. Log::setObserver(std::make_unique()); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got a few "database locked" errors auto observer = Log::removeObserver(); @@ -131,8 +134,9 @@ TEST_F(Storage, DatabaseLockedRead) { // First, try getting a file (the cache value should not exist). Log::setObserver(std::make_unique()); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got a no errors Log::removeObserver(); @@ -157,8 +161,9 @@ TEST_F(Storage, DatabaseLockedWrite) { auto response = std::make_shared(); cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast(observer.get()); @@ -175,9 +180,10 @@ TEST_F(Storage, DatabaseLockedWrite) { auto response = std::make_shared(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); // Make sure that we got a no errors Log::removeObserver(); @@ -206,8 +212,9 @@ TEST_F(Storage, DatabaseLockedRefresh) { auto response = std::make_shared(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast(observer.get()); @@ -221,8 +228,9 @@ TEST_F(Storage, DatabaseLockedRefresh) { auto response = std::make_shared(); response->data = "Demo"; cache.refresh({ Resource::Unknown, "mapbox://test" }, response->expires); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_EQ(nullptr, res.get()); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_EQ(nullptr, res.get()); + }); // Make sure that we got the right errors. auto observer = Log::removeObserver(); @@ -249,9 +257,10 @@ TEST_F(Storage, DatabaseDeleted) { auto response = std::make_shared(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); Log::removeObserver(); } @@ -265,9 +274,10 @@ TEST_F(Storage, DatabaseDeleted) { auto response = std::make_shared(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast(observer.get()); @@ -294,9 +304,10 @@ TEST_F(Storage, DatabaseInvalid) { auto response = std::make_shared(); response->data = "Demo"; cache.put({ Resource::Unknown, "mapbox://test" }, response); - std::unique_ptr res = cache.get({ Resource::Unknown, "mapbox://test" }); - EXPECT_NE(nullptr, res.get()); - EXPECT_EQ("Demo", res->data); + cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr res) { + EXPECT_NE(nullptr, res.get()); + EXPECT_EQ("Demo", res->data); + }); auto observer = Log::removeObserver(); auto flo = dynamic_cast(observer.get());