/* * This file Copyright (C) 2016 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * */ #include #include "RpcQueue.h" // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) RpcQueue::Tag RpcQueue::next_tag = {}; RpcQueue::RpcQueue(QObject* parent) : QObject(parent) , tag_(next_tag++) { connect(&future_watcher_, &QFutureWatcher::finished, this, &RpcQueue::stepFinished); } void RpcQueue::stepFinished() { RpcResponse result; if (future_watcher_.future().isResultReadyAt(0)) { result = future_watcher_.result(); RpcResponseFuture future = future_watcher_.future(); // we can't handle network errors, abort queue and pass the error upwards if (result.networkError != QNetworkReply::NoError) { assert(!result.success); promise_.reportFinished(&result); deleteLater(); return; } // call user-handler for ordinary errors if (!result.success && next_error_handler_) { next_error_handler_(future); } // run next request, if we have one to run and there was no error (or if we tolerate errors) if ((result.success || tolerate_errors_) && !queue_.isEmpty()) { runNext(future); return; } } else { assert(!next_error_handler_); assert(queue_.isEmpty()); // one way or another, the last step returned nothing. // assume it is OK and ensure that we're not going to give an empty response object to any of the next steps. result.success = true; } promise_.reportFinished(&result); deleteLater(); } void RpcQueue::runNext(RpcResponseFuture const& response) { assert(!queue_.isEmpty()); RpcResponseFuture const old_future = future_watcher_.future(); for (;;) { auto next = queue_.dequeue(); next_error_handler_ = next.second; future_watcher_.setFuture((next.first)(response)); if (old_future != future_watcher_.future()) { break; } if (queue_.isEmpty()) { deleteLater(); break; } } } void RpcQueue::run() { runNext(RpcResponseFuture()); }