From 5b1c8dd6446d80eae91f331b417aa398bbaba157 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 4 Apr 2023 13:25:13 -0400 Subject: [PATCH 1/5] Replace thread_local with unordered_map for wasmif --- libraries/chain/controller.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c2f9fe2d79..570d62fe8c 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -252,11 +252,9 @@ 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 - // Ideally wasmif should be thread_local which must be a static. - // Unittests can create multiple controller objects (testers) at the same time, - // which overwrites the same static wasmif, is used for eosvmoc too. wasm_interface wasmif; // used by main thread and all threads for EOSVMOC - thread_local static std::unique_ptr wasmif_thread_local; // a copy for each read-only thread, used by eos-vm and eos-vm-jit + std::mutex threaded_wasmifs_mtx; + std::unordered_map> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit app_window_type app_window = app_window_type::write; typedef pair handler_key; @@ -2695,8 +2693,11 @@ struct controller_impl { wasmif.init_thread_local_data(); else #endif + { + std::lock_guard g(threaded_wasmifs_mtx); // Non-EOSVMOC needs a wasmif per thread - wasmif_thread_local = std::make_unique( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty()); + threaded_wasmifs[std::this_thread::get_id()] = std::make_unique( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty()); + } } bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); }; @@ -2719,7 +2720,7 @@ struct controller_impl { ) return wasmif; else - return *wasmif_thread_local; + return *threaded_wasmifs[std::this_thread::get_id()]; } block_state_ptr fork_db_head() const; @@ -2729,7 +2730,6 @@ thread_local platform_timer controller_impl::timer; #if defined(EOSIO_EOS_VM_RUNTIME_ENABLED) || defined(EOSIO_EOS_VM_JIT_RUNTIME_ENABLED) thread_local eosio::vm::wasm_allocator controller_impl::wasm_alloc; #endif -thread_local std::unique_ptr controller_impl::wasmif_thread_local; const resource_limits_manager& controller::get_resource_limits_manager()const { From 647dd99e44c00be1a1205d5c6925ac8bde6124eb Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 4 Apr 2023 13:35:15 -0400 Subject: [PATCH 2/5] wait until all read-only threads are started before proceeding in producer_plugin startup --- plugins/producer_plugin/producer_plugin.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 0cbfd1a5df..0402d28b2d 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1210,6 +1210,7 @@ void producer_plugin::plugin_initialize(const boost::program_options::variables_ my->_snapshot_scheduler.set_create_snapshot_fn([this](producer_plugin::next_function next){create_snapshot(next);}); } FC_LOG_AND_RETHROW() } +using namespace std::chrono_literals; void producer_plugin::plugin_startup() { try { try { @@ -1255,6 +1256,7 @@ void producer_plugin::plugin_startup() } if ( my->_ro_thread_pool_size > 0 ) { + std::atomic num_threads_started = 0; my->_ro_thread_pool.start( my->_ro_thread_pool_size, []( const fc::exception& e ) { fc_elog( _log, "Exception in read-only thread pool, exiting: ${e}", ("e", e.to_detail_string()) ); @@ -1262,8 +1264,19 @@ void producer_plugin::plugin_startup() }, [&]() { chain.init_thread_local_data(); + ++num_threads_started; }); + // This will be changed with std::latch or std::atomic<>::wait + // when C++20 is used. + auto time_slept_ms = 0; + constexpr auto max_time_slept_ms = 1000; + while ( num_threads_started.load() < my->_ro_thread_pool_size && time_slept_ms < max_time_slept_ms ) { + std::this_thread::sleep_for( 1ms ); + ++time_slept_ms; + } + EOS_ASSERT(num_threads_started.load() == my->_ro_thread_pool_size, producer_exception, "read-only threads failed to start. num_threads_started: ${n}, time_slept_ms: ${t}ms", ("n", num_threads_started.load())("t", time_slept_ms)); + my->start_write_window(); } From 9b38815c56c8b2960342c428a015b38f35abd2f8 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 4 Apr 2023 14:38:15 -0400 Subject: [PATCH 3/5] make sure app wasm interface methods are called on wasmifs on all the read-only threads --- libraries/chain/controller.cpp | 33 ++++++++++++++++++- libraries/chain/eosio_contract.cpp | 2 +- .../chain/include/eosio/chain/controller.hpp | 2 ++ libraries/chain/webassembly/cf_system.cpp | 2 +- plugins/producer_plugin/producer_plugin.cpp | 3 +- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 570d62fe8c..894c664f0a 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -342,7 +342,12 @@ struct controller_impl { set_activation_handler(); self.irreversible_block.connect([this](const block_state_ptr& bsp) { - get_wasm_interface().current_lib(bsp->block_num); + // producer_plugin has already asserted irreversible_block signal is + // called in write window + wasmif.current_lib(bsp->block_num); + for (auto& w: threaded_wasmifs) { + w.second->current_lib(bsp->block_num); + } }); @@ -2723,6 +2728,24 @@ struct controller_impl { return *threaded_wasmifs[std::this_thread::get_id()]; } + void 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); + } + } + + void wasm_interface_exit() { + // exit all running wasmifs + wasmif.exit(); + for (auto& ele: threaded_wasmifs) { + ele.second->exit(); + } + } + block_state_ptr fork_db_head() const; }; /// controller_impl @@ -3714,6 +3737,14 @@ bool controller::is_write_window() const { return my->is_write_window(); } +void controller::code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) { + return my->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); +} + +void controller::wasm_interface_exit() { + return my->wasm_interface_exit(); +} + /// Protocol feature activation handlers: template<> diff --git a/libraries/chain/eosio_contract.cpp b/libraries/chain/eosio_contract.cpp index 6845272908..7170c03f59 100644 --- a/libraries/chain/eosio_contract.cpp +++ b/libraries/chain/eosio_contract.cpp @@ -160,7 +160,7 @@ void apply_eosio_setcode(apply_context& context) { old_size = (int64_t)old_code_entry.code.size() * config::setcode_ram_bytes_multiplier; if( old_code_entry.code_ref_count == 1 ) { db.remove(old_code_entry); - context.control.get_wasm_interface().code_block_num_last_used(account.code_hash, account.vm_type, account.vm_version, context.control.head_block_num() + 1); + context.control.code_block_num_last_used(account.code_hash, account.vm_type, account.vm_version, context.control.head_block_num() + 1); } else { db.modify(old_code_entry, [](code_object& o) { --o.code_ref_count; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 799d0e4212..2fb7c45238 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -376,6 +376,8 @@ namespace eosio { namespace chain { void set_to_write_window(); void set_to_read_window(); bool is_write_window() const; + void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num); + void wasm_interface_exit(); private: friend class apply_context; diff --git a/libraries/chain/webassembly/cf_system.cpp b/libraries/chain/webassembly/cf_system.cpp index 32afe93612..2af4bb5648 100644 --- a/libraries/chain/webassembly/cf_system.cpp +++ b/libraries/chain/webassembly/cf_system.cpp @@ -46,6 +46,6 @@ namespace eosio { namespace chain { namespace webassembly { } void interface::eosio_exit( int32_t code ) const { - context.control.get_wasm_interface().exit(); + context.control.wasm_interface_exit(); } }}} // ns eosio::chain::webassembly diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 0402d28b2d..51bc732d78 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -514,8 +514,9 @@ class producer_plugin_impl : public std::enable_shared_from_thistimestamp.to_time_point(); const chain::controller& chain = chain_plug->chain(); + EOS_ASSERT(chain.is_write_window(), producer_exception, "write window is expected for on_irreversible_block signal"); + _irreversible_block_time = lib->timestamp.to_time_point(); // promote any pending snapshots auto& snapshots_by_height = _pending_snapshot_index.get(); From 1f46ff65ea57e72479c8d4ef5490bf1b146de9f2 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 4 Apr 2023 14:42:07 -0400 Subject: [PATCH 4/5] remove first_block_num_used from wasm_instantiation_cache --- libraries/chain/include/eosio/chain/wasm_interface_private.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index ac5687b882..a56ccc259e 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -37,7 +37,6 @@ namespace eosio { namespace chain { struct wasm_interface_impl { struct wasm_cache_entry { digest_type code_hash; - uint32_t first_block_num_used; uint32_t last_block_num_used; std::unique_ptr module; uint8_t vm_type = 0; @@ -157,7 +156,6 @@ namespace eosio { namespace chain { it = wasm_instantiation_cache.emplace( wasm_interface_impl::wasm_cache_entry{ .code_hash = code_hash, - .first_block_num_used = codeobject->first_block_used, .last_block_num_used = UINT32_MAX, .module = nullptr, .vm_type = vm_type, @@ -209,7 +207,6 @@ namespace eosio { namespace chain { member > >, - ordered_non_unique, member>, ordered_non_unique, member> > > wasm_cache_index; From 6a97c7bb16afce2d21482670342025b9c842c60c Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 4 Apr 2023 17:56:09 -0400 Subject: [PATCH 5/5] revert the change for get_wasm_interface().exit() --- libraries/chain/controller.cpp | 12 ------------ libraries/chain/include/eosio/chain/controller.hpp | 1 - libraries/chain/webassembly/cf_system.cpp | 2 +- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 894c664f0a..ec0293df06 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2738,14 +2738,6 @@ struct controller_impl { } } - void wasm_interface_exit() { - // exit all running wasmifs - wasmif.exit(); - for (auto& ele: threaded_wasmifs) { - ele.second->exit(); - } - } - block_state_ptr fork_db_head() const; }; /// controller_impl @@ -3741,10 +3733,6 @@ void controller::code_block_num_last_used(const digest_type& code_hash, uint8_t return my->code_block_num_last_used(code_hash, vm_type, vm_version, block_num); } -void controller::wasm_interface_exit() { - return my->wasm_interface_exit(); -} - /// Protocol feature activation handlers: template<> diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 2fb7c45238..49ea0e72d5 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -377,7 +377,6 @@ namespace eosio { namespace chain { void set_to_read_window(); bool is_write_window() const; void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num); - void wasm_interface_exit(); private: friend class apply_context; diff --git a/libraries/chain/webassembly/cf_system.cpp b/libraries/chain/webassembly/cf_system.cpp index 2af4bb5648..32afe93612 100644 --- a/libraries/chain/webassembly/cf_system.cpp +++ b/libraries/chain/webassembly/cf_system.cpp @@ -46,6 +46,6 @@ namespace eosio { namespace chain { namespace webassembly { } void interface::eosio_exit( int32_t code ) const { - context.control.wasm_interface_exit(); + context.control.get_wasm_interface().exit(); } }}} // ns eosio::chain::webassembly