Skip to content

Commit

Permalink
reflect ignore_throw by default
Browse files Browse the repository at this point in the history
  • Loading branch information
baggepinnen committed Nov 10, 2023
1 parent b0c005f commit 18398e3
Showing 1 changed file with 22 additions and 3 deletions.
25 changes: 22 additions & 3 deletions docs/src/tutorials/error_recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Safety-critical real-time systems are often required to have performance critical error-recovery logic. While errors are not supposed to occur, they sometimes do anyways 😦, and when they do, we may want to make sure that the recovery logic runs with minimum latency.

In the following example, we are executing a loop that may throw an error. We can tell [`check_allocs`](@ref) that we allow allocations on the error path by passing `ignore_throw=true`, but a bigger problem may arise, the garbage collector may be invoked by the allocation, and introduce an unbounded latency before we execute the error recovery logic.
In the following example, we are executing a loop that may throw an error. By default [`check_allocs`](@ref) allows allocations on the error path, i.e., allocations that occur as a consequence of an exception being thrown. This can cause the garbage collector to be invoked by the allocation, and introduce an unbounded latency before we execute the error recovery logic.

To guard ourselves against this, we may follow these steps
1. Prove that the function does not allocate memory except for on exception paths.
Expand All @@ -29,13 +29,32 @@ end
exit_gracefully() = println("Calling mother")
using AllocCheck, Test
allocs = check_allocs(treading_lightly, (); ignore_throw=true) # Check that it's safe to proceed
allocs = check_allocs(treading_lightly, ()) # Check that it's safe to proceed
```
```@example ERROR
@test isempty(allocs)
```

[`check_allocs`](@ref) returned zero allocations.
[`check_allocs`](@ref) returned zero allocations. If we invoke [`check_allocs`](@ref) with the flag `ignore_throw = false`, we will see that the function may allocate memory on the error path:

```@example ERROR
allocs = check_allocs(treading_lightly, (); ignore_throw = false)
length(allocs)
```

Finally, we test that the function is producing the expected result:

```@example ERROR
val = treading_lightly()
@test val ≈ 22.468278186204103 # hide
```

In this example, we accepted an allocation on the exception path with the motivation that it occurred once only, after which the program was terminated. Implicit in this approach is an assumption that the exception path does not allocate too much memory to execute the error recovery logic before the garbage collector is turned back on. We should thus convince ourselves that this assumption is valid, e.g., by means of testing:

```@example ERROR
treading_lightly() # Warm start
allocated_memory = @allocated treading_lightly() # A call that triggers the exception path
@test allocated_memory < 1e4
```

The allocations sites reported with the flag `ignore_throw = false` may be used as a guide as to what to test.

0 comments on commit 18398e3

Please sign in to comment.