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.
This commit is contained in:
@@ -131,10 +131,10 @@ std::unique_ptr<WorkRequest> 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<Response> 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<Response> SQLiteCache::Impl::get(const Resource &resource) {
|
||||
if (getStmt->get<int>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
Impl(uv_loop_t*, const std::string &path = ":memory:");
|
||||
~Impl();
|
||||
|
||||
std::unique_ptr<Response> get(const Resource&);
|
||||
void get(const Resource&, Callback);
|
||||
void put(const Resource& resource, std::shared_ptr<const Response> response);
|
||||
void refresh(const Resource& resource, int64_t expires);
|
||||
|
||||
|
||||
@@ -233,7 +233,9 @@ bool Source::handlePartialTile(const TileID& id, Worker&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
util::ptr<TileData> 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<VectorTileData*>(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<VectorTileData*>(data.get())->countBuckets();
|
||||
auto callback = [this, data, bucketCount]() {
|
||||
if (static_cast<VectorTileData*>(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,
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
template <class Fn, class... Args>
|
||||
void invoke(Fn&& fn, Args&&... args) {
|
||||
auto tuple = std::make_tuple(std::move(args)...);
|
||||
auto task = std::make_shared<Invoker<Fn, decltype(tuple), Args...>>(
|
||||
auto task = std::make_shared<Invoker<Fn, decltype(tuple)>>(
|
||||
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 <class Fn, class... Args>
|
||||
// Invoke fn(args...) on this RunLoop, then invoke callback(results...) on the current RunLoop.
|
||||
template <class Fn, class Cb, class... Args>
|
||||
std::unique_ptr<WorkRequest>
|
||||
invokeWithResult(Fn&& fn, std::function<void ()> callback, Args&&... args) {
|
||||
auto tuple = std::make_tuple(std::move(args)...);
|
||||
auto task = std::make_shared<Invoker<Fn, decltype(tuple), Args...>>(
|
||||
invokeWithCallback(Fn&& fn, Cb&& callback, Args&&... args) {
|
||||
auto flag = std::make_shared<bool>();
|
||||
*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<Invoker<Fn, decltype(tuple)>>(
|
||||
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<WorkRequest>(task);
|
||||
}
|
||||
|
||||
// Invoke fn(args...) on this RunLoop, then invoke callback(result) on the current RunLoop.
|
||||
template <class R, class Fn, class... Args>
|
||||
std::unique_ptr<WorkRequest>
|
||||
invokeWithResult(Fn&& fn, std::function<void (R)> callback, Args&&... args) {
|
||||
auto tuple = std::make_tuple(std::move(args)...);
|
||||
auto task = std::make_shared<InvokerWithResult<Fn, decltype(tuple), R, Args...>>(
|
||||
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 <class... Args>
|
||||
auto bind(std::function<void (Args...)> fn) {
|
||||
return [this, fn = std::move(fn)] (Args&&... args) {
|
||||
invoke(std::move(fn), std::move(args)...);
|
||||
template <class Fn>
|
||||
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<RunLoop> current;
|
||||
|
||||
private:
|
||||
template <class F, class P, class... Args>
|
||||
class Invoker : public WorkTask, public std::enable_shared_from_this<Invoker<F, P, Args...>> {
|
||||
template <class F, class P>
|
||||
class Invoker : public WorkTask {
|
||||
public:
|
||||
Invoker(F&& f, P&& p)
|
||||
: func(std::move(f)),
|
||||
Invoker(F&& f, P&& p, std::shared_ptr<bool> canceled_ = nullptr)
|
||||
: canceled(canceled_),
|
||||
func(std::move(f)),
|
||||
params(std::move(p)) {
|
||||
}
|
||||
|
||||
using C = std::function<void ()>;
|
||||
|
||||
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<std::mutex> lock(mutex);
|
||||
if (!canceled) {
|
||||
invoke(std::index_sequence_for<Args...>{});
|
||||
if (!canceled || !*canceled) {
|
||||
invoke(std::make_index_sequence<std::tuple_size<P>::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<std::mutex> lock(mutex);
|
||||
canceled = true;
|
||||
*canceled = true;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t... I>
|
||||
void invoke(std::index_sequence<I...>) {
|
||||
func(std::forward<Args>(std::get<I>(params))...);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
func(std::get<I>(std::forward<P>(params))...);
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
bool canceled = false;
|
||||
std::shared_ptr<bool> canceled;
|
||||
|
||||
F func;
|
||||
P params;
|
||||
C callback;
|
||||
};
|
||||
|
||||
template <class F, class P, class R, class... Args>
|
||||
class InvokerWithResult : public WorkTask, public std::enable_shared_from_this<InvokerWithResult<F, P, R, Args...>> {
|
||||
public:
|
||||
InvokerWithResult(F&& f, P&& p)
|
||||
: func(std::move(f)),
|
||||
params(std::move(p)) {
|
||||
}
|
||||
|
||||
using C = std::function<void (R)>;
|
||||
|
||||
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<std::mutex> lock(mutex);
|
||||
if (!canceled) {
|
||||
invoke(std::index_sequence_for<Args...>{});
|
||||
}
|
||||
}
|
||||
|
||||
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<std::mutex> lock(mutex);
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t... I>
|
||||
void invoke(std::index_sequence<I...>) {
|
||||
callback(func(std::forward<Args>(std::get<I>(params))...));
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
bool canceled = false;
|
||||
|
||||
F func;
|
||||
P params;
|
||||
C callback;
|
||||
};
|
||||
|
||||
using Queue = std::queue<std::shared_ptr<WorkTask>>;
|
||||
|
||||
@@ -32,21 +32,14 @@ public:
|
||||
// Invoke object->fn(args...) in the runloop thread.
|
||||
template <typename Fn, class... Args>
|
||||
void invoke(Fn fn, Args&&... args) {
|
||||
loop->invoke(bind<Fn, Args...>(fn), std::forward<Args>(args)...);
|
||||
loop->invoke(bind(fn), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
|
||||
template <typename Fn, class... Args>
|
||||
template <typename Fn, class Cb, class... Args>
|
||||
std::unique_ptr<WorkRequest>
|
||||
invokeWithResult(Fn fn, std::function<void ()> callback, Args&&... args) {
|
||||
return loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
|
||||
template <class R, typename Fn, class... Args>
|
||||
std::unique_ptr<WorkRequest>
|
||||
invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
|
||||
return loop->invokeWithResult(bind<Fn, Args...>(fn), callback, std::forward<Args>(args)...);
|
||||
invokeWithCallback(Fn fn, Cb&& callback, Args&&... args) {
|
||||
return loop->invokeWithCallback(bind(fn), callback, std::forward<Args>(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 <typename Fn, class... Args>
|
||||
template <typename Fn>
|
||||
auto bind(Fn fn) {
|
||||
return [fn, this] (Args&&... a) { return (object->*fn)(std::forward<Args>(a)...); };
|
||||
return [fn, this] (auto &&... args) {
|
||||
return (object->*fn)(std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename P, std::size_t... I>
|
||||
|
||||
@@ -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<void (bool)> callback) {
|
||||
callback(bucket->setImage(data));
|
||||
}
|
||||
|
||||
TileParseResult parseVectorTile(TileWorker* worker, std::string data) {
|
||||
void parseVectorTile(TileWorker* worker, std::string data, std::function<void (TileParseResult)> callback) {
|
||||
try {
|
||||
pbf tilePBF(reinterpret_cast<const unsigned char *>(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<void (TileParseResult)> 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<void ()> callback) {
|
||||
worker->redoPlacement(angle, collisionDebug);
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,22 +58,22 @@ Worker::~Worker() = default;
|
||||
|
||||
std::unique_ptr<WorkRequest> Worker::parseRasterTile(RasterBucket& bucket, std::string data, std::function<void (bool)> 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<WorkRequest> Worker::parseVectorTile(TileWorker& worker, std::string data, std::function<void (TileParseResult)> 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<WorkRequest> Worker::parseLiveTile(TileWorker& worker, const LiveTile& tile, std::function<void (TileParseResult)> 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<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, bool collisionDebug, std::function<void ()> 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
|
||||
|
||||
@@ -17,9 +17,9 @@ public:
|
||||
EXPECT_EQ(val, 1);
|
||||
}
|
||||
|
||||
int fn2() {
|
||||
void fn2(std::function<void (int)> cb) {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
return 1;
|
||||
cb(1);
|
||||
}
|
||||
|
||||
void transferIn(std::unique_ptr<int> val) {
|
||||
@@ -27,15 +27,15 @@ public:
|
||||
EXPECT_EQ(*val, 1);
|
||||
}
|
||||
|
||||
std::unique_ptr<int> transferOut() {
|
||||
void transferOut(std::function<void (std::unique_ptr<int>)> cb) {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
return std::make_unique<int>(1);
|
||||
cb(std::make_unique<int>(1));
|
||||
}
|
||||
|
||||
std::unique_ptr<int> transferInOut(std::unique_ptr<int> val) {
|
||||
void transferInOut(std::unique_ptr<int> val, std::function<void (std::unique_ptr<int>)> 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<int> val) {
|
||||
@@ -43,21 +43,21 @@ public:
|
||||
EXPECT_EQ(*val, 1);
|
||||
}
|
||||
|
||||
std::shared_ptr<int> transferOutShared() {
|
||||
void transferOutShared(std::function<void (std::shared_ptr<int>)> cb) {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
return std::make_shared<int>(1);
|
||||
cb(std::make_shared<int>(1));
|
||||
}
|
||||
|
||||
std::string transferString(const std::string& string) {
|
||||
void transferString(const std::string& string, std::function<void (std::string)> 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<void (bool)> 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<TestObject> thread({"Test", ThreadType::Map, ThreadPriority::Regular}, tid);
|
||||
|
||||
thread.invoke(&TestObject::fn1, 1);
|
||||
requests.push_back(thread.invokeWithResult<int>(&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<int>(1));
|
||||
requests.push_back(thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferOut, [&] (std::unique_ptr<int> result) {
|
||||
requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr<int> result) {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
EXPECT_EQ(*result, 1);
|
||||
}));
|
||||
|
||||
requests.push_back(thread.invokeWithResult<std::unique_ptr<int>>(&TestObject::transferInOut, [&] (std::unique_ptr<int> result) {
|
||||
requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, [&] (std::unique_ptr<int> result) {
|
||||
EXPECT_EQ(tid, std::this_thread::get_id());
|
||||
EXPECT_EQ(*result, 1);
|
||||
}, std::make_unique<int>(1)));
|
||||
|
||||
thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1));
|
||||
requests.push_back(thread.invokeWithResult<std::shared_ptr<int>>(&TestObject::transferOutShared, [&] (std::shared_ptr<int> result) {
|
||||
requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr<int> 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<std::string>(&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<TestObject> thread({"Test", ThreadType::Worker, ThreadPriority::Low}, tid);
|
||||
|
||||
requests.push_back(thread.invokeWithResult<bool>(&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<void ()> fn) { fn(); }
|
||||
TestWorker(uv_loop_t*) {
|
||||
}
|
||||
|
||||
void send(std::function<void ()> fn, std::function<void ()> 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<void> 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<void> 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<void> 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();
|
||||
|
||||
@@ -15,8 +15,9 @@ TEST_F(Storage, DatabaseDoesNotExist) {
|
||||
|
||||
SQLiteCache::Impl cache(nullptr, "test/fixtures/404/cache.db");
|
||||
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
EXPECT_EQ(1ul, dynamic_cast<FixtureLogObserver*>(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<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> 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<FixtureLogObserver>());
|
||||
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> 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<FixtureLogObserver>());
|
||||
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> 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<Response>();
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
|
||||
@@ -175,9 +180,10 @@ TEST_F(Storage, DatabaseLockedWrite) {
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> 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<Response> 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>();
|
||||
response->data = "Demo";
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> res) {
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
|
||||
@@ -221,8 +228,9 @@ TEST_F(Storage, DatabaseLockedRefresh) {
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
cache.refresh({ Resource::Unknown, "mapbox://test" }, response->expires);
|
||||
std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
|
||||
EXPECT_EQ(nullptr, res.get());
|
||||
cache.get({ Resource::Unknown, "mapbox://test" }, [] (std::unique_ptr<Response> 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>();
|
||||
response->data = "Demo";
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> 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<Response> 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>();
|
||||
response->data = "Demo";
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> 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<Response> res) {
|
||||
EXPECT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
|
||||
@@ -294,9 +304,10 @@ TEST_F(Storage, DatabaseInvalid) {
|
||||
auto response = std::make_shared<Response>();
|
||||
response->data = "Demo";
|
||||
cache.put({ Resource::Unknown, "mapbox://test" }, response);
|
||||
std::unique_ptr<Response> 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<Response> res) {
|
||||
EXPECT_NE(nullptr, res.get());
|
||||
EXPECT_EQ("Demo", res->data);
|
||||
});
|
||||
|
||||
auto observer = Log::removeObserver();
|
||||
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
|
||||
|
||||
Reference in New Issue
Block a user