Repository navigation
Do not read env.options from the timer thread - #1027
Conversation
|
If this is the right approach, happy to review and merge. However, would it not be easier to load the required fields from |
|
@MichaelRawson Thanks, that would be simpler. The catch is that |
|
Please note that |
|
@quickbeam123 Agreed. I will investigate adapting |
|
Sorry to start thinking more deeply about this only now, but what is it exactly we are trying to fix with this change? What bad will happen if Timer reads I mean, we know this is a grey (if not black) area, but the current timer solution works for our purposes: it allows us to stop at any point in the main code (ideally also print some statistics) without having to spread bailout checks across every place in the code base that can in principle run for more than 0.000s, which I think would be the only correct way. The bottom line: maybe let's not waste energy on fixing somethings that not pragmatically broken? |
|
@quickbeam123 Current master still allows the timer to read freed The PR stops the timer before global destruction and removes these shutdown accesses while retaining asynchronous timeout enforcement, without adding bailout checks throughout the prover. The demonstrated benefit is eliminating the reported memory errors; preventing abnormal shutdown is a further benefit, but I have no evidence of incorrect solver results caused by this issue. |
Could we briefly discuss the exact execution there? Normally, I would assume, we call |
|
@quickbeam123 You're right that once the main thread has acquired The reproduction in #979 is a successful solve of So the sequence is: the main thread disables enforcement, cleanup deletes The controlled timeout-during-cleanup test does not call |
|
Thank you, @shalashaska117 , for pointing me to #979, which I should have read first, sorry. But |
|
I'm fine with deleting I see Martin's point and it would be very hard for a compiler to exploit this into something suprising, but let's not do illegal things where we can avoid it. The thing I don't understand about this patch is that We don't want to acquire [[noreturn]] void timer_thread()
{
unsigned timeLimit = env.options->timeLimitInDeciseconds();
unsigned instrLimit = env.options->instructionLimit();
unsigned simulatedInstrLimit = env.options->simulatedInstructionLimit();
while(true) {
if(timeLimit && Timer::elapsedDeciseconds() >= timeLimit) {
limitReached(TIME_LIMIT);
}
#if VAMPIRE_PERF_EXISTS
if(instrLimit || simulatedInstrLimit) {
Timer::updateInstructionCount();
if (instrLimit && LAST_INSTRUCTION_COUNT_READ >= MEGA*(long long)instrLimit) {
limitReached(INSTRUCTION_LIMIT);
}
}
#endif
// doesn't matter if we 'sleep in' a bit, we get the real time from the system clock
std::this_thread::sleep_for(TICK_INTERVAL);
}
ASSERTION_VIOLATION
}so that all the options get loaded into the thread's locals. With a suitable comment, of course. What did I miss? (other than |
The detached timer thread read the instruction limits from env.options on every tick, so it could read freed Options during global cleanup. reinitialise() now passes the time and simulated instruction limits to the thread. The instruction limit lives in an atomic in Timer, because main sets it after parsing under --parsing_does_not_count; with that flag the timer starts without one. An unlimited budget now stays unlimited after parsing; before, it became the parsing cost. Closes vprover#979.
b8ff18b to
f3bd1e8
Compare
|
@MichaelRawson You didn't miss anything on the #979 path: your version fixes it, and I've reduced the PR to it. Two details:
Deleting Not covered: modes that never call |
MichaelRawson
left a comment
There was a problem hiding this comment.
This is really nice, thanks for the update! This also handles the parsing_does_not_count logic nicer than I thought it could be done, bravo.
Closes #979.
The detached timer thread read the instruction limits from
env.optionson every tick, so it could read freedOptionsduring global cleanup.reinitialise()now passes the time and simulated instruction limits to the thread. The instruction limit lives in an atomic inTimer, becausemainsets it after parsing under--parsing_does_not_count; with that flag the timer starts without one. An unlimited budget now stays unlimited after parsing; before, it became the parsing cost and the run stopped right after parsing.Checks:
env.optionsafter the timer starts, fails on master and passes here.CHECK_LEAKS=ONand under ASan/UBSan. Release sanity passes. 14 CLI cases pass in Debug, Release and ASan/UBSan.-i 5only after parsing and keep an unlimited budget unlimited. There is no hardware counter here, so real instruction counting is untested.