diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c2f9fe2d79..ec0293df06 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; @@ -344,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); + } }); @@ -2695,8 +2698,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 +2725,17 @@ struct controller_impl { ) return wasmif; else - return *wasmif_thread_local; + 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); + } } block_state_ptr fork_db_head() const; @@ -2729,7 +2745,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 { @@ -3714,6 +3729,10 @@ 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); +} + /// 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..49ea0e72d5 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -376,6 +376,7 @@ 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); private: friend class apply_context; diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index faa4e0f260..e70f339821 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; @@ -139,7 +138,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, @@ -175,7 +173,6 @@ namespace eosio { namespace chain { member > >, - ordered_non_unique, member>, ordered_non_unique, member> > > wasm_cache_index; diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 0cbfd1a5df..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(); @@ -1210,6 +1211,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 +1257,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 +1265,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(); }