-
Notifications
You must be signed in to change notification settings - Fork 52
Add Prover Based ShutdownManager #489
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
base: master
Are you sure you want to change the base?
Conversation
This reverts commit 3a62853.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I find the idea good. It matches our architectural guidelines and solves a clear problem. The PR needs some minor improvements.
* ProverEnvironment}. | ||
* @throws UnsupportedOperationException if the solver does not support prover specific shutdown. | ||
*/ | ||
ShutdownManager getShutdownManagerForProver() throws UnsupportedOperationException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to API change, better provide a default implementation that throws the UnsupportedException.
Additionally, his method is a plain getter. Why does a plain getter throw such an exception? The getter itself does not depend on solver-features that could be unsupported, but only on JavaSMT-internal code. Perhaps just remove the exception from signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback! I removed the exception from the signature and added a default implementation.
return getShutdownManagerForProverImpl(); | ||
} | ||
|
||
protected ShutdownManager getShutdownManagerForProverImpl() throws UnsupportedOperationException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this method is not generic, we do not need it. just use inline it in the normal getShutdoenManagerForProver
.
With a default implementation in the API, we can even remove this implmentation completely and just mark the overridden method as abstract
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. I imagined that i need more solver specific code, but i didn't. I removed it. Thanks for the feedback!
return proverShutdownManager | ||
.getNotifier() | ||
.shouldShutdown(); // shutdownNotifer is defined in the | ||
// superclass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is formatted ugly, and it might not be understandable, please rephrase.
Is getNotifier()
a "fast" method call or does it construct any objects? The callback is expected to be executed quite often from native solver code. We could store the notifier directly within the class and just reuse it.
Addtiionally, we already have a method for getting the manager. Why do we then use direct access here? Please use the getter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the comment, as the new default should be obvious.
getNotifier()
does not construct any objects, it just returns the notifier that is created when the ShutdownManager
is created. You can argue that it is faster to have the reference directly, but this is built once when the prover is created. I changed it so that the notifier is generally accessible directly in the superclass.
Thanks for the feedback!
@@ -119,7 +123,8 @@ private boolean readSATResult(Result resultValue) throws SolverException, Interr | |||
return false; | |||
} else if (resultValue == Result.UNSAT) { | |||
return true; | |||
} else if (resultValue == Result.UNKNOWN && shutdownNotifier.shouldShutdown()) { | |||
} else if (resultValue == Result.UNKNOWN | |||
&& proverShutdownManager.getNotifier().shouldShutdown()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed that the manager always returns the same notifier-instance and the notifier always returns the same flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! The notifier is created final and only once, see here: private final ShutdownNotifier notifier = new ShutdownNotifier(this);
The flag can not be changed once a shutdown is triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Referencing some existing source code is no "guarantee".
The guarantee would be a JavaDoc specification for the ShutdownManager-API, which is missing:
https://sosy-lab.github.io/java-common-lib/api/org/sosy_lab/common/ShutdownManager.html#getNotifier()
A user can provide his own implementation of the shutdown-manager and change its behaviour.
I really would prefer to keep a local reference to this notifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no intention of a guarantee that getNotifier()
always returns the same instance, but there is definitively the intention that this does not matter and that all notifiers handed out by a manager always return the shutdown state of the manager, and that shouldShutdown()
will keep returning true
forever after it has returned true
once.
Since the split in separate manager and notifier classes this was not fully written down in the JavaDoc, but I improved this now: sosy-lab/java-common-lib@108faa6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PhilippWendler i just noticed that there is/can be a delay in between the shutdown being requested and the children being informed. See the 2 tests here. Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what exactly you are referring to. But if you have multi-threaded code, then you can of course never be sure when each of the threads is scheduled in relation to the others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but i would have expected that the chain of signals in the shutdown notifiers/managers is built in such a way that they are all informed in a synchronized way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean with "synchronized way"? "synchronized" is not some magic global property, one can only synchronize against specific things.
If you want help, you need to be more specific. What is the sequence of operations that you are observing and that is unexpected/unwanted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was merely curious ;D
We discussed this offline sufficiently. Thank you!
@Override | ||
protected ShutdownManager getShutdownManagerForProverImpl() throws UnsupportedOperationException { | ||
return proverShutdownManager; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One could move this method higher up in the hierarchy and just provide it in the abstract super-class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it completely, as we didn't really need it.
@@ -49,7 +49,7 @@ protected BoolectorAbstractProver( | |||
this.manager = manager; | |||
this.creator = creator; | |||
this.btor = btor; | |||
terminationCallback = shutdownNotifier::shouldShutdown; | |||
terminationCallback = proverShutdownManager.getNotifier()::shouldShutdown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for comparison to above: Here, we keep the same notifier and reuse it for all callback-calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We build a new manager in every prover creation (see here) and then use this new managers notifier to request shutdowns. So we always use the one notifier bound to the one manager of the prover (that is a child of the contexts ShutdownNotifier
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now outdated due to the changes.
*/ | ||
private TerminationCallback getTerminationTest() { | ||
return () -> { | ||
proverShutdownManager.getNotifier().shutdownIfNecessary(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could extract the reference to the notifier and just reuse.
Might look like this:
final ShutdownNotifier notifier = getShutdownManagerForProver().getNotifier()
return () -> {
notifier.shutdownIfNecessary();
return false;
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Thanks for the hint.
t.start(); | ||
assertThrows(InterruptedException.class, pe::isUnsat); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of the tests checks whether the solver is usable after a shutdown.
None of the tests checks that, out of multiple parallel instances, if one is terminated, then all other provers remain running and deliver results.
Could you add such tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just writing some tests and noticed that we do not really define how a solver (context and prover) should behave after a context-wide shutdown is requested.
Should prover creation throw a exception if the context is already shut down?
What operations are still usable and guaranteed to be correct/safe after a shutdown in your current prover?
This PR might provide a solution for a quite old issue: #100 :-) |
… shut-down etc.) and add more shutdown checks in the abstract prover
@@ -119,7 +123,8 @@ private boolean readSATResult(Result resultValue) throws SolverException, Interr | |||
return false; | |||
} else if (resultValue == Result.UNSAT) { | |||
return true; | |||
} else if (resultValue == Result.UNKNOWN && shutdownNotifier.shouldShutdown()) { | |||
} else if (resultValue == Result.UNKNOWN | |||
&& proverShutdownManager.getNotifier().shouldShutdown()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Referencing some existing source code is no "guarantee".
The guarantee would be a JavaDoc specification for the ShutdownManager-API, which is missing:
https://sosy-lab.github.io/java-common-lib/api/org/sosy_lab/common/ShutdownManager.html#getNotifier()
A user can provide his own implementation of the shutdown-manager and change its behaviour.
I really would prefer to keep a local reference to this notifier.
result = smtEngine.checkSat(); | ||
} | ||
shutdownNotifier.shutdownIfNecessary(); | ||
proverShutdownManager.getNotifier().shutdownIfNecessary(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to avoid calling getNotifier()
more than once here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just forgot to push everything. Should be removed now. Sorry for the delay.
- test context interrupt with subsequent prover usage - test prover interrupt with subsequent feature usage (ignored because unfinished)
…(do we have this already elsewhere?)
…e() etc. so that interrupts and common prerequisites are uniformly handled
I cleaned up the default behavior of the prover API (isUnsat(), getModel(), getUnsatCore() etc.) so that they share the common prerequisites and no inconsistencies within the solver implementations exist. I found 2 things i want to discuss:
|
…r shutdown notifier + default implementation
* @implNote If the SMT solver is able to handle satisfiability tests with assumptions please | ||
* consider implementing the {@link InterpolatingProverEnvironment} interface, and return an | ||
* Object of this type here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this comment. The return type of the method is InterpolatingProverEnvironment
, one always needs to return such an instance. What does this have to do with "solving with assumptions"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a comment for developers (e.g. students) so that they get info what to implement. We have this at several locations and for example helped me back in my bachelors thesis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, having @implNote
s is good. But what does this one want to say? Isn't InterpolatingProverEnvironment
always required, and what does implementing it have to do with "satisfiability tests with assumptions"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it is not always required. The default impl throws a UnsupportedOperationException
.
It is meant to lead the developer to the conclusion that this needs to be implemented if you want to interpolate. Usually students (in my experience) notice once they run the tests that things are missing ;D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but then the comment should still refer to interpolation and not satisfiability tests with assumptions.
…h ShutdownNotifiers
…llegalArgumentException for msat_model_create_iterator()
…n as Throwable (due to Z3)
…tedException to it
…iterator into branch feature_prover_based_shutdown
…rruptedException for assumption removal and just throw it
…rruptedException and just throw it, including getUnsatCore()
…n in TimeoutTest.java
@kfriedberger @PhilippWendler i now merged #501 into this to get the Solver/InterruptedExceptions for the model and prover API. I also added them to |
…rover checkInterpolationArguments()
I just realized that ca14eff was even more important than I thought before. If the intention of
This means that no matter what an application does, if it uses the shutdown interface as it is intended, it would randomly crash with So the only way to handle shutdown requests properly can be to throw But note that it is unnecessary to check for shutdown immediately when entering a method. Because of the asynchronicity no method can and needs to guarantee that it always notices a shutdown. The only place where one needs to check for shutdowns is in loops that execute for potentially long time. |
…ion with shutdown notifiers
Oh wow! Good catch! This means that all the API changes are actually fully justified. |
…ceptions as the new API changes do not need this anymore
Most solvers allow either re-usable shutdown or we use shared environments that are created per prover, making them isolated enough to be shut down without stopping the context.
This PR adds a controlled way of shutting down only a single prover with the manager returned by our API IFF it is supported by the solver. These prover shutdown managers are all children of the context given shutdown manager.
This should only be merged after #501, is this branch is merged here.