Files
MapLibreParticleLab/src/mbgl/storage/request.cpp
Thiago Marcos P. Santos 19881f8b24 Cancel all pending requests before leaving
This still has the unwanted side effect off blocking the Map thread
waiting for the workers to return (because they hold a reference
to the Map thread main loop).

Maybe the only way to solve this would be having a I/O thread for
dispatching messages.

The Request is not bound anymore to the invoker thread because the workers
will request for glyphs, but the reply will arrive at the Map thread.
2015-05-05 18:35:40 +03:00

106 lines
3.0 KiB
C++

#include <mbgl/storage/request.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/uv.hpp>
#include <uv.h>
#include <cassert>
#include <functional>
namespace mbgl {
struct Request::Canceled { std::mutex mutex; bool confirmed = false; };
// Note: This requires that loop is running in the current thread (or not yet running).
Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
: callback(callback_), resource(resource_) {
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
if (loop) {
async = new uv_async_t;
async->data = this;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
uv_async_init(loop, async, [](uv_async_t *a, int) { reinterpret_cast<Request *>(a->data)->notifyCallback(); });
#else
uv_async_init(loop, async, [](uv_async_t *a) { reinterpret_cast<Request *>(a->data)->notifyCallback(); });
#endif
}
}
// Called in the originating thread.
void Request::notifyCallback() {
if (!canceled) {
invoke();
} else {
bool destroy = false;
{
std::unique_lock<std::mutex> lock(canceled->mutex);
destroy = canceled->confirmed;
}
// Don't delete right way, because we have to unlock the mutex before deleting.
if (destroy) {
delete this;
}
}
}
void Request::invoke() {
assert(response);
// The user could supply a null pointer or empty std::function as a callback. In this case, we
// still do the file request, but we don't need to deliver a result.
if (callback) {
callback(*response);
}
delete this;
}
Request::~Request() {
if (async) {
uv_close(reinterpret_cast<uv_handle_t*>(async), [](uv_handle_t* handle) {
delete reinterpret_cast<uv_async_t*>(handle);
});
}
}
// Called in the FileSource thread.
void Request::notify(const std::shared_ptr<const Response> &response_) {
assert(!response);
response = response_;
assert(response);
if (async) {
uv_async_send(async);
} else {
// This request is not cancelable. This means that the callback will be executed in an
// arbitrary thread (== FileSource thread).
invoke();
}
}
// Called in the originating thread.
void Request::cancel() {
assert(async);
assert(!canceled);
canceled = util::make_unique<Canceled>();
}
// Called in the FileSource thread.
// Will only ever be invoked after cancel() was called in the original requesting thread.
void Request::destruct() {
assert(async);
assert(canceled);
std::unique_lock<std::mutex> lock(canceled->mutex);
canceled->confirmed = true;
uv_async_send(async);
// after this method returns, the FileSource thread has no knowledge of
// this object anymore.
}
}