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

don't start kernel level platform timer when applying blocks #1130

Merged
merged 2 commits into from
Jan 28, 2025

Conversation

spoonincode
Copy link
Member

When passing fc::time_point::max() to platform_timer::start(), platform_timer recognizes that,

void platform_timer::start(fc::time_point tp) {
assert(_state == state_t::stopped);
if(tp == fc::time_point::maximum()) {
_state = state_t::running;
return;
}

and pretends the timer is running without actually starting the timer. Then when stop() is called it would still make a request to the kernel to stop the never started timer. There is nothing logically wrong with that, it's just a wasteful syscall that isn't needed that can end up happening thousands of times a second.

My memory was that historically when applying a block, transaction_context::init() would effectively platform_timer::start(fc::time_point::max()). That would result in the condition above and it's why I wrote #1119 worded the way it is. But I can't actually find evidence of this in history so I may be misremembering it.

Instead, today, transaction_context::init() passes a time 1 year in the future when applying a block. This actually makes the situation worse than what I thought: now for every transaction while applying a block a timer is entirely needlessly started and stopped.

So this PR,

  1. Makes transaction_context::init() do platform_timer::start(fc::time_point::max()); the timer somehow needs to indicate running state for things like checktime() to work so unless wanting to touch a lot more code we can't just entirely be hands off the timer. We could also do something like a platform_timer::start_forever() instead if not liking overloading the meaning of start().
  2. The platform_timer now remembers when it was started with fc::time_point::max() and when stopped skips the kernel call.

There are a handful of ways to go about accomplishing various nuances in what this PR does. Any strong opinions for a different approach can be explored.

if(kevent64(kqueue_fd, &aTimerEvent, 1, NULL, 0, KEVENT_FLAG_IMMEDIATE, NULL) != 0)
expired = true;
_state = state_t::timed_out;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this wasn't updated completely in #1107. We should really refactor the code a bit to move the common state logic out of the platform specific impls.

if(prior_state == state_t::stopped)
return;
_state = state_t::stopped;
if(prior_state == state_t::timed_out || timer_running_forever)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another minor tweak here is to not stop the timer if it has already expired. Obviously that's a rather rare scenario, but might as well be thorough while tweaking all this.

@spoonincode spoonincode linked an issue Jan 27, 2025 that may be closed by this pull request
Comment on lines 60 to 66
if(tp == fc::time_point::maximum()) {
_state = state_t::running;
timer_running_forever = true;
return;
}
fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch();
timer_running_forever = false;
Copy link
Contributor

@greg7mdp greg7mdp Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change, just a remark. Personally I prefer initializing members once if possible, as in:

Suggested change
if(tp == fc::time_point::maximum()) {
_state = state_t::running;
timer_running_forever = true;
return;
}
fc::microseconds x = tp.time_since_epoch() - fc::time_point::now().time_since_epoch();
timer_running_forever = false;
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();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that does look good here

@spoonincode spoonincode merged commit 389741e into main Jan 28, 2025
36 checks passed
@spoonincode spoonincode deleted the no_timer_on_apply branch January 28, 2025 00:01
@ericpassmore
Copy link
Contributor

Note:start
category: Other
component: Internal
summary: More efficient system calls related to platform timer during application of blocks.
Note:end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

don't stop an already stopped platform_timer
4 participants