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.
This commit is contained in:
Nathan Hourt 2021-01-23 21:39:23 -06:00 committed by Michel Santos
parent 082df7ab4a
commit c76e1f3157
4 changed files with 80 additions and 10 deletions

View file

@ -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<typename EvaluatorType>
void register_evaluator()
optional<op_evaluator::evaluator_handle> register_evaluator()
{
auto& eval_ptr = _operation_evaluators[operation::tag<typename EvaluatorType::operation_type>::value];
if (eval_ptr == nullptr)
eval_ptr = std::make_unique<op_evaluator_impl<EvaluatorType>>();
else
eval_ptr->append_evaluator(std::make_unique<op_evaluator_impl<EvaluatorType>>());
return eval_ptr->append_evaluator(std::make_unique<op_evaluator_impl<EvaluatorType>>());
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 ////////////////////

View file

@ -122,29 +122,62 @@ namespace graphene { namespace chain {
class op_evaluator
{
protected:
unique_ptr<op_evaluator> 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<typename>
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<op_evaluator> next_evaluator) = 0;
virtual evaluator_handle append_evaluator(unique_ptr<op_evaluator> 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<typename T>
class op_evaluator_impl : public op_evaluator
{
unique_ptr<op_evaluator> next_evaluator;
public:
virtual void append_evaluator(unique_ptr<op_evaluator> next_evaluator) override {
if (this->next_evaluator == nullptr)
virtual evaluator_handle append_evaluator(unique_ptr<op_evaluator> 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<typename T::operation_type>::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;
}
};

View file

@ -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<typename T>
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;

View file

@ -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()) );