2016-08-16 10:33:04 -07:00
|
|
|
// Copyright (c) 2016 The Zcash developers
|
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#include "asyncrpcoperation.h"
|
|
|
|
|
|
|
|
#include <boost/uuid/uuid.hpp>
|
|
|
|
#include <boost/uuid/uuid_generators.hpp>
|
|
|
|
#include <boost/uuid/uuid_io.hpp>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <ctime>
|
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
static boost::uuids::random_generator uuidgen;
|
|
|
|
|
2018-01-14 08:23:05 -08:00
|
|
|
static std::map<OperationStatus, std::string> OperationStatusMap = {
|
2016-08-16 10:33:04 -07:00
|
|
|
{OperationStatus::READY, "queued"},
|
|
|
|
{OperationStatus::EXECUTING, "executing"},
|
|
|
|
{OperationStatus::CANCELLED, "cancelled"},
|
|
|
|
{OperationStatus::FAILED, "failed"},
|
|
|
|
{OperationStatus::SUCCESS, "success"}
|
|
|
|
};
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Every operation instance should have a globally unique id
|
|
|
|
*/
|
|
|
|
AsyncRPCOperation::AsyncRPCOperation() : error_code_(0), error_message_() {
|
2016-08-16 10:33:04 -07:00
|
|
|
// Set a unique reference for each operation
|
|
|
|
boost::uuids::uuid uuid = uuidgen();
|
2016-09-08 12:32:29 -07:00
|
|
|
id_ = "opid-" + boost::uuids::to_string(uuid);
|
2016-08-28 19:50:39 -07:00
|
|
|
creation_time_ = (int64_t)time(NULL);
|
2016-09-08 12:32:29 -07:00
|
|
|
set_state(OperationStatus::READY);
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-09-07 19:39:33 -07:00
|
|
|
AsyncRPCOperation::AsyncRPCOperation(const AsyncRPCOperation& o) :
|
2016-09-08 12:32:29 -07:00
|
|
|
id_(o.id_), creation_time_(o.creation_time_), state_(o.state_.load()),
|
|
|
|
start_time_(o.start_time_), end_time_(o.end_time_),
|
|
|
|
error_code_(o.error_code_), error_message_(o.error_message_),
|
|
|
|
result_(o.result_)
|
2016-08-16 10:33:04 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncRPCOperation& AsyncRPCOperation::operator=( const AsyncRPCOperation& other ) {
|
2016-09-08 12:32:29 -07:00
|
|
|
this->id_ = other.id_;
|
2016-08-28 19:50:39 -07:00
|
|
|
this->creation_time_ = other.creation_time_;
|
|
|
|
this->state_.store(other.state_.load());
|
2016-09-08 12:32:29 -07:00
|
|
|
this->start_time_ = other.start_time_;
|
|
|
|
this->end_time_ = other.end_time_;
|
|
|
|
this->error_code_ = other.error_code_;
|
|
|
|
this->error_message_ = other.error_message_;
|
|
|
|
this->result_ = other.result_;
|
2016-08-16 10:33:04 -07:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AsyncRPCOperation::~AsyncRPCOperation() {
|
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Override this cancel() method if you can interrupt main() when executing.
|
|
|
|
*/
|
2016-08-16 10:33:04 -07:00
|
|
|
void AsyncRPCOperation::cancel() {
|
2016-08-28 19:50:39 -07:00
|
|
|
if (isReady()) {
|
|
|
|
set_state(OperationStatus::CANCELLED);
|
|
|
|
}
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Start timing the execution run of the code you're interested in
|
|
|
|
*/
|
|
|
|
void AsyncRPCOperation::start_execution_clock() {
|
2016-09-08 12:32:29 -07:00
|
|
|
std::lock_guard<std::mutex> guard(lock_);
|
2016-08-28 19:50:39 -07:00
|
|
|
start_time_ = std::chrono::system_clock::now();
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Stop timing the execution run
|
|
|
|
*/
|
|
|
|
void AsyncRPCOperation::stop_execution_clock() {
|
2016-09-08 12:32:29 -07:00
|
|
|
std::lock_guard<std::mutex> guard(lock_);
|
2016-08-28 19:50:39 -07:00
|
|
|
end_time_ = std::chrono::system_clock::now();
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Implement this virtual method in any subclass. This is just an example implementation.
|
|
|
|
*/
|
2016-08-16 10:33:04 -07:00
|
|
|
void AsyncRPCOperation::main() {
|
2016-08-28 19:50:39 -07:00
|
|
|
if (isCancelled()) {
|
2016-08-16 10:33:04 -07:00
|
|
|
return;
|
2016-08-28 19:50:39 -07:00
|
|
|
}
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
set_state(OperationStatus::EXECUTING);
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
start_execution_clock();
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
// Do some work here..
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
stop_execution_clock();
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
// If there was an error, you might set it like this:
|
|
|
|
/*
|
|
|
|
setErrorCode(123);
|
|
|
|
setErrorMessage("Murphy's law");
|
|
|
|
setState(OperationStatus::FAILED);
|
|
|
|
*/
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
// Otherwise, if the operation was a success:
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue v(UniValue::VSTR, "We have a result!");
|
2016-08-28 19:50:39 -07:00
|
|
|
set_result(v);
|
|
|
|
set_state(OperationStatus::SUCCESS);
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
2017-01-06 10:15:56 -08:00
|
|
|
* Return the error of the completed operation as a UniValue object.
|
|
|
|
* If there is no error, return null UniValue.
|
2016-08-28 19:50:39 -07:00
|
|
|
*/
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue AsyncRPCOperation::getError() const {
|
2016-08-28 19:50:39 -07:00
|
|
|
if (!isFailed()) {
|
2017-01-06 10:15:56 -08:00
|
|
|
return NullUniValue;
|
2016-08-28 19:50:39 -07:00
|
|
|
}
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-09-08 12:32:29 -07:00
|
|
|
std::lock_guard<std::mutex> guard(lock_);
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue error(UniValue::VOBJ);
|
2016-08-28 19:50:39 -07:00
|
|
|
error.push_back(Pair("code", this->error_code_));
|
|
|
|
error.push_back(Pair("message", this->error_message_));
|
2017-01-06 10:15:56 -08:00
|
|
|
return error;
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
2017-01-06 10:15:56 -08:00
|
|
|
* Return the result of the completed operation as a UniValue object.
|
|
|
|
* If the operation did not succeed, return null UniValue.
|
2016-08-28 19:50:39 -07:00
|
|
|
*/
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue AsyncRPCOperation::getResult() const {
|
2016-08-28 19:50:39 -07:00
|
|
|
if (!isSuccess()) {
|
2017-01-06 10:15:56 -08:00
|
|
|
return NullUniValue;
|
2016-08-28 19:50:39 -07:00
|
|
|
}
|
2016-08-16 10:33:04 -07:00
|
|
|
|
2016-09-08 12:32:29 -07:00
|
|
|
std::lock_guard<std::mutex> guard(lock_);
|
2016-08-28 19:50:39 -07:00
|
|
|
return this->result_;
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
2017-01-06 10:15:56 -08:00
|
|
|
* Returns a status UniValue object.
|
2016-08-16 10:33:04 -07:00
|
|
|
* If the operation has failed, it will include an error object.
|
|
|
|
* If the operation has succeeded, it will include the result value.
|
2016-09-07 19:39:33 -07:00
|
|
|
* If the operation was cancelled, there will be no error object or result value.
|
2016-08-16 10:33:04 -07:00
|
|
|
*/
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue AsyncRPCOperation::getStatus() const {
|
2016-08-16 10:33:04 -07:00
|
|
|
OperationStatus status = this->getState();
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue obj(UniValue::VOBJ);
|
2016-09-08 12:32:29 -07:00
|
|
|
obj.push_back(Pair("id", this->id_));
|
2016-08-16 10:33:04 -07:00
|
|
|
obj.push_back(Pair("status", OperationStatusMap[status]));
|
2016-08-28 19:50:39 -07:00
|
|
|
obj.push_back(Pair("creation_time", this->creation_time_));
|
2016-09-07 19:39:33 -07:00
|
|
|
// TODO: Issue #1354: There may be other useful metadata to return to the user.
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue err = this->getError();
|
|
|
|
if (!err.isNull()) {
|
2016-08-16 10:33:04 -07:00
|
|
|
obj.push_back(Pair("error", err.get_obj()));
|
|
|
|
}
|
2017-01-06 10:15:56 -08:00
|
|
|
UniValue result = this->getResult();
|
|
|
|
if (!result.isNull()) {
|
2016-08-16 10:33:04 -07:00
|
|
|
obj.push_back(Pair("result", result));
|
|
|
|
|
|
|
|
// Include execution time for successful operation
|
2016-08-28 19:50:39 -07:00
|
|
|
std::chrono::duration<double> elapsed_seconds = end_time_ - start_time_;
|
2016-08-16 10:33:04 -07:00
|
|
|
obj.push_back(Pair("execution_secs", elapsed_seconds.count()));
|
|
|
|
|
|
|
|
}
|
2017-01-06 10:15:56 -08:00
|
|
|
return obj;
|
2016-08-16 10:33:04 -07:00
|
|
|
}
|
|
|
|
|
2016-08-28 19:50:39 -07:00
|
|
|
/**
|
|
|
|
* Return the operation state in human readable form.
|
|
|
|
*/
|
2016-08-22 09:15:22 -07:00
|
|
|
std::string AsyncRPCOperation::getStateAsString() const {
|
|
|
|
OperationStatus status = this->getState();
|
|
|
|
return OperationStatusMap[status];
|
|
|
|
}
|