Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Clean up wasm cache entries based on irreversibility & fix wavm module cleanup #6983

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ struct controller_impl {
}
emit(self.irreversible_block, s);
}

wasmif.prune_wasm_cache(s->block_num);
}

void replay(std::function<bool()> shutdown) {
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/eosio_contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ void apply_eosio_setcode(apply_context& context) {

EOS_ASSERT( account.code_version != code_id, set_exact_code, "contract is already running this version of code" );

context.control.get_wasm_interface().account_code_change(account.code_version, code_id, context.control.head_block_num());

db.modify( account, [&]( auto& a ) {
/** TODO: consider whether a microsecond level local timestamp is sufficient to detect code version changes*/
// TODO: update setcode message to include the hash, then validate it in validate
Expand Down
6 changes: 6 additions & 0 deletions libraries/chain/include/eosio/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ namespace eosio { namespace chain {
//validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints
static void validate(const controller& control, const bytes& code);

//indicate when code has changed on an account, effectively reducing the reference count on
//the old one and increasing the reference count on the new one
void account_code_change(const digest_type& from, const digest_type& to, const uint32_t pending_block_num);

void prune_wasm_cache(const uint32_t through_block_num);

//Calls apply or error on a given code
void apply(const digest_type& code_id, const shared_string& code, apply_context& context);

Expand Down
35 changes: 30 additions & 5 deletions libraries/chain/include/eosio/chain/wasm_interface_private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@ using namespace fc;
using namespace eosio::chain::webassembly;
using namespace IR;
using namespace Runtime;
using boost::multi_index_container;

namespace eosio { namespace chain {

struct wasm_interface_impl {
struct wasm_cache_entry {
digest_type code_hash;
unsigned reference_count = 0;
//must be set to UINT32_MAX when reference_count > 1
uint32_t last_block_num_referenced = 0;
std::unique_ptr<wasm_instantiated_module_interface> module;
};
struct by_hash;
struct by_last_block_num;

wasm_interface_impl(wasm_interface::vm_type vm) {
if(vm == wasm_interface::vm_type::wavm)
runtime_interface = std::make_unique<webassembly::wavm::wavm_runtime>();
Expand Down Expand Up @@ -50,12 +61,14 @@ namespace eosio { namespace chain {
return mem_image;
}

std::unique_ptr<wasm_instantiated_module_interface>& get_instantiated_module( const digest_type& code_id,
const std::unique_ptr<wasm_instantiated_module_interface>& get_instantiated_module( const digest_type& code_id,
const shared_string& code,
transaction_context& trx_context )
{
auto it = instantiation_cache.find(code_id);
if(it == instantiation_cache.end()) {
wasm_cache_index::iterator it = wasm_instantiation_cache.find(code_id);
if(it == wasm_instantiation_cache.end())
it = wasm_instantiation_cache.emplace(wasm_interface_impl::wasm_cache_entry{code_id, 1, UINT32_MAX, nullptr}).first;
if(!it->module) {
auto timer_pause = fc::make_scoped_exit([&](){
trx_context.resume_billing_timer();
});
Expand Down Expand Up @@ -84,12 +97,24 @@ namespace eosio { namespace chain {
} catch(const IR::ValidationException& e) {
EOS_ASSERT(false, wasm_serialization_error, e.message.c_str());
}
it = instantiation_cache.emplace(code_id, runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module))).first;

wasm_instantiation_cache.modify(it, [&](auto& c) {
c.module = runtime_interface->instantiate_module((const char*)bytes.data(), bytes.size(), parse_initial_memory(module));
});
}
return it->second;
return it->module;
}

std::unique_ptr<wasm_runtime_interface> runtime_interface;

typedef boost::multi_index_container<
wasm_cache_entry,
indexed_by<
ordered_unique<tag<by_hash>, member<wasm_cache_entry, digest_type, &wasm_cache_entry::code_hash>>,
ordered_non_unique<tag<by_last_block_num>, member<wasm_cache_entry, uint32_t, &wasm_cache_entry::last_block_num_referenced>>
>
> wasm_cache_index;
wasm_cache_index wasm_instantiation_cache;
map<digest_type, std::unique_ptr<wasm_instantiated_module_interface>> instantiation_cache;
};

Expand Down
8 changes: 0 additions & 8 deletions libraries/chain/include/eosio/chain/webassembly/wavm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ class wavm_runtime : public eosio::chain::wasm_runtime_interface {
std::unique_ptr<wasm_instantiated_module_interface> instantiate_module(const char* code_bytes, size_t code_size, std::vector<uint8_t> initial_memory) override;

void immediately_exit_currently_running_module() override;

struct runtime_guard {
runtime_guard();
~runtime_guard();
};

private:
std::shared_ptr<runtime_guard> _runtime_guard;
};

//This is a temporary hack for the single threaded implementation
Expand Down
24 changes: 24 additions & 0 deletions libraries/chain/wasm_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,30 @@ namespace eosio { namespace chain {
my->get_instantiated_module(code_id, code, context.trx_context)->apply(context);
}

void wasm_interface::account_code_change(const digest_type& from, const digest_type& to, const uint32_t pending_block_num) {
if(from != digest_type()) {
wasm_interface_impl::wasm_cache_index::iterator it = my->wasm_instantiation_cache.find(from);
if(it != my->wasm_instantiation_cache.end())
my->wasm_instantiation_cache.modify(it, [&pending_block_num](auto& e) {
if(!--e.reference_count)
e.last_block_num_referenced = pending_block_num;
});
}
if(to != digest_type()) {
auto success = my->wasm_instantiation_cache.emplace(wasm_interface_impl::wasm_cache_entry{to, 1, UINT32_MAX, nullptr});
if(!success.second)
my->wasm_instantiation_cache.modify(success.first, [](auto& e) {
++e.reference_count;
e.last_block_num_referenced = UINT32_MAX;
});
}
}

void wasm_interface::prune_wasm_cache(const uint32_t through_block_num) {
auto& blockidx = my->wasm_instantiation_cache.get<wasm_interface_impl::by_last_block_num>();
blockidx.erase(blockidx.begin(), blockidx.upper_bound(through_block_num));
}

void wasm_interface::exit() {
my->runtime_interface->immediately_exit_currently_running_module();
}
Expand Down
61 changes: 38 additions & 23 deletions libraries/chain/webassembly/wavm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#include "Runtime/Linker.h"
#include "Runtime/Intrinsics.h"

#include <mutex>
#include <vector>
#include <iterator>

using namespace IR;
using namespace Runtime;
Expand All @@ -21,14 +22,47 @@ namespace eosio { namespace chain { namespace webassembly { namespace wavm {

running_instance_context the_running_instance_context;

namespace detail {
struct wavm_runtime_initializer {
wavm_runtime_initializer() {
Runtime::init();
}
};

using live_module_ref = std::list<ObjectInstance*>::iterator;

struct wavm_live_modules {
live_module_ref add_live_module(ModuleInstance* module_instance) {
return live_modules.insert(live_modules.begin(), asObject(module_instance));
}

void remove_live_module(live_module_ref it) {
live_modules.erase(it);
std::vector<ObjectInstance*> root;
std::copy(live_modules.begin(), live_modules.end(), std::back_inserter(root));
Runtime::freeUnreferencedObjects(std::move(root));
}

std::list<ObjectInstance*> live_modules;
};

static wavm_live_modules the_wavm_live_modules;

}

class wavm_instantiated_module : public wasm_instantiated_module_interface {
public:
wavm_instantiated_module(ModuleInstance* instance, std::unique_ptr<Module> module, std::vector<uint8_t> initial_mem) :
_initial_memory(initial_mem),
_instance(instance),
_module(std::move(module))
_module(std::move(module)),
_module_ref(detail::the_wavm_live_modules.add_live_module(instance))
{}

~wavm_instantiated_module() {
detail::the_wavm_live_modules.remove_live_module(_module_ref);
}

void apply(apply_context& context) override {
vector<Value> args = {Value(uint64_t(context.receiver)),
Value(uint64_t(context.act.account)),
Expand Down Expand Up @@ -79,30 +113,11 @@ class wavm_instantiated_module : public wasm_instantiated_module_interface {
//_instance is deleted via WAVM's object garbage collection when wavm_rutime is deleted
ModuleInstance* _instance;
std::unique_ptr<Module> _module;
detail::live_module_ref _module_ref;
};


wavm_runtime::runtime_guard::runtime_guard() {
// TODO clean this up
//check_wasm_opcode_dispositions();
Runtime::init();
}

wavm_runtime::runtime_guard::~runtime_guard() {
Runtime::freeUnreferencedObjects({});
}

static weak_ptr<wavm_runtime::runtime_guard> __runtime_guard_ptr;
static std::mutex __runtime_guard_lock;

wavm_runtime::wavm_runtime() {
std::lock_guard<std::mutex> l(__runtime_guard_lock);
if (__runtime_guard_ptr.use_count() == 0) {
_runtime_guard = std::make_shared<runtime_guard>();
__runtime_guard_ptr = _runtime_guard;
} else {
_runtime_guard = __runtime_guard_ptr.lock();
}
static detail::wavm_runtime_initializer the_wavm_runtime_initializer;
}

wavm_runtime::~wavm_runtime() {
Expand Down