Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pause timer calculation. #130

Merged
merged 10 commits into from
May 10, 2022
47 changes: 30 additions & 17 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,8 @@ struct controller_impl {
}

transaction_trace_ptr apply_onerror( const generated_transaction& gtrx,
fc::time_point deadline,
fc::time_point block_deadline,
fc::microseconds max_transaction_time,
fc::time_point start,
uint32_t& cpu_time_to_bill_us, // only set on failure
uint32_t billed_cpu_time_us,
Expand Down Expand Up @@ -1190,7 +1191,8 @@ struct controller_impl {
const packed_transaction trx( std::move( etrx ) );
transaction_context trx_context( self, trx, std::move(trx_timer), start );

trx_context.deadline = deadline;
trx_context.block_deadline = block_deadline;
trx_context.max_transaction_time_subjective = max_transaction_time;
trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trx_context.enforce_whiteblacklist = enforce_whiteblacklist;
Expand Down Expand Up @@ -1274,14 +1276,19 @@ struct controller_impl {
|| failure_is_subjective(e);
}

transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline, uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false ) {
transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& trxid,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false )
{
const auto& idx = db.get_index<generated_transaction_multi_index,by_trx_id>();
auto itr = idx.find( trxid );
EOS_ASSERT( itr != idx.end(), unknown_transaction_exception, "unknown transaction" );
return push_scheduled_transaction( *itr, deadline, billed_cpu_time_us, explicit_billed_cpu_time );
return push_scheduled_transaction( *itr, block_deadline, max_transaction_time, billed_cpu_time_us, explicit_billed_cpu_time );
}

transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto, fc::time_point deadline, uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false )
transaction_trace_ptr push_scheduled_transaction( const generated_transaction_object& gto,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false )
{ try {

const bool validating = !self.is_producing_block();
Expand Down Expand Up @@ -1340,7 +1347,8 @@ struct controller_impl {
transaction_checktime_timer trx_timer(timer);
transaction_context trx_context( self, *trx->packed_trx(), std::move(trx_timer) );
trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource
trx_context.deadline = deadline;
trx_context.block_deadline = block_deadline;
trx_context.max_transaction_time_subjective = max_transaction_time;
trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trx_context.enforce_whiteblacklist = gtrx.sender.empty() ? true : !sender_avoids_whitelist_blacklist_enforcement( gtrx.sender );
Expand Down Expand Up @@ -1419,7 +1427,7 @@ struct controller_impl {
if( gtrx.sender != account_name() && !(validating ? failure_is_subjective(*trace->except) : scheduled_failure_is_subjective(*trace->except))) {
// Attempt error handling for the generated transaction.

auto error_trace = apply_onerror( gtrx, deadline, trx_context.pseudo_start,
auto error_trace = apply_onerror( gtrx, block_deadline, max_transaction_time, trx_context.pseudo_start,
cpu_time_to_bill_us, billed_cpu_time_us, explicit_billed_cpu_time,
trx_context.enforce_whiteblacklist );
error_trace->failed_dtrx_trace = trace;
Expand Down Expand Up @@ -1506,12 +1514,13 @@ struct controller_impl {
* the pending block.
*/
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
fc::time_point deadline,
fc::time_point block_deadline,
fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us,
bool explicit_billed_cpu_time,
uint32_t subjective_cpu_bill_us )
{
EOS_ASSERT(deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized");
EOS_ASSERT(block_deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized");

transaction_trace_ptr trace;
try {
Expand All @@ -1535,7 +1544,8 @@ struct controller_impl {
if ((bool)subjective_cpu_leeway && pending->_block_status == controller::block_status::incomplete) {
trx_context.leeway = *subjective_cpu_leeway;
}
trx_context.deadline = deadline;
trx_context.block_deadline = block_deadline;
trx_context.max_transaction_time_subjective = max_transaction_time;
trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
trx_context.subjective_cpu_bill_us = subjective_cpu_bill_us;
Expand Down Expand Up @@ -1781,7 +1791,8 @@ struct controller_impl {
in_trx_requiring_checks = old_value;
});
in_trx_requiring_checks = true;
auto trace = push_transaction( onbtrx, fc::time_point::maximum(), self.get_global_properties().configuration.min_transaction_cpu_usage, true, 0 );
auto trace = push_transaction( onbtrx, fc::time_point::maximum(), fc::microseconds::maximum(),
self.get_global_properties().configuration.min_transaction_cpu_usage, true, 0 );
if( trace->except ) {
wlog("onblock ${block_num} is REJECTING: ${entire_trace}",("block_num", head->block_num + 1)("entire_trace", trace));
}
Expand Down Expand Up @@ -2050,10 +2061,10 @@ struct controller_impl {
: ( !!std::get<0>( trx_metas.at( packed_idx ) ) ?
std::get<0>( trx_metas.at( packed_idx ) )
: std::get<1>( trx_metas.at( packed_idx ) ).get() ) );
trace = push_transaction( trx_meta, fc::time_point::maximum(), receipt.cpu_usage_us, true, 0 );
trace = push_transaction( trx_meta, fc::time_point::maximum(), fc::microseconds::maximum(), receipt.cpu_usage_us, true, 0 );
++packed_idx;
} else if( std::holds_alternative<transaction_id_type>(receipt.trx) ) {
trace = push_scheduled_transaction( std::get<transaction_id_type>(receipt.trx), fc::time_point::maximum(), receipt.cpu_usage_us, true );
trace = push_scheduled_transaction( std::get<transaction_id_type>(receipt.trx), fc::time_point::maximum(), fc::microseconds::maximum(), receipt.cpu_usage_us, true );
} else {
EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" );
}
Expand Down Expand Up @@ -2882,21 +2893,23 @@ void controller::push_block( std::future<block_state_ptr>& block_state_future,
my->push_block( block_state_future, forked_branch_cb, trx_lookup );
}

transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline,
transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time,
uint32_t subjective_cpu_bill_us ) {
validate_db_available_size();
EOS_ASSERT( get_read_mode() != db_read_mode::IRREVERSIBLE, transaction_type_exception, "push transaction not allowed in irreversible mode" );
EOS_ASSERT( trx && !trx->implicit && !trx->scheduled, transaction_type_exception, "Implicit/Scheduled transaction not allowed" );
return my->push_transaction(trx, deadline, billed_cpu_time_us, explicit_billed_cpu_time, subjective_cpu_bill_us );
return my->push_transaction(trx, block_deadline, max_transaction_time, billed_cpu_time_us, explicit_billed_cpu_time, subjective_cpu_bill_us );
}

transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline,
transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time )
{
EOS_ASSERT( get_read_mode() != db_read_mode::IRREVERSIBLE, transaction_type_exception, "push scheduled transaction not allowed in irreversible mode" );
validate_db_available_size();
return my->push_scheduled_transaction( trxid, deadline, billed_cpu_time_us, explicit_billed_cpu_time );
return my->push_scheduled_transaction( trxid, block_deadline, max_transaction_time, billed_cpu_time_us, explicit_billed_cpu_time );
}

const flat_set<account_name>& controller::get_actor_whitelist() const {
Expand Down
6 changes: 4 additions & 2 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,17 @@ namespace eosio { namespace chain {
/**
*
*/
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline,
transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
fc::time_point deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time,
uint32_t subjective_cpu_bill_us );

/**
* Attempt to execute a specific transaction in our deferred trx database
*
*/
transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& scheduled, fc::time_point deadline,
transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& scheduled,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time );

block_state_ptr finalize_block( const signer_callback_type& signer_callback );
Expand Down
7 changes: 4 additions & 3 deletions libraries/chain/include/eosio/chain/transaction_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ namespace eosio { namespace chain {
bool apply_context_free = true;
bool enforce_whiteblacklist = true;

fc::time_point deadline = fc::time_point::maximum();
fc::time_point block_deadline = fc::time_point::maximum();
fc::microseconds leeway = fc::microseconds( config::default_subjective_cpu_leeway_us );
int64_t billed_cpu_time_us = 0;
uint32_t subjective_cpu_bill_us = 0;
Expand All @@ -161,14 +161,15 @@ namespace eosio { namespace chain {

bool cpu_limit_due_to_greylist = false;

fc::microseconds max_transaction_time_subjective;
fc::time_point paused_time;
swatanabe marked this conversation as resolved.
Show resolved Hide resolved
fc::microseconds initial_objective_duration_limit;
fc::microseconds objective_duration_limit;
fc::time_point _deadline = fc::time_point::maximum();
fc::time_point _deadline = fc::time_point::maximum(); // calculated deadline
int64_t deadline_exception_code = block_cpu_usage_exceeded::code_value;
int64_t billing_timer_exception_code = block_cpu_usage_exceeded::code_value;
fc::time_point pseudo_start;
fc::microseconds billed_time;
fc::microseconds billing_timer_duration_limit;
};

} }
2 changes: 1 addition & 1 deletion libraries/chain/platform_timer_asio_fallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void platform_timer::start(fc::time_point tp) {
p.get_future().get();
#endif
expired = 0;
my->timer->expires_after(std::chrono::microseconds((int)x.count()));
my->timer->expires_after(std::chrono::microseconds(x.count()));
my->timer->async_wait([this](const boost::system::error_code& ec) {
if(ec)
return;
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/platform_timer_macos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void platform_timer::start(fc::time_point tp) {
expired = 1;
else {
struct kevent64_s aTimerEvent;
EV_SET64(&aTimerEvent, my->timerid, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_USECONDS|NOTE_CRITICAL, (int)x.count(), (uint64_t)this, 0, 0);
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 = 0;
if(kevent64(kqueue_fd, &aTimerEvent, 1, NULL, 0, KEVENT_FLAG_IMMEDIATE, NULL) != 0)
Expand Down
4 changes: 3 additions & 1 deletion libraries/chain/platform_timer_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ void platform_timer::start(fc::time_point tp) {
if(x.count() <= 0)
expired = 1;
else {
struct itimerspec enable = {{0, 0}, {0, (int)x.count()*1000}};
time_t secs = x.count() / 1000000;
long nsec = (x.count() - (secs*1000000)) * 1000;
struct itimerspec enable = {{0, 0}, {secs, nsec}};
expired = 0;
if(timer_settime(my->timerid, 0, &enable, NULL) != 0)
expired = 1;
Expand Down
52 changes: 33 additions & 19 deletions libraries/chain/transaction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ namespace eosio { namespace chain {
void transaction_context::init(uint64_t initial_net_usage)
{
EOS_ASSERT( !is_initialized, transaction_exception, "cannot initialize twice" );
const static int64_t large_number_no_overflow = std::numeric_limits<int64_t>::max()/2;

// 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 auto& cfg = control.get_global_properties().configuration;
auto& rl = control.get_mutable_resource_limits_manager();
Expand Down Expand Up @@ -176,11 +178,21 @@ namespace eosio { namespace chain {
billing_timer_exception_code = leeway_deadline_exception::code_value;
}

billing_timer_duration_limit = _deadline - start;
// Possibly limit deadline to subjective max_transaction_time
if( max_transaction_time_subjective != fc::microseconds::maximum() && (start + max_transaction_time_subjective) <= _deadline ) {
_deadline = start + max_transaction_time_subjective;
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
}

// Possibly limit deadline to caller provided wall clock block deadline
if( block_deadline < _deadline ) {
_deadline = block_deadline;
billing_timer_exception_code = deadline_exception::code_value;
}

// Check if deadline is limited by caller-set deadline (only change deadline if billed_cpu_time_us is not set)
if( explicit_billed_cpu_time || deadline < _deadline ) {
_deadline = deadline;
// Explicit billed_cpu_time_us should be used, block_deadline will be maximum unless in test code
if( explicit_billed_cpu_time ) {
swatanabe marked this conversation as resolved.
Show resolved Hide resolved
_deadline = block_deadline;
deadline_exception_code = deadline_exception::code_value;
} else {
deadline_exception_code = billing_timer_exception_code;
Expand All @@ -200,12 +212,12 @@ namespace eosio { namespace chain {
if( initial_net_usage > 0 )
add_net_usage( initial_net_usage ); // Fail early if current net usage is already greater than the calculated limit

checktime(); // Fail early if deadline has already been exceeded

if(control.skip_trx_checks())
transaction_timer.start(fc::time_point::maximum());
else
transaction_timer.start(_deadline);
if(control.skip_trx_checks()) {
transaction_timer.start( fc::time_point::maximum() );
} else {
transaction_timer.start( _deadline );
checktime(); // Fail early if deadline has already been exceeded
}

is_initialized = true;
}
Expand Down Expand Up @@ -426,9 +438,8 @@ namespace eosio { namespace chain {
void transaction_context::pause_billing_timer() {
if( explicit_billed_cpu_time || pseudo_start == fc::time_point() ) return; // either irrelevant or already paused

auto now = fc::time_point::now();
billed_time = now - pseudo_start;
deadline_exception_code = deadline_exception::code_value; // Other timeout exceptions cannot be thrown while billable timer is paused.
paused_time = fc::time_point::now();
billed_time = paused_time - pseudo_start;
pseudo_start = fc::time_point();
transaction_timer.stop();
}
Expand All @@ -437,14 +448,17 @@ namespace eosio { namespace chain {
if( explicit_billed_cpu_time || pseudo_start != fc::time_point() ) return; // either irrelevant or already running

auto now = fc::time_point::now();
auto paused = now - paused_time;

pseudo_start = now - billed_time;
if( (pseudo_start + billing_timer_duration_limit) <= deadline ) {
_deadline = pseudo_start + billing_timer_duration_limit;
deadline_exception_code = billing_timer_exception_code;
} else {
_deadline = deadline;
_deadline += paused;

// do not allow to go past block wall clock deadline
if( block_deadline < _deadline ) {
deadline_exception_code = deadline_exception::code_value;
_deadline = block_deadline;
}

transaction_timer.start(_deadline);
}

Expand Down
Loading