I've (mistakenly) picked up a course about verifying concurrent programs, and we've so far covered this method called "Owicki-Gries method". Apparently, one can prove various results about the program by associating an assertion with each statement, and show these assertions are inductive and don't interfere with each other. One of our assignments involves Lamports' Fast Mutual Exclusion algorithm, detailed in this paper:
In the paper, an Owicki-Gries style proof for mutual exclusion is given. It looks completely anti-intuitive. What I struggle to understand is how to come up with these assertions in the first place? When do you know these assertions are neither too strong (so strong that it breaks interference freedom) nor too weak(e.g. something trivial, like a tautology with each statement)?
Cheers
To start with, and to provide an understanding of the Owicki-Gries method, I'd strongly recommend checking out the two chapters on Owicki-Gries that can be found in this textbook.
(The full textbook can also be found in draft format here during mid 2021, or you could email Professor Morgan to request a copy)
When you write an assertion and then look to prove local correctness, to quote the textbook "each assertion ... is a postcondition for the
fragment immediately before, and a precondition for the one (line of code) immediately after.
To answer your question, you ask about how to decide on the strength of an assertion. As an assertion is needed before and after each line of code, the assertion will range from extremely weak to moderately weak. As such, I'd suggest trying a moderately weak assertion initially.
As you're proving local correct, global invariants and global correctness, if you realise that the assertions are too strong, you can go back to the code and weaken them. Conversely, if the assertions are too weak you can try tweak them to be a little stronger.
You know they are the right strength when they support the running of your program while meeting the conditions of Owicki-Gries and satisfying the properties of safety and liveness.
Related
The biggest misunderstanding for me in Swift is the throws keyword. Consider the following piece of code:
func myUsefulFunction() throws
We cannot really understand what kind of error it will throw. The only thing we know is that it might throw some error. The only way to understand what the error might be is by looking at the documentation or checking the error at runtime.
But isn't this against Swift's nature? Swift has powerful generics and a type system to make the code expressive, yet it feels as if throws is exactly opposite because you cannot get anything about the error from looking at the function signature.
Why is that so? Or have I missed something important and mistook the concept?
I was an early proponent of typed errors in Swift. This is how the Swift team convinced me I was wrong.
Strongly typed errors are fragile in ways that can lead to poor API evolution. If the API promises to throw only one of precisely 3 errors, then when a fourth error condition arises in a later release, I have a choice: I bury it somehow in the existing 3, or I force every caller to rewrite their error handling code to deal with it. Since it wasn't in the original 3, it probably isn't a very common condition, and this puts strong pressure on APIs not to expand their list of errors, particularly once a framework has extensive use over a long time (think: Foundation).
Of course with open enums, we can avoid that, but an open enum achieves none of the goals of a strongly typed error. It is basically an untyped error again because you still need a "default."
You might still say "at least I know where the error comes from with an open enum," but this tends to make things worse. Say I have a logging system and it tries to write and gets an IO error. What should it return? Swift doesn't have algebraic data types (I can't say () -> IOError | LoggingError), so I'd probably have to wrap IOError into LoggingError.IO(IOError) (which forces every layer to explicitly rewrap; you can't have rethrows very often). Even if it did have ADTs, do you really want IOError | MemoryError | LoggingError | UnexpectedError | ...? Once you have a few layers, I wind up with layer upon layer of wrapping of some underlying "root cause" that have to be painfully unwrapped to deal with.
And how are you going to deal with it? In the overwhelming majority of cases, what do catch blocks look like?
} catch {
logError(error)
return
}
It is extremely uncommon for Cocoa programs (i.e. "apps") to dig deeply into the exact root cause of the error and perform different operations based on each precise case. There might be one or two cases that have a recovery, and the rest are things you couldn't do anything about anyway. (This is a common issue in Java with checked exception that aren't just Exception; it's not like no one has gone down this path before. I like Yegor Bugayenko's arguments for checked exceptions in Java which basically argues as his preferred Java practice exactly the Swift solution.)
This is not to say that there aren't cases where strongly typed errors would be extremely useful. But there are two answers to this: first, you're free to implement strongly typed errors on your own with an enum and get pretty good compiler enforcement. Not perfect (you still need a default catch outside the switch statement, but not inside), but pretty good if you follow some conventions on your own.
Second, if this use case turns out to be important (and it might), it is not difficult to add strongly typed errors later for those cases without breaking the common cases that want fairly generic error handling. They would just add syntax:
func something() throws MyError { }
And callers would have to treat that as a strong type.
Last of all, for strongly typed errors to be of much use, Foundation would need to throw them since it is the largest producer of errors in the system. (How often do you really create an NSError from scratch compared to deal with one generated by Foundation?) That would be a massive overhaul of Foundation and very hard to keep compatible with existing code and ObjC. So typed errors would need to be absolutely fantastic at solving very common Cocoa problems to be worth considering as the default behavior. It couldn't be just a little nicer (let alone have the problems described above).
So none of this is to say that untyped errors are the 100% perfect solution to error handling in all cases. But these arguments convinced me that it was the right way to go in Swift today.
The choice is a deliberate design decision.
They did not want the situation where you don't need to declare exception throwing as in Objective-C, C++ and C# because that makes callers have to either assume all functions throw exceptions and include boilerplate to handle exceptions that might not happen, or to just ignore the possibility of exceptions. Neither of these are ideal and the second makes exceptions unusable except for the case when you want to terminate the program because you can't guarantee that every function in the call stack has correctly deallocated resources when the stack is unwound.
The other extreme is the idea you have advocated and that each type of exception thrown can be declared. Unfortunately, people seem to object to the consequence of this which is that you have large numbers of catch blocks so you can handle each type of exception. So, for instance, in Java, they will throw Exception reducing the situation to the same as we have in Swift or worse, they use unchecked exceptions so you can ignore the problem altogether. The GSON library is an example of the latter approach.
We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the catch() block.
https://github.com/google/gson/blob/master/GsonDesignDocument.md
That is an egregiously bad decision. "Hi, you can't be trusted to do your own error handling, so your application should crash instead".
Personally, I think Swift gets the balance about right. You have to handle errors, but you don't have to write reams of catch statements to do it. If they went any further, people would find ways to subvert the mechanism.
The full rationale for the design decision is at https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst
EDIT
There seems to be some people having problems with some of the things I have said. So here is an explanation.
There are two broad categories of reasons why a program might throw an exception.
unexpected conditions in the environment external to the program such as an IO error on a file or malformed data. These are errors that the application can usually handle, for example by reporting the error to the user and allowing them to choose a different course of action.
Errors in programming such as null pointer or array bound errors. The proper way to fix these is for the programmer to make a code change.
The second type of error should not, in general be caught, because they indicate a false assumption about the environment that could mean the program's data is corrupt. There my be no way to continue safely, so you have to abort.
The first type of error usually can be recovered, but in order to recover safely, every stack frame has to be unwound correctly which means that the function corresponding to each stack frame must be aware that the functions it calls may throw an exception and take steps to ensure that everything gets cleaned up consistently if an exception is thrown, with, for example, a finally block or equivalent. If the compiler doesn't provide support for telling the programmer they have forgotten to plan for exceptions, the programmer won't always plan for exceptions and will write code that leaks resources or leaves data in an inconsistent state.
The reason why the gson attitude is so appalling is because they are saying you can't recover from a parse error (actually, worse, they are telling you that you lack the skills to recover from a parse error). That is a ridiculous thing to assert, people attempt to parse invalid JSON files all the time. Is it a good thing that my program crashes if somebody selects an XML file by mistake? No isn't. It should report the problem and ask them to select a different file.
And the gson thing was, by the way, just an example of why using unchecked exceptions for errors you can recover from is bad. If I do want to recover from somebody selecting an XML file, I need to catch Java runtime exceptions, but which ones? Well I could look in the Gson docs to find out, assuming they are correct and up to date. If they had gone with checked exceptions, the API would tell me which exceptions to expect and the compiler would tell me if I don't handle them.
Both require and assert are used to perform certain checks during runtime to verify certain conditions.
So what is the basic difference between them?
The only one I see is that require throws IllegalArgumentException and assert throws AssertionError.
How do I choose which one to use?
As Kigyo mentioned there is a semantic difference
assert means that your program has reached an inconsistent state this might be a problem with the current method/function (I like to think of it a bit as HTTP 500 InternalServerError)
require means that the caller of the method is at fault and should fix its call (I like to think of it a bit as HTTP 400 BadRequest)
There is also a major technical difference:
assert is annotated with #elidable(ASSERTION)
meaning you can compile your program with -Xelide-below ASSERTION or with -Xdisable-assertions and the compiler will not generate the bytecode for the assertions. This can significantly reduce bytecode size and improve performance if you have a large number of asserts.
Knowing this, you can use an assert to verify all the invariants everywhere in your program (all the preconditions/postconditions for every single method/function calls) and not pay the price in production.
You would usually have the "test" build with all the assertions enabled, it would be slower as it would verify all the assertions at all times, then you could have the "production" build of your product without the assertions, which you would eliminate all the internal state checks done through assertion
require is not elidable, it makes more sense for use in libraries (including internal libraries) to inform the caller of the preconditions to call a given method/function.
This is only my subjective point of view.
I use require whenever I want a constraint on parameters.
As an example we can take the factorial for natural numbers. As we do not want to address negative numbers, we want to throw an IllegalArgumentException.
I would use assert, whenever you want to make sure some conditions (like invariants) are always true during execution. I see it as a way of testing.
Here is an example implementation of factorial with require and assert
def fac(i: Int) = {
require(i >= 0, "i must be non negative") //this is for correct input
#tailrec def loop(k: Int, result: Long = 1): Long = {
assert(result == 1 || result >= k) //this is only for verification
if(k > 0) loop(k - 1, result * k) else result
}
loop(i)
}
When result > 1 is true, then the loop was executed at least once. So the result has to be bigger or equal to k. That would be a loop invariant.
When you are sure that your code is correct, you can remove the assert, but the require would stay.
You can see here for a detailed discussion within Scala language.
I can add that, the key to distinguish between require and assert is to understand these two. These two are both tools of software quality but from different toolboxes of different paradigms. In summary assert is a Software testing tool which takes a corrective approach, whereas require is a design by contract tool which takes a preventive approach.
Both require and assert are means of controlling validity of state. Historically there were 2 distinct paradigms for dealing with invalid states. The first one which is mainstream collectively called software testing discipline methodologies and tools. The other, called design by contract. These are two paradigms which are not comparable.
Software testing ensures a code versatile enough to be capable of error prone actions, were not misused. Design by contract controls code from having such capability. In other words Software testing is corrective, and design by contract is preventive.
assert is used to write unit tests, i.e. if a method passes all tests each written by an assert expression, the code is qualified as error free. So assert seats besides operational code, and is an independent body.
require is embedded within code and part of it to assure nothing harmful can happen.
In very simple language:
Require is used to enforce a precondition on the caller of a function or the creator of an object of some class. Whereas, assert is used to check the code of the function itself.
So, if a precondition fails, then you get an illegal argument exception. Whereas, if an assertion fails and it's not the caller's fault and consequently you get an assertion error.
require, ensure and invariance are concepts in Contract By Design (CBD) development process.
require checks for the pre-conditions that the caller should satisfy to consume the routine.
ensure checks for the correctness in the return value (and to also verify only the desired change has happened and nothing more)
invariance checks for the validness of the class at all critical times.
CBD is a development methodology to build correct/robust software. For more details on CBD Google and you should hit a link from Eiffel Software. Hope this helps.
Scaladocs/javadocs are pretty good as well:
assert()
Tests an expression, throwing an AssertionError if false. Calls to this method will not be generated if -Xelide-below is greater than ASSERTION.
require()
Tests an expression, throwing an IllegalArgumentException if false. This method is similar to assert, but blames the caller of the method for violating the condition.
This is a very naive question about input validation in general.
I learned about input validation techniques such as parse and validatestring. In fact, MATLAB built-in functions are full of those validations and parsers. So, I naturally thought this is the professional way of code development. With these techniques, you can be sure of data format of input variables. Otherwise your codes will reject the inputs and return an error.
However, some people argue that if there is a problem in input variable, codes will cause errors and stop. You'll notice the problem anyway, and then what's the point of those complicated validations? Given that codes for validation itself take some efforts and time, often with quite complicated flow controls, I had to admit this opinion has its point. With massive input validations, readability of codes may be compromised.
I would like hear about opinions from advanced users on this issue.
Here is my experience, I hope it matches best practice.
First of all, let me mention that I typically work in situations where I have full control, and won't build my own UI as #tom mentioned. In general, if there is at any point a large probability that your program gets junk inputs it will be worth checking for them.
Some tradeoffs that I typically make to decide whether I should check my inputs:
Development time vs debug time
If erronious inputs are hard to debug (for example because they don't cause errors but just undesirable outcomes) the balance will typically be in favor of checking, otherwise not.
If you are not sure where you will end up (re)using the code, it may help to enforce any assumptions that are required on the input.
Development time vs runtime experience
If your code takes an hour to run, and will break in the end when an invalid input value occurs, you would want to check of this at the beginning of the code
If the code runs into an error whilst opening a file, the user may not understand immediately, if you mention that no valid filename is specified this may be easier to deal with.
The really (really) short story:
Break your design down into user interface, business logic and data - (see MVC pattern)
In your UI layer, do "common sense" validation, e.g. if the input is a $ cost value then it should be >= 0, be able to be parsed into a decimal etc.
In your business logic layer, validate the value, e.g. the $ cost value might not be allowed to be greater than the profit margin (etc.)
In your data layer, validate the data operation, e.g. that insert operation succeeded
The extra really short story: YES! Validate all inputs.
For extra reading credits see: this!
http://dlang.org/expression.html#AssertExpression
Regarding assert(0): "The optimization and code generation phases of compilation may assume that it is unreachable code."
The same documentation claims assert(0) is a 'special case', but there are several reasons that follow.
Can the D compiler optimize based on general assert-ions made in contracts and elsewhere?
(as if I needed another reason to enjoy the in{} and out{} constructs, but it certainly would make me feel a little more giddy to know that writing them could make things go fwoosh-ier)
In theory, yes, in practice, I don't think it does, especially since the asserts are killed before even getting to the optimizer on dmd -release. I'm not sure about gdc and ldc, but I think they share this portion of the code.
The spec's special case reference btw is that assert(0) is still present, in some form, with the -release compile flag. It is translated into an illegal instruction there (asm {hlt;} - non-kernel programs on x86 aren't allowed to use that so it will segfault upon hitting it), whereas all other asserts are simply left out of the code entirely in -release mode.
GDC certainly does optimise based on asserts. The if conditions make for much better code, even causing unnecessary code to disappear. However, unfortunately at the moment the way it is implemented is that the entire assert can disappear in release build mode so then the compiler never sees the beneficial if-condition info and actually generates worse code in release than in debug mode! Ironic. I have to admit that I've only looked at this effect with if conditions in asserts in the body, I haven't checked what effect in and out blocks have. The in- and out- etc contract blocks can be turned off based on a command line switch iirc, so they are not even compiled, I think this possibly means the compiler doesn't even look at them. So this is another thing that might possibly affect code generation, I haven't looked at it. But there is a feature here that I would very much like to see, that the if condition truth values in the assert conditions (checking that there is no side-effect code in the expression for the assert cond) can always be injected into the compiler as an assumption, just as if there had been an if statement even in release mode. It would involve pretending you had just seen an if ( xxx ) but with the actual code generation for the test suppressed in release mode, and with subsequent code feeling the beneficial effects of say known truth values, value-range limits and so on.
Do the risks caused by bypassing Perl safe signals for example like shown in the second timeout example in the DBI documentation concern only the code that uses such bypassing?
The code in that example works hard to localize the change to just that section of code, or any code called from it.
There is not 100% guarantee that no code will be effected outside the code that bypasses safe signals, because signals are no longer safe. In the example the call being timed out is a DBI->connect. For most DBD's this will be implemented mostly in C, unless the C code can handle being aborted and tried again you might find that some data structures internal to the DBD, or the libraries it uses, are left in a inconstant state.
The chances of the example code going wrong is probably incredibly tiny. My personal anecdote on the issues is that I had used the traditional Perl signal handling for years before safe signals were introduced and for a long time I had never had a problem. I hadn't even been very cautious about what I did in my signal handlers. Then we managed to hit a data set that actually did trigger memory corruptions in about 1 out of ever 100 runs. Just modifying the signal handlers to use better practices, similar to those in the example, eliminated our issues.
What does that even mean? By using unsafe signals, you can corrupt Perl's internals and Perl variables. It can also cause problem if a non-reentrant C library call is interrupted.
This can lead to SEGFAULTs and other problems, and those may only manifest themselves outside the block where the timeout is in effect.