-
Notifications
You must be signed in to change notification settings - Fork 8
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
Concurrency-safe history mechanism proposal (POC) #286
Conversation
Comparing the generated Dafny before and after should help in understanding the proposed code generation changes. Unfortunately I don't see an easy way to get a nice diff of unrelated files on GitHub.
|
method GetStringSingleValue ( config: InternalConfig, input: GetStringInput ) | ||
returns (output: Result<GetStringOutput, Error>) { |
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 like to see the diff in how History is used in method ensures, as that is where we are primarily using them in our code right now. e.g.: https://github.com/aws/aws-cryptographic-material-providers-library-java/blob/main/AwsCryptographicMaterialProviders/dafny/AwsCryptographicMaterialProviders/src/Keyrings/AwsKms/AwsKmsKeyring.dfy#L154-L159
I understand the tests below sort of approximate this, but it's still hard to grok what the exact scope of changes to our libraries would have to be with this change.
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.
Coming back to this, the thing I am having a hard time imagining is where in our clients we would be creating history
, and how we would be passing it around. i.e. every time we create a new client, would we also need to create an accompanying history?
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 we'd be creating a single history object at "the beginning of Dafny execution" and threading it through. I realize that's not very prescriptive in general, but at least in a polymorph library that uses other clients I expect it would make sense to create one at the beginning of each operation (since that's where external code calls into Dafny).
It's possible it would work well for the ***Service
module to create the history and pass it into the ***Operations
module, but I still have to actually try that.
Thanks for the link, I'll see if I can expand the POC to show the equivalent of that kind of check too.
modifies client.Modifies | ||
ensures client.ValidState() | ||
{ | ||
var history := new History(); |
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 this not a ghost?
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.
It's automatically inferred to be (since the History constructor is ghost if nothing else), so not necessary to explicitly add the ghost
keyword, but I will just to be clearer.
Closing this as I'm re-evaluating the design somewhat. |
Not intended to be merged, just showing the difference between how
History
is currently generated and used and how I'd like to refactor it:TestModels/HistoryBefore
, particularlySimpleStringImplTest.dfy
, shows the pattern of assertions currently supported, and the same test underTestModels/HistoryAfter
shows the equivalent after refactoring. I force-added the generated Dafny code so you can explicitly see what gets generated and how I propose to change the code generation logic (by manually editing it after generating it in theHistoryAfter
copy).The proposed change is to make
History
a locally-passed value instead of a constant on clients themselves. It also uses classes implementing a markerEvent
trait instead of datatypes, so that the history can be a heterogeneous sequence of events across multiple operations or even clients, instead of a set of independent histories for each operation.There are a number of advantages to this style:
History
is not morally concurrency-safe: Dafny is currently proving assertions such asSeq.Last(client.History.GetString).input.value == <what the code above just provided>
, but since the client is used by multiple concurrent executions this isn't actually true: other parallel calls could easily have occurred to add more events!Modifies
andHistory
separately. It is also compatible with the upcoming support for verifying the{:concurrent}
attribute, since the history is no longer shared mutable state across concurrent executions, so it doesn't have to be included inreads
ormodifies
clauses.HistoryAfter
.The main disadvantage is that by using classes (the only type that can currently extend traits) instead of datatypes to represent call events, the code gets a fair bit more verbose. The helper
WasNthLastWith
predicate on each event type helps quite a bit, but I'm also open to suggestion on naming and structure.I've made no effort to hook this temporary structure up in CI, but
make verify
succeeds on both models at least.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.