diff options
| author | Kévin Le Gouguec <kevin.legouguec@gmail.com> | 2019-09-13 23:56:42 +0200 |
|---|---|---|
| committer | Kévin Le Gouguec <kevin.legouguec@gmail.com> | 2019-09-13 23:56:42 +0200 |
| commit | 652d82d4404ee7fd3917e6a422c200d52b06c5ce (patch) | |
| tree | dc81ed609a3137cecdf201b95baab4e08f5b77c3 /reviews/blog-roll.md | |
| parent | b586fd87fc90fda40b1927ec1b88984de9e7d9ea (diff) | |
| download | memory-leaks-652d82d4404ee7fd3917e6a422c200d52b06c5ce.tar.xz | |
Take notes on Joe Duffy's Error Model
Diffstat (limited to 'reviews/blog-roll.md')
| -rw-r--r-- | reviews/blog-roll.md | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/reviews/blog-roll.md b/reviews/blog-roll.md index d7b7dd8..3fa4d93 100644 --- a/reviews/blog-roll.md +++ b/reviews/blog-roll.md @@ -171,3 +171,117 @@ Satirical websites fighting [web bloat] with minimalist designs. Think of internationalization [web bloat]: https://idlewords.com/talks/website_obesity.htm + +# [Joe Duffy's Blog] + +[The Error Model] +: An in-depth look at what "errors" are in the context of software, + how some languages choose to deal with them, and what model the + Midori team implemented. + + > Our overall solution was to offer a two-pronged error model. On + > one hand, you had fail-fast – we called it abandonment – for + > programming bugs. And on the other hand, you had statically + > checked exceptions for recoverable errors. + + Starts by outlining the "performance metrics" for a good error + model, then goes over unsatisfactory models: + + - **Error codes** clutter a function's signature with an extra + return value, and the resulting branches degrade performance; + they do not automatically interrupt execution, thus when bugs + finally show up, it can be hard to track their origin down. + - Though they did provide their developers with an escape + hatch that lets them ignore return values, their `ignore` + keyword is at least auditable. + + - **Unchecked exceptions** make it hard to reason about a + program's flow. They persist because in the grand scheme of + things, they stay out of the way When Things Work™. + + - **Exceptions in general** tend to come with some performance + baggage (e.g. symbols for stack traces), and encourage coarse + error-handling (i.e. throwing a `try` blanket spanning several + statements instead of focusing on individual calls). + + All of these models conflate *recoverable errors* (e.g. invalid + program inputs) that the application can act on (by telling users + about their mistakes, assuming a transient environment failure and + re-trying, or ignoring the error) with *bugs*, i.e. unexpected + conditions that, when unhandled, create bogus states and + transitions in the program, and may only have visible impacts + further down the line. + + As these bugs are "unrecoverable", the team chose the + "**abandonment**" strategy (aka "fail-fast") to deal with them. + + > My impression is that, largely because of the continued success + > of monolithic kernels, the world at large hasn’t yet made the + > leap to “operating system as a distributed system” insight. + > Once you do, however, a lot of design principles become + > apparent. + + > As with most distributed systems, our architecture assumed + > process failure was inevitable. + + The micro-kernel architecture, where basic kernel features such as + "the scheduler, memory manager, filesystem, networking stack, and + even device drivers" are all run as isolated user-mode processes, + encourages "wholesale abandonment" as an error-handling stragegy, + since the failure remains contained and does not bring down the + whole system. + + They implemented **contracts** using dedicated syntax and made + them part of a function's interface. Delegating contract-checking + to a library would have buried pre- and post-conditions as regular + calls inside a function's definition, whereas they wanted them to + become part of the function's metadata, where they can be analyzed + by optimizers, IDEs, etc. + + > Contracts begin where the type system leaves off. + + Contract violations trigger (at worst) program abandonment; + **type-system** violations plainly prevent the program from + existing. + + Since "90%" of their contracts were either null or range checks, + they found a way to encode nullability and ranges in the + type-system, reducing this: + + public virtual int Read(char[] buffer, int index, int count) + requires buffer != null + requires index >= 0 + requires count >= 0 + requires buffer.Length - index < count { + ... + } + + To this: + + public virtual int Read(char[] buffer) { + ... + } + + While preserving the same guarantees, checked at compile-time. + + **Nullable** types were designated with `T?`; `T` implicitly + converted to `T?`, but conversion from `T?` to `T` required noisy + (i.e. auditable) operators which would trigger abandonment when + needed. + + *Recoverable errors* were handled with checked exceptions, which + were part of a function's signature. Since most bugs were dealt + with through abandonment, most of their APIs didn't throw. + + To make it easier to reason about control flow, callers had to + explicitly say `try` before calling a function that might throw, + which did what Rust's deprecated `try!()` did: yield the return + value on the happy path, else re-throw. + + Covers performance concerns, composition with concurrency; muddies + the waters somewhat with "aborts" which kind of look like + `longjmp`s to me? Except it runs the code in `catch` blocks it + finds while crawling back the stack? + +[Joe Duffy's Blog]: http://joeduffyblog.com/ +[The Error Model]: http://joeduffyblog.com/2016/02/07/the-error-model/ |
