From c76e1f3157a18e16519772ea89fffb526e4bb729 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Sat, 23 Jan 2021 21:39:23 -0600 Subject: [PATCH] Toward dynamically reloading contracts... Add the ability to dynamically unload an auxiliary evaluator (any evaluator for a given operation type except the first one registered for that operation type), and as an added convenience, add an idiomatically consistent way to check if a given index is already registered or not. --- .../chain/include/graphene/chain/database.hpp | 30 +++++++++++- .../include/graphene/chain/evaluator.hpp | 49 ++++++++++++++++--- .../include/graphene/db/object_database.hpp | 4 ++ libraries/db/object_database.cpp | 7 +++ 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 7e829f00..a4d3248b 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -347,6 +347,8 @@ namespace graphene { namespace chain { /** * @brief Register a new evaluator to the evaluator chain for its operation type * @tparam EvaluatorType An evaluator type which will be used to evaluate its declared operation type + * @return If registering an evaluator for an operation that already has an evaluator, returns a handle for + * the newly added evaluator which can be used to delete it later. * * This method registers a new evaluator type with tthe database. The evaluator specifies an operation type * which it should be used to evaluate. The evaluator will be instantiated each time an operaton of the @@ -356,15 +358,39 @@ namespace graphene { namespace chain { * multiple evaluator types are registered for a given operation type, they will all execute in the order of * registration; however, only the return value of the first registered evaluator will be returned; return * values of subsequently registered evaluators will be silently dropped. + * + * The first evaluator registered for a given operation type is permanent, and is the only evaluator which + * can return a value. Subsequent (auxiliary) evaluators for that operation type can be deleted at runtime + * by calling @ref delete_evaluator() with the evaluator_handle obtained when registering the evaluator. */ template - void register_evaluator() + optional register_evaluator() { auto& eval_ptr = _operation_evaluators[operation::tag::value]; if (eval_ptr == nullptr) eval_ptr = std::make_unique>(); else - eval_ptr->append_evaluator(std::make_unique>()); + return eval_ptr->append_evaluator(std::make_unique>()); + return {}; + } + + /** + * @brief Delete an auxiliary evaluator + * @param handle The evaluator handle for the evaluator to delete, as returned by @ref register_evaluator + * + * Auxiliary evaluators, or the second and subsequent evaluators registered for a given operation type, + * can be deleted so that they no longer execute when operations of the relevant type are processed. + * + * If it may be desired to delete an auxiliary evaluator, retain the evaluator handle obtained when the + * evaluator was initially registered and when it is necessary to delete the evaluator, pass the handle + * to this function. + * + * The evaluator will have been deleted by the time this function returns. + */ + void delete_evaluator(op_evaluator::evaluator_handle&& handle) + { + if ((uint64_t)handle.get_operation_type() < _operation_evaluators.size()) + _operation_evaluators[handle.get_operation_type()]->delete_evaluator(std::move(handle)); } //////////////////// db_balance.cpp //////////////////// diff --git a/libraries/chain/include/graphene/chain/evaluator.hpp b/libraries/chain/include/graphene/chain/evaluator.hpp index 25680528..ca13a1d3 100644 --- a/libraries/chain/include/graphene/chain/evaluator.hpp +++ b/libraries/chain/include/graphene/chain/evaluator.hpp @@ -122,29 +122,62 @@ namespace graphene { namespace chain { class op_evaluator { + protected: + unique_ptr next_evaluator; public: + class evaluator_handle { + // Move-only semantics, and only friends can construct + evaluator_handle(const op_evaluator* pointer, operation::tag_type type) + : pointer(pointer), operation_type(type) {} + evaluator_handle(const evaluator_handle&) = delete; + evaluator_handle& operator=(const evaluator_handle&) = delete; + + friend class op_evaluator; + template + friend class op_evaluator_impl; + + // Pointer to the handled evaluator + const op_evaluator* pointer; + // Tag of the handled evaluator + operation::tag_type operation_type; + + public: + evaluator_handle(evaluator_handle&&) = default; + evaluator_handle& operator=(evaluator_handle&&) = default; + + operation::tag_type get_operation_type() const { return operation_type; } + }; + virtual ~op_evaluator(){} - virtual void append_evaluator(unique_ptr next_evaluator) = 0; + virtual evaluator_handle append_evaluator(unique_ptr next_evaluator) = 0; + virtual void delete_evaluator(evaluator_handle&& handle) { + if (next_evaluator.get() == handle.pointer) + // Next evaluator in chain is the one to delete. Move its next pointer into ours, and unique_ptr will delete the one that's going away. + next_evaluator = std::move(next_evaluator->next_evaluator); + else + next_evaluator->delete_evaluator(std::move(handle)); + } virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply) = 0; }; template class op_evaluator_impl : public op_evaluator { - unique_ptr next_evaluator; public: - virtual void append_evaluator(unique_ptr next_evaluator) override { - if (this->next_evaluator == nullptr) + virtual evaluator_handle append_evaluator(unique_ptr next_evaluator) override { + if (this->next_evaluator == nullptr) { this->next_evaluator = std::move(next_evaluator); - else - this->next_evaluator->append_evaluator(std::move(next_evaluator)); + return evaluator_handle(this->next_evaluator.get(), operation::tag::value); + } else { + return this->next_evaluator->append_evaluator(std::move(next_evaluator)); + } } virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply = true) override { T eval; auto result = eval.start_evaluate(eval_state, op, apply); - if (next_evaluator != nullptr) - next_evaluator->evaluate(eval_state, op, apply); + if (this->next_evaluator != nullptr) + this->next_evaluator->evaluate(eval_state, op, apply); return result; } }; diff --git a/libraries/db/include/graphene/db/object_database.hpp b/libraries/db/include/graphene/db/object_database.hpp index d51249e1..a1efbfff 100644 --- a/libraries/db/include/graphene/db/object_database.hpp +++ b/libraries/db/include/graphene/db/object_database.hpp @@ -75,6 +75,10 @@ namespace graphene { namespace db { const index& get_index()const { return get_index(T::space_id,T::type_id); } const index& get_index(uint8_t space_id, uint8_t type_id)const; const index& get_index(object_id_type id)const { return get_index(id.space(),id.type()); } + template + const index* find_index()const { return find_index(T::space_id,T::type_id); } + const index* find_index(object_id_type id)const { return find_index(id.space(), id.type()); } + const index* find_index(uint8_t space_id, uint8_t type_id)const; /// @} const object& get_object( object_id_type id )const; diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index 7e67c926..2f8d9565 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -58,6 +58,13 @@ const index& object_database::get_index(uint8_t space_id, uint8_t type_id)const FC_ASSERT( tmp ); return *tmp; } + +const index* object_database::find_index(uint8_t space_id, uint8_t type_id) const +{ + if (_index.size() > space_id && _index[space_id].size() > type_id) + return _index[space_id][type_id].get(); + return nullptr; +} index& object_database::get_mutable_index(uint8_t space_id, uint8_t type_id) { FC_ASSERT( _index.size() > space_id, "", ("space_id",space_id)("type_id",type_id)("index.size",_index.size()) );