From 00619bf9cffed3914e7366ce8f7ff8ee61eaca4d Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Thu, 17 Aug 2023 18:52:44 -0400 Subject: [PATCH 01/11] use single backend per thread --- .../eosio/chain/webassembly/eos-vm.hpp | 7 +++-- .../chain/webassembly/runtimes/eos-vm.cpp | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp index 5a1a058878..4b182dcd41 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp @@ -49,8 +49,11 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface { private: // todo: managing this will get more complicated with sync calls; - eos_vm_backend_t* _bkend = nullptr; // non owning pointer to allow for immediate exit - context_t _exec_ctx; + // Each thread uses its own backend and exec context. + // Their constructors do not take any arguments; therefore their life time + // do not rely on others. Safe to be thread_local. + thread_local static eos_vm_backend_t _bkend; + thread_local static context_t _exec_ctx; template friend class eos_vm_instantiated_module; diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index e23f91b90e..13bbbf6801 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -129,14 +129,17 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { - // Reset execution context (reused per thread) + // re-initialize backend from the instantiate module of the contract + _runtime->_bkend = *_instantiated_module; + // re-initialize exec ctx with instantiate module _runtime->_exec_ctx.set_module(&(_instantiated_module->get_module())); - _instantiated_module->set_context(&_runtime->_exec_ctx); - _instantiated_module->reset_max_call_depth(); - _instantiated_module->reset_max_pages(); + // link exe ctx to backend + _runtime->_bkend.set_context(&_runtime->_exec_ctx); + // set other per apply data + _runtime->_bkend.reset_max_call_depth(); + _runtime->_bkend.reset_max_pages(); + _runtime->_bkend.set_wasm_allocator(&context.control.get_wasm_allocator()); - _instantiated_module->set_wasm_allocator(&context.control.get_wasm_allocator()); - _runtime->_bkend = _instantiated_module.get(); apply_options opts; if(context.control.is_builtin_activated(builtin_protocol_feature_t::configurable_wasm_limits)) { const wasm_config& config = context.control.get_global_properties().wasm_configuration; @@ -144,8 +147,8 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { } auto fn = [&]() { eosio::chain::webassembly::interface iface(context); - _runtime->_bkend->initialize(&iface, opts); - _runtime->_bkend->call( + _runtime->_bkend.initialize(&iface, opts); + _runtime->_bkend.call( iface, "env", "apply", context.get_receiver().to_uint64_t(), context.get_action().account.to_uint64_t(), @@ -153,7 +156,7 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { }; try { checktime_watchdog wd(context.trx_context.transaction_timer); - _runtime->_bkend->timed_run(wd, fn); + _runtime->_bkend.timed_run(wd, fn); } catch(eosio::vm::timeout_exception&) { context.trx_context.checktime(); } catch(eosio::vm::wasm_memory_exception& e) { @@ -161,7 +164,6 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { } catch(eosio::vm::exception& e) { FC_THROW_EXCEPTION(wasm_execution_error, "eos-vm system failure"); } - _runtime->_bkend = nullptr; } private: @@ -282,6 +284,10 @@ std::unique_ptr eos_vm_profile_runtime::inst } #endif +template +thread_local eos_vm_runtime::context_t eos_vm_runtime::_exec_ctx; +template +thread_local eos_vm_backend_t eos_vm_runtime::_bkend; } template From 7eff62bf7b7e5223d11961175863ad1484f50107 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Fri, 18 Aug 2023 21:37:31 -0400 Subject: [PATCH 02/11] use single wasm_interface; remove wasm_interface_collection.hpp and wasm_interface_collection.cpp; simplify multi-threading wasm_interface code significantly --- libraries/chain/CMakeLists.txt | 1 - libraries/chain/apply_context.cpp | 1 - libraries/chain/controller.cpp | 25 ++-- .../chain/include/eosio/chain/controller.hpp | 3 +- .../include/eosio/chain/wasm_interface.hpp | 24 +++- .../eosio/chain/wasm_interface_collection.hpp | 65 --------- .../eosio/chain/wasm_interface_private.hpp | 54 ++++++- libraries/chain/wasm_interface.cpp | 53 ++++++- libraries/chain/wasm_interface_collection.cpp | 134 ------------------ libraries/testing/tester.cpp | 1 - tests/test_read_only_trx.cpp | 5 +- 11 files changed, 134 insertions(+), 232 deletions(-) delete mode 100644 libraries/chain/include/eosio/chain/wasm_interface_collection.hpp delete mode 100644 libraries/chain/wasm_interface_collection.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index e44014b39c..67f51fc8bf 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -106,7 +106,6 @@ add_library( eosio_chain wast_to_wasm.cpp wasm_interface.cpp - wasm_interface_collection.cpp wasm_eosio_validation.cpp wasm_eosio_injection.cpp wasm_config.cpp diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 6dc0f9a1b6..d4a067feb5 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index a620336b31..ebfdf2ab84 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -256,7 +255,7 @@ struct controller_impl { #if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) thread_local static vm::wasm_allocator wasm_alloc; // a copy for main thread and each read-only thread #endif - wasm_interface_collection wasm_if_collect; + wasm_interface wasmif; app_window_type app_window = app_window_type::write; typedef pair handler_key; @@ -315,7 +314,7 @@ struct controller_impl { chain_id( chain_id ), read_mode( cfg.read_mode ), thread_pool(), - wasm_if_collect( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) + wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) { fork_db.open( [this]( block_timestamp_type timestamp, const flat_set& cur_features, @@ -342,7 +341,7 @@ struct controller_impl { set_activation_handler(); self.irreversible_block.connect([this](const block_state_ptr& bsp) { - wasm_if_collect.current_lib(bsp->block_num); + wasmif.current_lib(bsp->block_num); }); @@ -2686,20 +2685,26 @@ struct controller_impl { #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED bool is_eos_vm_oc_enabled() const { - return wasm_if_collect.is_eos_vm_oc_enabled(); + return wasmif.is_eos_vm_oc_enabled(); } #endif + // Only called from read-only trx execution threads when producer_plugin + // starts them. Only OC requires initialize thread specific data. void init_thread_local_data() { - wasm_if_collect.init_thread_local_data(db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty()); +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + if ( is_eos_vm_oc_enabled() ) { + wasmif.init_thread_local_data(); + } +#endif } - wasm_interface_collection& get_wasm_interface() { - return wasm_if_collect; + wasm_interface& get_wasm_interface() { + return wasmif; } void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) { - wasm_if_collect.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); + wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); } block_state_ptr fork_db_head() const; @@ -3400,7 +3405,7 @@ const apply_handler* controller::find_apply_handler( account_name receiver, acco } return nullptr; } -wasm_interface_collection& controller::get_wasm_interface() { +wasm_interface& controller::get_wasm_interface() { return my->get_wasm_interface(); } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 5960853786..965f96628a 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -38,7 +38,6 @@ namespace eosio { namespace chain { class account_object; class deep_mind_handler; class subjective_billing; - class wasm_interface_collection; using resource_limits::resource_limits_manager; using apply_handler = std::function; using forked_branch_callback = std::function; @@ -349,7 +348,7 @@ namespace eosio { namespace chain { */ const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const; - wasm_interface_collection& get_wasm_interface(); + wasm_interface& get_wasm_interface(); static chain_id_type extract_chain_id(snapshot_reader& snapshot); diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 7e67d28151..b70efe5209 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -46,11 +46,15 @@ namespace eosio { namespace chain { oc_none }; - wasm_interface(vm_type vm, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile); + inline static bool test_disable_tierup = false; // set by unittests to test tierup failing + + wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile); ~wasm_interface(); +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED // initialize exec per thread void init_thread_local_data(); +#endif //call before dtor to skip what can be minutes of dtor overhead with some runtimes; can cause leaks void indicate_shutting_down(); @@ -61,15 +65,29 @@ namespace eosio { namespace chain { //indicate that a particular code probably won't be used after given block_num void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); - //indicate the current LIB. evicts old cache entries, each evicted entry is provided to callback - void current_lib(const uint32_t lib, const std::function& callback); + //indicate the current LIB. evicts old cache entries + //, each evicted entry is provided to callback + void current_lib(const uint32_t lib); //Calls apply or error on a given code void apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context); + // only called from non-main threads (read-only trx execution threads) when producer_plugin starts them + //void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, bool profile); + +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + bool is_eos_vm_oc_enabled() const; +#endif + //Returns true if the code is cached bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const; + + // If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true, + // then apply returns immediately. Provided function must be multi-thread safe. + std::function substitute_apply; + private: + vm_oc_enable eosvmoc_tierup; unique_ptr my; }; diff --git a/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp b/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp deleted file mode 100644 index 4ee4ac7388..0000000000 --- a/libraries/chain/include/eosio/chain/wasm_interface_collection.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace eosio::chain { - - /** - * @class wasm_interface_collection manages the active wasm_interface to use for execution. - */ - class wasm_interface_collection { - public: - inline static bool test_disable_tierup = false; // set by unittests to test tierup failing - - wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, - const chainbase::database& d, const std::filesystem::path& data_dir, - const eosvmoc::config& eosvmoc_config, bool profile); - - ~wasm_interface_collection(); - - void apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context); - - // used for tests, only valid on main thread - bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) { - EOS_ASSERT(is_on_main_thread(), wasm_execution_error, "is_code_cached called off the main thread"); - return wasmif.is_code_cached(code_hash, vm_type, vm_version); - } - - // update current lib of all wasm interfaces - void current_lib(const uint32_t lib); - - // only called from non-main threads (read-only trx execution threads) when producer_plugin starts them - void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, bool profile); - -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - bool is_eos_vm_oc_enabled() const { - return ((eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) || wasm_runtime == wasm_interface::vm_type::eos_vm_oc); - } -#endif - - void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num); - - // If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true, - // then apply returns immediately. Provided function must be multi-thread safe. - std::function substitute_apply; - - private: - bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); }; - - private: - const std::thread::id main_thread_id; - const wasm_interface::vm_type wasm_runtime; - const wasm_interface::vm_oc_enable eosvmoc_tierup; - - wasm_interface wasmif; // used by main thread - std::mutex threaded_wasmifs_mtx; - std::unordered_map> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit - -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - std::unique_ptr eosvmoc; // used by all threads -#endif - }; - -} // eosio::chain diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index e7ea5776e6..541ae187c4 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -43,7 +43,31 @@ namespace eosio { namespace chain { struct by_hash; struct by_last_block_num; - wasm_interface_impl(wasm_interface::vm_type vm, const chainbase::database& d, +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED +struct eosvmoc_tier { + // Called from main thread + eosvmoc_tier(const std::filesystem::path& d, const eosvmoc::config& c, const chainbase::database& db) + : cc(d, c, db) { + // Construct exec and mem for the main thread + exec = std::make_unique(cc); + mem = std::make_unique(wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size); + } + + // Called from read-only threads + void init_thread_local_data() { + exec = std::make_unique(cc); + mem = std::make_unique(eosvmoc::memory::sliced_pages_for_ro_thread); + } + + eosvmoc::code_cache_async cc; + + // Each thread requires its own exec and mem. Defined in wasm_interface.cpp + thread_local static std::unique_ptr exec; + thread_local static std::unique_ptr mem; +}; +#endif + + wasm_interface_impl(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) : db(d) , wasm_runtime_time(vm) @@ -66,6 +90,13 @@ namespace eosio { namespace chain { #endif if(!runtime_interface) EOS_THROW(wasm_exception, "${r} wasm runtime not supported on this platform and/or configuration", ("r", vm)); + +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + if(eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) { + EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated"); + eosvmoc = std::make_unique(data_dir, eosvmoc_config, d); + } +#endif } ~wasm_interface_impl() = default; @@ -84,18 +115,23 @@ namespace eosio { namespace chain { } // reports each code_hash and vm_version that will be erased to callback - void current_lib(uint32_t lib, const std::function& callback) { + void current_lib(uint32_t lib) { //anything last used before or on the LIB can be evicted const auto first_it = wasm_instantiation_cache.get().begin(); const auto last_it = wasm_instantiation_cache.get().upper_bound(lib); - if (callback) { - for(auto it = first_it; it != last_it; it++) { - callback(it->code_hash, it->vm_version); - } - } +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + if(eosvmoc) for(auto it = first_it; it != last_it; it++) + eosvmoc->cc.free_code(it->code_hash, it->vm_version); +#endif wasm_instantiation_cache.get().erase(first_it, last_it); } +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + bool is_eos_vm_oc_enabled() const { + return (eosvmoc || wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc); + } +#endif + const std::unique_ptr& get_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, transaction_context& trx_context ) { @@ -148,6 +184,10 @@ namespace eosio { namespace chain { const chainbase::database& db; const wasm_interface::vm_type wasm_runtime_time; + +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + std::unique_ptr eosvmoc{nullptr}; // used by all threads +#endif }; } } // eosio::chain diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index abe014d946..0348157d03 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -32,15 +32,19 @@ namespace eosio { namespace chain { - wasm_interface::wasm_interface(vm_type vm, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) - : my( new wasm_interface_impl(vm, d, data_dir, eosvmoc_config, profile) ) {} + wasm_interface::wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) + : eosvmoc_tierup(eosvmoc_tierup), my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ) {} wasm_interface::~wasm_interface() {} #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED void wasm_interface::init_thread_local_data() { - if (my->wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface) + // OC tierup and OC runtime are mutually exclusive + if (my->eosvmoc) { + my->eosvmoc->init_thread_local_data(); + } else if (my->wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface) { my->runtime_interface->init_thread_local_data(); + } } #endif @@ -76,11 +80,39 @@ namespace eosio { namespace chain { my->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); } - void wasm_interface::current_lib(const uint32_t lib, const std::function& callback) { - my->current_lib(lib, callback); + void wasm_interface::current_lib(const uint32_t lib) { + my->current_lib(lib); } void wasm_interface::apply( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context ) { + if (substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context)) + return; +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + if (my->eosvmoc && (eosvmoc_tierup == wasm_interface::vm_oc_enable::oc_all || context.should_use_eos_vm_oc())) { + const chain::eosvmoc::code_descriptor* cd = nullptr; + chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary; + try { + const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name; + cd = my->eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure); + if (test_disable_tierup) + cd = nullptr; + } catch (...) { + // swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline + // In the future, consider moving bits of EOS VM that can fire exceptions and such out of this call path + static bool once_is_enough; + if (!once_is_enough) + elog("EOS VM OC has encountered an unexpected failure"); + once_is_enough = true; + } + if (cd) { + if (!context.is_applying_block()) // read_only_trx_test.py looks for this log statement + tlog("${a} speculatively executing ${h} with eos vm oc", ("a", context.get_receiver())("h", code_hash)); + my->eosvmoc->exec->execute(*cd, *my->eosvmoc->mem, context); + return; + } + } +#endif + my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context)->apply(context); } @@ -88,9 +120,20 @@ namespace eosio { namespace chain { return my->is_code_cached(code_hash, vm_type, vm_version); } +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + bool wasm_interface::is_eos_vm_oc_enabled() const { + return my->is_eos_vm_oc_enabled(); + } +#endif + wasm_instantiated_module_interface::~wasm_instantiated_module_interface() = default; wasm_runtime_interface::~wasm_runtime_interface() = default; +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + thread_local std::unique_ptr wasm_interface_impl::eosvmoc_tier::exec{}; + thread_local std::unique_ptr wasm_interface_impl::eosvmoc_tier::mem{}; +#endif + std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) { std::string s; in >> s; diff --git a/libraries/chain/wasm_interface_collection.cpp b/libraries/chain/wasm_interface_collection.cpp deleted file mode 100644 index 0ea68397bf..0000000000 --- a/libraries/chain/wasm_interface_collection.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED -#include -#else -#define _REGISTER_EOSVMOC_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG) -#endif - -namespace eosio::chain { - -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED -struct eosvmoc_tier { - // Called from main thread - eosvmoc_tier(const std::filesystem::path& d, const eosvmoc::config& c, const chainbase::database& db) - : cc(d, c, db) { - // Construct exec and mem for the main thread - exec = std::make_unique(cc); - mem = std::make_unique(wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size); - } - - // Called from read-only threads - void init_thread_local_data() { - exec = std::make_unique(cc); - mem = std::make_unique(eosvmoc::memory::sliced_pages_for_ro_thread); - } - - eosvmoc::code_cache_async cc; - - // Each thread requires its own exec and mem. - thread_local static std::unique_ptr exec; - thread_local static std::unique_ptr mem; -}; - -thread_local std::unique_ptr eosvmoc_tier::exec{}; -thread_local std::unique_ptr eosvmoc_tier::mem{}; -#endif - -wasm_interface_collection::wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, - const chainbase::database& d, const std::filesystem::path& data_dir, - const eosvmoc::config& eosvmoc_config, bool profile) - : main_thread_id(std::this_thread::get_id()) - , wasm_runtime(vm) - , eosvmoc_tierup(eosvmoc_tierup) - , wasmif(vm, d, data_dir, eosvmoc_config, profile) { -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if (eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) { - EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated"); - eosvmoc = std::make_unique(data_dir, eosvmoc_config, d); - } -#endif -} - -wasm_interface_collection::~wasm_interface_collection() = default; - -void wasm_interface_collection::apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context) { - if (substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context)) - return; -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if (eosvmoc && (eosvmoc_tierup == wasm_interface::vm_oc_enable::oc_all || context.should_use_eos_vm_oc())) { - const chain::eosvmoc::code_descriptor* cd = nullptr; - chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary; - try { - const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name; - cd = eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure); - if (test_disable_tierup) - cd = nullptr; - } catch (...) { - // swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline - // In the future, consider moving bits of EOS VM that can fire exceptions and such out of this call path - static bool once_is_enough; - if (!once_is_enough) - elog("EOS VM OC has encountered an unexpected failure"); - once_is_enough = true; - } - if (cd) { - if (!context.is_applying_block()) // read_only_trx_test.py looks for this log statement - tlog("${a} speculatively executing ${h} with eos vm oc", ("a", context.get_receiver())("h", code_hash)); - eosvmoc->exec->execute(*cd, *eosvmoc->mem, context); - return; - } - } -#endif - if (is_on_main_thread()) { - wasmif.apply(code_hash, vm_type, vm_version, context); - return; - } - threaded_wasmifs[std::this_thread::get_id()]->apply(code_hash, vm_type, vm_version, context); -} - -// update current lib of all wasm interfaces -void wasm_interface_collection::current_lib(const uint32_t lib) { - // producer_plugin has already asserted irreversible_block signal is called in write window - std::function cb{}; -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if (eosvmoc) { - cb = [&](const digest_type& code_hash, uint8_t vm_version) { - eosvmoc->cc.free_code(code_hash, vm_version); - }; - } -#endif - wasmif.current_lib(lib, cb); - for (auto& w : threaded_wasmifs) { - w.second->current_lib(lib, cb); - } -} - -// only called from non-main threads (read-only trx execution threads) when producer_plugin starts them -void wasm_interface_collection::init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, - const eosvmoc::config& eosvmoc_config, bool profile) { - EOS_ASSERT(!is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread"); -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - if (is_eos_vm_oc_enabled()) { - // EOSVMOC needs further initialization of its thread local data - if (eosvmoc) - eosvmoc->init_thread_local_data(); - wasmif.init_thread_local_data(); - } -#endif - - std::lock_guard g(threaded_wasmifs_mtx); - // Non-EOSVMOC needs a wasmif per thread - threaded_wasmifs[std::this_thread::get_id()] = std::make_unique(wasm_runtime, d, data_dir, eosvmoc_config, profile); -} - -void wasm_interface_collection::code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) { - // The caller of this function apply_eosio_setcode has already asserted that - // the transaction is not a read-only trx, which implies we are - // in write window. Safe to call threaded_wasmifs's code_block_num_last_used - wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num); - for (auto& w : threaded_wasmifs) { - w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); - } -} - -} // namespace eosio::chain diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index b57103af15..6bcc9d1b8d 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/test_read_only_trx.cpp b/tests/test_read_only_trx.cpp index b371730681..b2e615ef14 100644 --- a/tests/test_read_only_trx.cpp +++ b/tests/test_read_only_trx.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -90,9 +89,9 @@ BOOST_AUTO_TEST_CASE(not_check_configs_if_no_read_only_threads) { void test_trxs_common(std::vector& specific_args, bool test_disable_tierup = false) { try { fc::scoped_exit> on_exit = []() { - chain::wasm_interface_collection::test_disable_tierup = false; + chain::wasm_interface::test_disable_tierup = false; }; - chain::wasm_interface_collection::test_disable_tierup = test_disable_tierup; + chain::wasm_interface::test_disable_tierup = test_disable_tierup; using namespace std::chrono_literals; fc::temp_directory temp; From 1451b6c1c9b3f3a042f6985b89ef9060f75ec860 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 22 Aug 2023 18:12:29 -0400 Subject: [PATCH 03/11] simplify get_instantiated_module() and make common case faster --- .../eosio/chain/wasm_interface_private.hpp | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 541ae187c4..342cb554ba 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -137,31 +137,30 @@ struct eosvmoc_tier { { wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); - const code_object* codeobject = nullptr; - if(it == wasm_instantiation_cache.end()) { - codeobject = &db.get(boost::make_tuple(code_hash, vm_type, vm_version)); - - it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry{ - .code_hash = code_hash, - .last_block_num_used = UINT32_MAX, - .module = nullptr, - .vm_type = vm_type, - .vm_version = vm_version - } ).first; + if(it != wasm_instantiation_cache.end()) { + // An instantiated module's module should never be null. + assert(it->module); + return it->module; } - if(!it->module) { - if(!codeobject) - codeobject = &db.get(boost::make_tuple(code_hash, vm_type, vm_version)); + // Parse the contract WASM into an instantiated module and + // cache the instantiated module. + const code_object* codeobject = &db.get(boost::make_tuple(code_hash, vm_type, vm_version)); + it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry{ + .code_hash = code_hash, + .last_block_num_used = UINT32_MAX, + .module = nullptr, + .vm_type = vm_type, + .vm_version = vm_version + } ).first; + auto timer_pause = fc::make_scoped_exit([&](){ + trx_context.resume_billing_timer(); + }); + trx_context.pause_billing_timer(); + wasm_instantiation_cache.modify(it, [&](auto& c) { + c.module = runtime_interface->instantiate_module(codeobject->code.data(), codeobject->code.size(), code_hash, vm_type, vm_version); + }); - auto timer_pause = fc::make_scoped_exit([&](){ - trx_context.resume_billing_timer(); - }); - trx_context.pause_billing_timer(); - wasm_instantiation_cache.modify(it, [&](auto& c) { - c.module = runtime_interface->instantiate_module(codeobject->code.data(), codeobject->code.size(), code_hash, vm_type, vm_version); - }); - } return it->module; } From 2773c5036419f4da4666095e2ff200c5c8f49900 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 22 Aug 2023 19:14:30 -0400 Subject: [PATCH 04/11] add a mutex to wasm_instantiation_cache --- .../eosio/chain/wasm_interface_private.hpp | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 342cb554ba..fa976b6602 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -22,6 +22,8 @@ #include #include +#include + using namespace fc; using namespace eosio::chain::webassembly; using namespace IR; @@ -102,11 +104,16 @@ struct eosvmoc_tier { ~wasm_interface_impl() = default; bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const { + std::shared_lock g(instantiation_cache_mutex); wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); return it != wasm_instantiation_cache.end(); } void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num) { + // The caller of this method apply_eosio_setcode has asserted that + // the transaction is not read-only, implying we are + // in write window. Read-only threads are not running. + // Safe to update the cache without locking. wasm_cache_index::iterator it = wasm_instantiation_cache.find(boost::make_tuple(code_hash, vm_type, vm_version)); if(it != wasm_instantiation_cache.end()) wasm_instantiation_cache.modify(it, [block_num](wasm_cache_entry& e) { @@ -116,7 +123,10 @@ struct eosvmoc_tier { // reports each code_hash and vm_version that will be erased to callback void current_lib(uint32_t lib) { - //anything last used before or on the LIB can be evicted + // producer_plugin has asserted irreversible_block signal is called + // in write window. Read-only threads are not running. + // Safe to update the cache without locking. + // Anything last used before or on the LIB can be evicted. const auto first_it = wasm_instantiation_cache.get().begin(); const auto last_it = wasm_instantiation_cache.get().upper_bound(lib); #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED @@ -135,6 +145,7 @@ struct eosvmoc_tier { const std::unique_ptr& get_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, transaction_context& trx_context ) { + std::shared_lock r_lock(instantiation_cache_mutex); wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); if(it != wasm_instantiation_cache.end()) { @@ -142,6 +153,19 @@ struct eosvmoc_tier { assert(it->module); return it->module; } + r_lock.unlock(); + + // Acquire a unique lock as we need to modify the cache + std::unique_lock rw_lock(instantiation_cache_mutex); + + // While waiting for aquiring the write lock, another thread might + // have already compiled the contract and it is ready for use. + // Check again if that's the case. + it = wasm_instantiation_cache.find(boost::make_tuple(code_hash, vm_type, vm_version) ); + if (it != wasm_instantiation_cache.end()) { + assert(it->module); + return it->module; + } // Parse the contract WASM into an instantiated module and // cache the instantiated module. @@ -183,6 +207,7 @@ struct eosvmoc_tier { const chainbase::database& db; const wasm_interface::vm_type wasm_runtime_time; + mutable std::shared_mutex instantiation_cache_mutex; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED std::unique_ptr eosvmoc{nullptr}; // used by all threads From bec7ce87f73bf07e3d20bc567cc84b4e68b6af7a Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 23 Aug 2023 11:16:49 -0400 Subject: [PATCH 05/11] lock wasm_instantiation_cache only when read only threads are processing transactions --- .../eosio/chain/wasm_interface_private.hpp | 77 ++++++++++++------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index fa976b6602..555e67aca2 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -104,6 +104,8 @@ struct eosvmoc_tier { ~wasm_interface_impl() = default; bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const { + // This method is only called from tests. No need to check if we should + // lock or not. std::shared_lock g(instantiation_cache_mutex); wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); return it != wasm_instantiation_cache.end(); @@ -142,41 +144,63 @@ struct eosvmoc_tier { } #endif - const std::unique_ptr& get_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, - const uint8_t& vm_version, transaction_context& trx_context ) + const std::unique_ptr& get_instantiated_module( + const digest_type& code_hash, + const uint8_t& vm_type, + const uint8_t& vm_version, + transaction_context& trx_context) { - std::shared_lock r_lock(instantiation_cache_mutex); - wasm_cache_index::iterator it = wasm_instantiation_cache.find( - boost::make_tuple(code_hash, vm_type, vm_version) ); - if(it != wasm_instantiation_cache.end()) { - // An instantiated module's module should never be null. - assert(it->module); - return it->module; + wasm_cache_index::iterator it; + if (trx_context.control.is_write_window()) { + // When in write window (either read only threads are not enabled or + // they are not schedued to run), only main thread is processing + // transactions. No need to lock. + it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); + } else { + std::shared_lock g(instantiation_cache_mutex); + it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); } - r_lock.unlock(); - // Acquire a unique lock as we need to modify the cache - std::unique_lock rw_lock(instantiation_cache_mutex); - - // While waiting for aquiring the write lock, another thread might - // have already compiled the contract and it is ready for use. - // Check again if that's the case. - it = wasm_instantiation_cache.find(boost::make_tuple(code_hash, vm_type, vm_version) ); if (it != wasm_instantiation_cache.end()) { + // An instantiated module's module should never be null. assert(it->module); return it->module; } - // Parse the contract WASM into an instantiated module and - // cache the instantiated module. + if (trx_context.control.is_write_window()) { + return build_and_get_instantiated_module(code_hash, vm_type, vm_version, trx_context); + } else { + // Use unique lock as we need to modify the cache + std::unique_lock g(instantiation_cache_mutex); + + // While waiting for aquiring the lock, another thread might + // have already compiled the contract and it is ready for use. + // Check if that's the case. + it = wasm_instantiation_cache.find(boost::make_tuple(code_hash, vm_type, vm_version) ); + if (it != wasm_instantiation_cache.end()) { + assert(it->module); + return it->module; + } + + return build_and_get_instantiated_module(code_hash, vm_type, vm_version, trx_context); + } + } + + // Locked by the caller if required. + const std::unique_ptr& build_and_get_instantiated_module( + const digest_type& code_hash, + const uint8_t& vm_type, + const uint8_t& vm_version, + transaction_context& trx_context ) + { const code_object* codeobject = &db.get(boost::make_tuple(code_hash, vm_type, vm_version)); - it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry{ - .code_hash = code_hash, - .last_block_num_used = UINT32_MAX, - .module = nullptr, - .vm_type = vm_type, - .vm_version = vm_version - } ).first; + wasm_cache_index::iterator it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry { + .code_hash = code_hash, + .last_block_num_used = UINT32_MAX, + .module = nullptr, + .vm_type = vm_type, + .vm_version = vm_version + } ).first; auto timer_pause = fc::make_scoped_exit([&](){ trx_context.resume_billing_timer(); }); @@ -184,7 +208,6 @@ struct eosvmoc_tier { wasm_instantiation_cache.modify(it, [&](auto& c) { c.module = runtime_interface->instantiate_module(codeobject->code.data(), codeobject->code.size(), code_hash, vm_type, vm_version); }); - return it->module; } From fd7d561602c01366dcbe93cd0dd2dc7fbd27ca10 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 23 Aug 2023 12:17:20 -0400 Subject: [PATCH 06/11] bump eos-vm to the head of eos_vm_single_wasmif --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 74dd3f0c5e..7d184fb359 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 74dd3f0c5e621c4e22b543b0f1aa2539a516c46f +Subproject commit 7d184fb359683ff8653ea0ac37f4fc10df8f2fe8 From bed6fbcb87b2770cdac8c0b24620188f8186748d Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Sat, 26 Aug 2023 12:07:30 -0400 Subject: [PATCH 07/11] bump eos-vm to head of eos_vm_single_wasmif --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 7d184fb359..b79fd167ef 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 7d184fb359683ff8653ea0ac37f4fc10df8f2fe8 +Subproject commit b79fd167ef39dc147e148ad1d1604e0cb82e9dab From 828b47e961d290e86c0594ca3af4f34cd19cfbef Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Thu, 31 Aug 2023 08:55:02 -0400 Subject: [PATCH 08/11] update to use backend's share method --- libraries/chain/webassembly/runtimes/eos-vm.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index 13bbbf6801..7a7fc91bba 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -129,15 +129,16 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { - // re-initialize backend from the instantiate module of the contract - _runtime->_bkend = *_instantiated_module; - // re-initialize exec ctx with instantiate module + // initialize backend from the instantiated module of the contract + _runtime->_bkend.share(*_instantiated_module); + // set exec ctx's mod to instantiated module's mod _runtime->_exec_ctx.set_module(&(_instantiated_module->get_module())); // link exe ctx to backend _runtime->_bkend.set_context(&_runtime->_exec_ctx); - // set other per apply data + // set max_call_depth and max_pages to original values _runtime->_bkend.reset_max_call_depth(); _runtime->_bkend.reset_max_pages(); + // set wasm allocator per apply data _runtime->_bkend.set_wasm_allocator(&context.control.get_wasm_allocator()); apply_options opts; From 28aa45232621bcb59f071887a20f4c01787eb22e Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Thu, 31 Aug 2023 11:39:38 -0400 Subject: [PATCH 09/11] incorporate review comments --- .../include/eosio/chain/wasm_interface.hpp | 11 ++-- .../eosio/chain/wasm_interface_private.hpp | 54 +++++++------------ .../chain/webassembly/runtimes/eos-vm.cpp | 3 +- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index b70efe5209..72f5cd3f23 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -54,6 +54,9 @@ namespace eosio { namespace chain { #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED // initialize exec per thread void init_thread_local_data(); + + // returns true if EOS VM OC is enabled + bool is_eos_vm_oc_enabled() const; #endif //call before dtor to skip what can be minutes of dtor overhead with some runtimes; can cause leaks @@ -66,19 +69,11 @@ namespace eosio { namespace chain { void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num); //indicate the current LIB. evicts old cache entries - //, each evicted entry is provided to callback void current_lib(const uint32_t lib); //Calls apply or error on a given code void apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context); - // only called from non-main threads (read-only trx execution threads) when producer_plugin starts them - //void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, bool profile); - -#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED - bool is_eos_vm_oc_enabled() const; -#endif - //Returns true if the code is cached bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const; diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index 555e67aca2..961b6dd27e 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include using namespace fc; using namespace eosio::chain::webassembly; @@ -95,7 +95,7 @@ struct eosvmoc_tier { #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED if(eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) { - EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated"); + EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated"); eosvmoc = std::make_unique(data_dir, eosvmoc_config, d); } #endif @@ -104,9 +104,9 @@ struct eosvmoc_tier { ~wasm_interface_impl() = default; bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const { - // This method is only called from tests. No need to check if we should - // lock or not. - std::shared_lock g(instantiation_cache_mutex); + // This method is only called from tests; performance is not critical. + // No need for an additional check if we should lock or not. + std::lock_guard g(instantiation_cache_mutex); wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); return it != wasm_instantiation_cache.end(); } @@ -150,51 +150,33 @@ struct eosvmoc_tier { const uint8_t& vm_version, transaction_context& trx_context) { - wasm_cache_index::iterator it; if (trx_context.control.is_write_window()) { // When in write window (either read only threads are not enabled or // they are not schedued to run), only main thread is processing // transactions. No need to lock. - it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); + return get_or_build_instantiated_module(code_hash, vm_type, vm_version, trx_context); } else { - std::shared_lock g(instantiation_cache_mutex); - it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); - } - - if (it != wasm_instantiation_cache.end()) { - // An instantiated module's module should never be null. - assert(it->module); - return it->module; - } - - if (trx_context.control.is_write_window()) { - return build_and_get_instantiated_module(code_hash, vm_type, vm_version, trx_context); - } else { - // Use unique lock as we need to modify the cache - std::unique_lock g(instantiation_cache_mutex); - - // While waiting for aquiring the lock, another thread might - // have already compiled the contract and it is ready for use. - // Check if that's the case. - it = wasm_instantiation_cache.find(boost::make_tuple(code_hash, vm_type, vm_version) ); - if (it != wasm_instantiation_cache.end()) { - assert(it->module); - return it->module; - } - - return build_and_get_instantiated_module(code_hash, vm_type, vm_version, trx_context); + std::lock_guard g(instantiation_cache_mutex); + return get_or_build_instantiated_module(code_hash, vm_type, vm_version, trx_context); } } // Locked by the caller if required. - const std::unique_ptr& build_and_get_instantiated_module( + const std::unique_ptr& get_or_build_instantiated_module( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, transaction_context& trx_context ) { + wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) ); + if (it != wasm_instantiation_cache.end()) { + // An instantiated module's module should never be null. + assert(it->module); + return it->module; + } + const code_object* codeobject = &db.get(boost::make_tuple(code_hash, vm_type, vm_version)); - wasm_cache_index::iterator it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry { + it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry { .code_hash = code_hash, .last_block_num_used = UINT32_MAX, .module = nullptr, @@ -226,11 +208,11 @@ struct eosvmoc_tier { ordered_non_unique, member> > > wasm_cache_index; + mutable std::mutex instantiation_cache_mutex; wasm_cache_index wasm_instantiation_cache; const chainbase::database& db; const wasm_interface::vm_type wasm_runtime_time; - mutable std::shared_mutex instantiation_cache_mutex; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED std::unique_ptr eosvmoc{nullptr}; // used by all threads diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index 7a7fc91bba..345e8cd94b 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -129,7 +129,8 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface { _instantiated_module(std::move(mod)) {} void apply(apply_context& context) override { - // initialize backend from the instantiated module of the contract + // set up backend to share the compiled mod in the instantiated + // module of the contract _runtime->_bkend.share(*_instantiated_module); // set exec ctx's mod to instantiated module's mod _runtime->_exec_ctx.set_module(&(_instantiated_module->get_module())); From 494738563dce2b95534227da80f859d96bfd0aa3 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Thu, 31 Aug 2023 21:25:32 -0400 Subject: [PATCH 10/11] bump eos-vm to the latest of eos_vm_single_wasmif (prevent invalid sharing of compiled mod in backend) --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index b79fd167ef..23d897d38b 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit b79fd167ef39dc147e148ad1d1604e0cb82e9dab +Subproject commit 23d897d38b8de8db59e164d762ede935255f990e From 0eebbac98b98b2e55d01b826c52737db1ca20c00 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Thu, 7 Sep 2023 21:30:18 -0400 Subject: [PATCH 11/11] bump eos-vm to the head of main --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index 23d897d38b..b06ac3d4c1 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 23d897d38b8de8db59e164d762ede935255f990e +Subproject commit b06ac3d4c171cfd0a0f8fd74752e4eab35f60cb0