diff --git a/libraries/chain/include/eosio/chain/platform_timer.hpp b/libraries/chain/include/eosio/chain/platform_timer.hpp index 253c35b13d..7aeb1cfd00 100644 --- a/libraries/chain/include/eosio/chain/platform_timer.hpp +++ b/libraries/chain/include/eosio/chain/platform_timer.hpp @@ -13,13 +13,19 @@ struct platform_timer { platform_timer(); ~platform_timer(); + //start() & stop() are not thread safe to each other; i.e. do not overlap calls to start() and stop() void start(fc::time_point tp); void stop(); + //interrupt_timer() can be called from any thread void interrupt_timer(); - /* Sets a callback for when timer expires. Be aware this could might fire from a signal handling context and/or + /* Sets a callback for when timer expires. Be aware this could 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. Setting to nullptr disables any current set callback */ + result in an exception. Setting to nullptr disables any current set callback. + Also, stop() is not perfectly synchronized with the callback. It is possible for stop() to return and the + callback still execute if the timer expires and stop() is called nearly simultaneously. + However, set_expiration_callback() is synchronized with the callback. + */ void set_expiration_callback(void(*func)(void*), void* user) { bool expect_false = false; while(!atomic_compare_exchange_strong(&_callback_variables_busy, &expect_false, true)) @@ -45,6 +51,7 @@ struct platform_timer { void expire_now(); std::atomic _state = state_t::stopped; + bool timer_running_forever = false; struct impl; constexpr static size_t fwd_size = 8; diff --git a/libraries/chain/platform_timer_asio_fallback.cpp b/libraries/chain/platform_timer_asio_fallback.cpp index 96ae98bfd4..9bea8d7172 100644 --- a/libraries/chain/platform_timer_asio_fallback.cpp +++ b/libraries/chain/platform_timer_asio_fallback.cpp @@ -56,11 +56,14 @@ platform_timer::~platform_timer() { } void platform_timer::start(fc::time_point tp) { - if(tp == fc::time_point::maximum()) { + assert(_state == state_t::stopped); + timer_running_forever = tp == fc::time_point::maximum(); + if(timer_running_forever) { _state = state_t::running; return; } fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch(); + timer_running_forever = false; if(x.count() <= 0) _state = state_t::timed_out; else { @@ -89,11 +92,14 @@ void platform_timer::interrupt_timer() { } void platform_timer::stop() { - if(_state == state_t::stopped) + const state_t prior_state = _state; + if(prior_state == state_t::stopped) + return; + _state = state_t::stopped; + if(prior_state == state_t::timed_out || timer_running_forever) return; my->timer->cancel(); - _state = state_t::stopped; } }} diff --git a/libraries/chain/platform_timer_kqueue.cpp b/libraries/chain/platform_timer_kqueue.cpp index 53a2804d84..738b388d4f 100644 --- a/libraries/chain/platform_timer_kqueue.cpp +++ b/libraries/chain/platform_timer_kqueue.cpp @@ -88,20 +88,23 @@ platform_timer::~platform_timer() { } void platform_timer::start(fc::time_point tp) { - if(tp == fc::time_point::maximum()) { - expired = false; + assert(_state == state_t::stopped); + timer_running_forever = tp == fc::time_point::maximum(); + if(timer_running_forever) { + _state = state_t::running; return; } fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch(); + timer_running_forever = false; if(x.count() <= 0) - expired = true; + _state = state_t::timed_out; else { struct kevent64_s aTimerEvent; EV_SET64(&aTimerEvent, my->timerid, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_USECONDS|NOTE_CRITICAL, x.count(), (uint64_t)this, 0, 0); - expired = false; + _state = state_t::running; if(kevent64(kqueue_fd, &aTimerEvent, 1, NULL, 0, KEVENT_FLAG_IMMEDIATE, NULL) != 0) - expired = true; + _state = state_t::timed_out; } } @@ -120,7 +123,11 @@ void platform_timer::interrupt_timer() { } void platform_timer::stop() { - if(_state == state_t::stopped) + const state_t prior_state = _state; + if(prior_state == state_t::stopped) + return; + _state = state_t::stopped; + if(prior_state == state_t::timed_out || timer_running_forever) return; struct kevent64_s stop_timer_event; diff --git a/libraries/chain/platform_timer_posix.cpp b/libraries/chain/platform_timer_posix.cpp index c968ccbd60..45aa9c0201 100644 --- a/libraries/chain/platform_timer_posix.cpp +++ b/libraries/chain/platform_timer_posix.cpp @@ -56,7 +56,8 @@ platform_timer::~platform_timer() { void platform_timer::start(fc::time_point tp) { assert(_state == state_t::stopped); - if(tp == fc::time_point::maximum()) { + timer_running_forever = tp == fc::time_point::maximum(); + if(timer_running_forever) { _state = state_t::running; return; } @@ -88,11 +89,14 @@ void platform_timer::interrupt_timer() { } void platform_timer::stop() { - if(_state == state_t::stopped) + const state_t prior_state = _state; + if(prior_state == state_t::stopped) + return; + _state = state_t::stopped; + if(prior_state == state_t::timed_out || timer_running_forever) return; struct itimerspec disable = {{0, 0}, {0, 0}}; timer_settime(my->timerid, 0, &disable, NULL); - _state = state_t::stopped; } } diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 3276aef1b4..d45110c76a 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -111,7 +111,8 @@ namespace eosio::chain { init_net_usage = initial_net_usage; // set maximum to a semi-valid deadline to allow for pause math and conversion to dates for logging - if( block_deadline == fc::time_point::maximum() ) block_deadline = start + fc::hours(24*7*52); + const fc::time_point far_future_time = start + fc::days(7*52); + if( block_deadline == fc::time_point::maximum() ) block_deadline = far_future_time; const auto& cfg = control.get_global_properties().configuration; auto& rl = control.get_mutable_resource_limits_manager(); @@ -250,7 +251,10 @@ namespace eosio::chain { _deadline = block_deadline; } - transaction_timer.start( _deadline ); + if(_deadline < far_future_time) + transaction_timer.start( _deadline ); + else + transaction_timer.start( fc::time_point::maximum() ); //avoids overhead in starting the timer at all checktime(); // Fail early if deadline has already been exceeded is_initialized = true; }