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: Cancellation tokens to cancel and restart checkers #497

Merged
merged 22 commits into from
Feb 14, 2022

Conversation

keyboardDrummer
Copy link
Collaborator

@keyboardDrummer keyboardDrummer commented Feb 10, 2022

This PR enables Boogie's execution engine to effectively kill verification on demand, and recycle killed checkers/SMTLib processes

Changes

  • Enable cancellation while waiting for input from the solver
  • Only recycle a Checker if its associated prover is responsive
  • Return a PipelineOutcome.Cancelled outcome if a request was cancelled.

Issues it fixes

This PR should be able to fix the following bugs:
dafny-lang/ide-vscode#119
dafny-lang/ide-vscode#114
dafny-lang/ide-vscode#96

How it has been tested

  1. Add a unit test InferAndVerifyCanBeCancelledWhileWaitingForProver

  2. Dafny tests
    fix: "Stuck on Verifying..." dafny-lang/dafny#1771
    It takes a program which is hard to verify, and two changes that makes it easy to verify and then a parse error.
    Without this PR, the test fails and loops indefinitely on the first program.
    With this PR, the tests succeeds and all the changes are taken into account.

@MikaelMayer MikaelMayer changed the title Cancellation token2 fix: Cancellation tokens to cancel checkers Feb 10, 2022
@MikaelMayer MikaelMayer changed the title fix: Cancellation tokens to cancel checkers fix: Cancellation tokens to cancel and restart checkers Feb 10, 2022
@keyboardDrummer keyboardDrummer marked this pull request as ready for review February 11, 2022 15:19
Copy link
Member

@cpitclaudel cpitclaudel left a comment

Choose a reason for hiding this comment

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

I don't have enough context to do a sufficient review, but this looks very nice. Can you say why you can ignore the CancelledException early on?

lock (this) {
if (outputReceivers.TryDequeue(out var source)) {
source.SetResult(null);
}
Copy link
Member

Choose a reason for hiding this comment

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

Could there be more than one item in the queue here? Is it safe to remove just one?

Copy link
Collaborator Author

@keyboardDrummer keyboardDrummer Feb 11, 2022

Choose a reason for hiding this comment

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

There's a while so it should remove all of them. I guess you reviewed an older version with just an if :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

There cannot be two things waiting for it in our current code be because we always prefix the calls to getProverResponse with await. However, as a safeguard for future problems that are unlikely to happend, we added the comment that this class is not thread-safe, meaning it should be also used only with one thread (which is what happens, it is owned by a Checker that runs the prover on only one thread.)

Copy link
Member

Choose a reason for hiding this comment

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

I guess you reviewed an older version with just an if :)

!! Precisely right :)

@@ -632,12 +644,12 @@ private void RecoverIfProverCrashedAfterReset()

public override void FullReset(VCExpressionGenerator gen)
{
if (options.Solver == SolverKind.Z3)
if (options.Solver == SolverKind.Z3 || options.Solver == SolverKind.NoOpWithZ3Options)
Copy link
Member

Choose a reason for hiding this comment

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

👍

}
SendThisVC("(push 1)");
var result = action();
popLater = true;
Copy link
Member

Choose a reason for hiding this comment

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

This function always set poplater to true, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes

Copy link
Member

Choose a reason for hiding this comment

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

I'm not a fan of this API and its out popLater parameter, but I'm fine with sticking with what the code used to do.

hasOutput = true;
proverRunTime = DateTime.UtcNow - proverStart;
switch (outcome)
{
Copy link
Member

Choose a reason for hiding this comment

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

This would be a good use case for a dictionary

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can't find the code on which you made this comment, so I don't know how to reply.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's the switch that converts the outcome to a string..

Copy link
Member

Choose a reason for hiding this comment

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

The code is the switch on outcome in WaitForOutput. But since all that this PR does to it is reindent it, it's probably not worth changing it.

Copy link
Collaborator

@atomb atomb left a comment

Choose a reason for hiding this comment

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

I think this looks great, and seems to simplify code and add functionality at the same time. I'd just like to confirm that issue #487 (fixed in PR #488) doesn't reappear.

@@ -39,8 +39,6 @@ public ConditionGenerationContracts(Program p, CheckerPool checkerPool)
[ContractClass(typeof(ConditionGenerationContracts))]
public abstract class ConditionGeneration : IDisposable
{
protected internal object CheckerCommonState;

Copy link
Collaborator

Choose a reason for hiding this comment

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

🎉

public virtual string[] CalculatePath(int controlFlowConstant)
{
throw new System.NotImplementedException();
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, nice! This doesn't need to be here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not for https://github.com/boogie-org/boogie, but possibly for a consumer of https://github.com/boogie-org/boogie, although I don't see why you would want to override this. For APIs that are [unexpectedly] used outside of Boogie, we should document the use-case and mark the API with an annotation, like [PublicAPI], otherwise it'll be impossible to make changes to Boogie without fear of breaking a consumer.

split.ReleaseChecker();
throw;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you tested this against a badly-behaved prover? I'm worried that this could undo the fix from #488.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since we don't catch the exception, we also don't have to rethrow it since it's thrown up automatically. Where does your concern come from?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, right, the change from catch to finally should avoid it. And there is a test that would be failing if this change did undo that fix. 👍

/// </summary>
public Checker(CheckerPool pool, VC.ConditionGeneration vcgen, Program prog, string /*?*/ logFilePath, bool appendLogFile,
Split split, ProverContext ctx = null)
public Checker(CheckerPool pool, string /*?*/ logFilePath, bool appendLogFile)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like that Checker is no longer initialized with program-specific data.

while (true) {
var exprs = await ParseSExprs(true).ToListAsync();
Contract.Assert(exprs.Count <= 1);
if (exprs.Count == 0) {
return null;
}

var resp = exprs[0];
Copy link
Member

Choose a reason for hiding this comment

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

The existing code ignores possible malformed output from the underlying prover, right? I guess it's OK since it was already like that.

HandleError(error);
errorCnt++;
error = null;
lock (this) {
Copy link
Member

@cpitclaudel cpitclaudel Feb 14, 2022

Choose a reason for hiding this comment

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

Do you want external clients to be able take this lock? If not I'd use a private object.

Copy link
Member

Choose a reason for hiding this comment

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

Oh but I see below that the code was already like this, sorry

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, good point. Never thought of that. Is it worth the extra code though? External clients taking the lock seems like only a theoretical issue.

{
if (e.Data != null)
lock (this) {
if (e.Data == null)
Copy link
Member

Choose a reason for hiding this comment

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

In the method above you moved the e.data test out of the lock, but not here. Could you do the same here? It would avoid taking the lock when e.Data is null

Copy link
Member

@cpitclaudel cpitclaudel left a comment

Choose a reason for hiding this comment

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

LGTM, none of my comments are blocking

@keyboardDrummer keyboardDrummer merged commit 241dfcd into boogie-org:master Feb 14, 2022
@keyboardDrummer keyboardDrummer deleted the cancellationToken2 branch February 14, 2022 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants