diff --git a/docs/01_nodeos/03_plugins/producer_plugin/index.md b/docs/01_nodeos/03_plugins/producer_plugin/index.md index 07aa7b1067..de4c154fe4 100644 --- a/docs/01_nodeos/03_plugins/producer_plugin/index.md +++ b/docs/01_nodeos/03_plugins/producer_plugin/index.md @@ -27,10 +27,11 @@ Config Options for eosio::producer_plugin: chain is stale. -x [ --pause-on-startup ] Start this node in a state where production is paused - --max-transaction-time arg (=499) Locally lowers the max_transaction_cpu_ - usage limit (in milliseconds) that an - input transaction is allowed to execute - before being considered invalid + --max-transaction-time arg (=499) Setting this value (in milliseconds) + will restrict the allowed transaction + execution time to a value potentially + lower than the on-chain consensus + max_transaction_cpu_usage value. --max-irreversible-block-age arg (=-1) Limits the maximum age (in seconds) of the DPOS Irreversible Block for a chain diff --git a/libraries/custom_appbase/include/eosio/chain/application.hpp b/libraries/custom_appbase/include/eosio/chain/application.hpp index 8d514b5bba..1d510a996d 100644 --- a/libraries/custom_appbase/include/eosio/chain/application.hpp +++ b/libraries/custom_appbase/include/eosio/chain/application.hpp @@ -29,6 +29,12 @@ class priority_queue_executor { // This adds to the total time that the main thread can be busy when a high priority task is waiting. static constexpr uint16_t minimum_runtime_ms = 3; + // inform how many read_threads will be calling read_only/read_exclusive queues + // Currently only used to assert if exec_queue::read_exclusive is used without any read threads + void init_read_threads(size_t num_read_threads) { + pri_queue_.init_read_threads(num_read_threads); + } + template auto post( int priority, exec_queue q, Func&& func ) { return boost::asio::post( io_serv_, pri_queue_.wrap( priority, q, --order_, std::forward(func))); @@ -92,9 +98,9 @@ class priority_queue_executor { pri_queue_.clear(); } - void set_to_read_window(uint32_t num_threads, std::function should_exit) { + void set_to_read_window(std::function should_exit) { exec_window_ = exec_window::read; - pri_queue_.enable_locking(num_threads, std::move(should_exit)); + pri_queue_.enable_locking(std::move(should_exit)); } void set_to_write_window() { diff --git a/libraries/custom_appbase/include/eosio/chain/exec_pri_queue.hpp b/libraries/custom_appbase/include/eosio/chain/exec_pri_queue.hpp index 671e6961a6..15f7664eab 100644 --- a/libraries/custom_appbase/include/eosio/chain/exec_pri_queue.hpp +++ b/libraries/custom_appbase/include/eosio/chain/exec_pri_queue.hpp @@ -19,10 +19,10 @@ enum class exec_queue { // not being executed in read-only threads. Single threaded. read_exclusive // the queue storing tasks which should only be executed // in parallel with other read_exclusive or read_only tasks in the - // read-only thread pool. Should never be executed on the main thread. - // If no read-only thread pool is available this queue grows unbounded - // as tasks will never execute. User is responsible for not queueing - // read_exclusive tasks if no read-only thread pool is available. + // read-only thread pool. Will never be executed on the main thread. + // If no read-only thread pool is available that calls one of the execute_* with + // read_exclusive then this queue grows unbounded. exec_pri_queue asserts + // if asked to queue a read_exclusive task when init'ed with 0 read-only threads. }; // Locking has to be coordinated by caller, use with care. @@ -30,16 +30,21 @@ class exec_pri_queue : public boost::asio::execution_context { public: + // inform how many read_threads will be calling read_only/read_exclusive queues + void init_read_threads(size_t num_read_threads) { + num_read_threads_ = num_read_threads; + } + void stop() { std::lock_guard g( mtx_ ); exiting_blocking_ = true; cond_.notify_all(); } - void enable_locking(uint32_t num_threads, std::function should_exit) { - assert(num_threads > 0 && num_waiting_ == 0); + void enable_locking(std::function should_exit) { + assert(num_read_threads_ > 0 && num_waiting_ == 0); lock_enabled_ = true; - max_waiting_ = num_threads; + max_waiting_ = num_read_threads_; should_exit_ = std::move(should_exit); exiting_blocking_ = false; } @@ -52,6 +57,7 @@ class exec_pri_queue : public boost::asio::execution_context // called from appbase::application_base::exec poll_one() or run_one() template void add(int priority, exec_queue q, size_t order, Function function) { + assert( num_read_threads_ > 0 || q != exec_queue::read_exclusive); prio_queue& que = priority_que(q); std::unique_ptr handler(new queued_handler(priority, order, std::move(function))); if (lock_enabled_) { @@ -290,6 +296,7 @@ class exec_pri_queue : public boost::asio::execution_context return t; } + size_t num_read_threads_ = 0; bool lock_enabled_ = false; mutable std::mutex mtx_; std::condition_variable cond_; diff --git a/libraries/custom_appbase/tests/custom_appbase_tests.cpp b/libraries/custom_appbase/tests/custom_appbase_tests.cpp index 54b4c57688..77b75c43e8 100644 --- a/libraries/custom_appbase/tests/custom_appbase_tests.cpp +++ b/libraries/custom_appbase/tests/custom_appbase_tests.cpp @@ -90,7 +90,8 @@ BOOST_AUTO_TEST_CASE( execute_from_read_only_queue ) { auto app_thread = start_app_thread(app); // set to run functions from read_only queue only - app->executor().set_to_read_window(1, [](){return false;}); + app->executor().init_read_threads(1); + app->executor().set_to_read_window([](){return false;}); // post functions std::map rslts {}; @@ -137,7 +138,8 @@ BOOST_AUTO_TEST_CASE( execute_from_empty_read_only_queue ) { auto app_thread = start_app_thread(app); // set to run functions from read_only & read_exclusive queues only - app->executor().set_to_read_window(1, [](){return false;}); + app->executor().init_read_threads(1); + app->executor().set_to_read_window([](){return false;}); // post functions std::map rslts {}; @@ -245,8 +247,9 @@ BOOST_AUTO_TEST_CASE( execute_from_read_only_and_read_write_queues ) { BOOST_AUTO_TEST_CASE( execute_from_read_only_and_read_exclusive_queues ) { appbase::scoped_app app; + app->executor().init_read_threads(3); // set to run functions from read_only & read_exclusive queues only - app->executor().set_to_read_window(3, [](){return false;}); + app->executor().set_to_read_window([](){return false;}); // post functions std::vector> rslts(16); @@ -343,7 +346,8 @@ BOOST_AUTO_TEST_CASE( execute_many_from_read_only_and_read_exclusive_queues ) { std::thread::id app_thread_id = app_thread.get_id(); // set to run functions from read_only & read_exclusive queues only - app->executor().set_to_read_window(3, [](){return false;}); + app->executor().init_read_threads(3); + app->executor().set_to_read_window([](){return false;}); // post functions constexpr size_t num_expected = 600u; diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 3d8ac08cd1..789aaaf14b 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1051,7 +1051,7 @@ void producer_plugin::set_program_options( ("enable-stale-production,e", boost::program_options::bool_switch()->notifier([this](bool e){my->_production_enabled = e;}), "Enable block production, even if the chain is stale.") ("pause-on-startup,x", boost::program_options::bool_switch()->notifier([this](bool p){my->_pause_production = p;}), "Start this node in a state where production is paused") ("max-transaction-time", bpo::value()->default_value(config::block_interval_ms-1), - "Locally lowers the max_transaction_cpu_usage limit (in milliseconds) that an input transaction is allowed to execute before being considered invalid") + "Setting this value (in milliseconds) will restrict the allowed transaction execution time to a value potentially lower than the on-chain consensus max_transaction_cpu_usage value.") ("max-irreversible-block-age", bpo::value()->default_value( -1 ), "Limits the maximum age (in seconds) of the DPOS Irreversible Block for a chain this node will produce blocks on (use negative value to indicate unlimited)") ("producer-name,p", boost::program_options::value>()->composing()->multitoken(),