diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 226a68fe4c..1c44610637 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -4533,7 +4533,7 @@ struct controller_impl { // validated. if (applying_block) { ilog("Interrupting apply block"); - main_thread_timer.expire_now(); + main_thread_timer.interrupt_timer(); } } diff --git a/libraries/chain/include/eosio/chain/platform_timer.hpp b/libraries/chain/include/eosio/chain/platform_timer.hpp index 72fb8d1fef..88aed7fa97 100644 --- a/libraries/chain/include/eosio/chain/platform_timer.hpp +++ b/libraries/chain/include/eosio/chain/platform_timer.hpp @@ -7,8 +7,6 @@ #include -#include - namespace eosio { namespace chain { struct platform_timer { @@ -17,7 +15,8 @@ struct platform_timer { void start(fc::time_point tp); void stop(); - void expire_now(); + void interrupt_timer(); + void _expire_now(); // called by internal timer /* Sets a callback for when timer expires. Be aware this could might fire from a signal handling context and/or on any particular thread. Only a single callback can be registered at once; trying to register more will @@ -35,9 +34,18 @@ struct platform_timer { _expiration_callback_data = user; } - std::atomic_bool expired = true; + enum class state_t { + running, + timed_out, + interrupted, + stopped + }; + state_t timer_state() const { return _state; } private: + std::atomic _state = state_t::stopped; + + struct impl; constexpr static size_t fwd_size = 8; fc::fwd my; diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index c8fa1ed0bc..44f3c26dbd 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -20,12 +20,13 @@ namespace eosio::chain { void start(fc::time_point tp); void stop(); + platform_timer::state_t timer_state() const { return _timer.timer_state(); } + /* Sets a callback for when timer expires. Be aware this could might fire from a signal handling context and/or on any particular thread. Only a single callback can be registered at once; trying to register more will result in an exception. Use nullptr to disable a previously set callback. */ void set_expiration_callback(void(*func)(void*), void* user); - std::atomic_bool& expired; private: platform_timer& _timer; diff --git a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp index f038f2e30f..8b608a1b22 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface_private.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface_private.hpp @@ -124,8 +124,7 @@ struct eosvmoc_tier { return; if (executing_code_hash.load() == code_id) { ilog("EOS VM OC tier up interrupting ${id}", ("id", code_id)); - eos_vm_oc_compile_interrupt = true; - main_thread_timer.expire_now(); + main_thread_timer.interrupt_timer(); } }); } @@ -172,7 +171,6 @@ struct eosvmoc_tier { const bool allow_oc_interrupt = attempt_tierup && context.is_applying_block() && context.trx_context.has_undo(); auto ex = fc::make_scoped_exit([&]() { if (allow_oc_interrupt) { - eos_vm_oc_compile_interrupt = false; executing_code_hash.store({}); // indicate no longer executing } }); @@ -181,7 +179,7 @@ struct eosvmoc_tier { try { get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context)->apply(context); } catch (const interrupt_exception& e) { - if (allow_oc_interrupt && eos_vm_oc_compile_interrupt) { + if (allow_oc_interrupt && main_thread_timer.timer_state() == platform_timer::state_t::interrupted) { ++eos_vm_oc_compile_interrupt_count; dlog("EOS VM OC compile complete interrupt of ${r} <= ${a}::${act} code ${h}, interrupt #${c}", ("r", context.get_receiver())("a", context.get_action().account) @@ -310,7 +308,6 @@ struct eosvmoc_tier { const wasm_interface::vm_type wasm_runtime_time; const wasm_interface::vm_oc_enable eosvmoc_tierup; large_atomic executing_code_hash{}; - std::atomic eos_vm_oc_compile_interrupt{false}; uint32_t eos_vm_oc_compile_interrupt_count{0}; // for testing #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED diff --git a/libraries/chain/platform_timer_accuracy.cpp b/libraries/chain/platform_timer_accuracy.cpp index 94a69c8944..8634bd4f17 100644 --- a/libraries/chain/platform_timer_accuracy.cpp +++ b/libraries/chain/platform_timer_accuracy.cpp @@ -39,9 +39,10 @@ void compute_and_print_timer_accuracy(platform_timer& timer) { for(unsigned int i = 0; i < loops; ++i) { auto start = std::chrono::high_resolution_clock::now(); timer.start(fc::time_point(fc::time_point::now().time_since_epoch() + fc::microseconds(interval))); - while(!timer.expired) {} + while(timer.timer_state() == platform_timer::state_t::running) {} auto end = std::chrono::high_resolution_clock::now(); int timer_slop = std::chrono::duration_cast(end-start).count() - interval; + timer.stop(); //since more samples are run for the shorter expirations, weigh the longer expirations accordingly. This //helps to make a few results more fair. Two such examples: AWS c4&i5 xen instances being rather stable diff --git a/libraries/chain/platform_timer_posix.cpp b/libraries/chain/platform_timer_posix.cpp index 4388fa18b9..6ccbe46571 100644 --- a/libraries/chain/platform_timer_posix.cpp +++ b/libraries/chain/platform_timer_posix.cpp @@ -21,7 +21,7 @@ struct platform_timer::impl { static void sig_handler(int, siginfo_t* si, void*) { platform_timer* self = (platform_timer*)si->si_value.sival_ptr; - self->expire_now(); + self->_expire_now(); } }; @@ -56,35 +56,42 @@ platform_timer::~platform_timer() { void platform_timer::start(fc::time_point tp) { if(tp == fc::time_point::maximum()) { - expired = false; + _state = state_t::running; return; } fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch(); if(x.count() <= 0) - expired = true; + _state = state_t::timed_out; else { time_t secs = x.count() / 1000000; long nsec = (x.count() - (secs*1000000)) * 1000; struct itimerspec enable = {{0, 0}, {secs, nsec}}; - expired = false; + _state = state_t::running; if(timer_settime(my->timerid, 0, &enable, NULL) != 0) - expired = true; + _state = state_t::timed_out; } } -void platform_timer::expire_now() { - bool expected = false; - if (expired.compare_exchange_strong(expected, true)) { +void platform_timer::_expire_now() { + state_t expected = state_t::running; + if (_state.compare_exchange_strong(expected, state_t::timed_out)) { + call_expiration_callback(); + } +} + +void platform_timer::interrupt_timer() { + state_t expected = state_t::running; + if (_state.compare_exchange_strong(expected, state_t::interrupted)) { call_expiration_callback(); } } void platform_timer::stop() { - if(expired) + if(_state == state_t::stopped) return; struct itimerspec disable = {{0, 0}, {0, 0}}; timer_settime(my->timerid, 0, &disable, NULL); - expired = true; + _state = state_t::stopped; } } diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 33ad785a5d..c95afa17c8 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -14,8 +14,7 @@ namespace eosio::chain { transaction_checktime_timer::transaction_checktime_timer(platform_timer& timer) - : expired(timer.expired), _timer(timer) { - expired = 0; + : _timer(timer) { } void transaction_checktime_timer::start(fc::time_point tp) { @@ -489,11 +488,12 @@ namespace eosio::chain { } void transaction_context::checktime()const { - if(BOOST_LIKELY(transaction_timer.expired == false)) + platform_timer::state_t expired = transaction_timer.timer_state(); + if(BOOST_LIKELY(expired == platform_timer::state_t::running)) return; auto now = fc::time_point::now(); - if (explicit_billed_cpu_time && block_deadline > now) { + if (expired == platform_timer::state_t::interrupted) { EOS_THROW( interrupt_exception, "interrupt signaled, ran ${bt}us, start ${s}", ("bt", now - pseudo_start)("s", start) ); } else if( explicit_billed_cpu_time || deadline_exception_code == deadline_exception::code_value ) { diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index 5325382049..253dabdb49 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -29,7 +29,8 @@ namespace { guard(transaction_checktime_timer& timer, F&& func) : _timer(timer), _func(std::forward(func)) { _timer.set_expiration_callback(&callback, this); - if(_timer.expired) { + platform_timer::state_t expired = _timer.timer_state(); + if(expired == platform_timer::state_t::timed_out || expired == platform_timer::state_t::interrupted) { _func(); // it's harmless if _func is invoked twice } }