mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-27 19:30:04 +00:00
70 lines
2.6 KiB
Markdown
70 lines
2.6 KiB
Markdown
|
# Reentrancy
|
||
|
|
||
|
A function is called reentrant,
|
||
|
when its execution can be interrupted and the function can be then safely called again.
|
||
|
|
||
|
When programming in Millfork, you need to distinguish conceptually three kinds of reentrant functions:
|
||
|
|
||
|
* nesting-safe
|
||
|
|
||
|
* recursion-safe
|
||
|
|
||
|
* interrupt-safe
|
||
|
|
||
|
As Millfork is a middle-level language, it leaves taking care of those issues to the programmer.
|
||
|
|
||
|
## Nesting safety
|
||
|
|
||
|
Nesting occurs when a function is called when calculating parameters for another call of the same function:
|
||
|
|
||
|
f(f(4))
|
||
|
f(0, f(1,1))
|
||
|
f(g(f(5))
|
||
|
f(g()) // where g calls f, directly or indirectly
|
||
|
|
||
|
Since parameters are passed via global variables,
|
||
|
calling a function while preparing parameters for another call to the same function may cause undefined behaviour.
|
||
|
|
||
|
For that reason, a function is considered nesting-safe if it has maximum one parameter.
|
||
|
|
||
|
It is possible to make a safe nested call to a non-nesting safe function, provided two conditions are met:
|
||
|
|
||
|
* the function cannot modify its parameters
|
||
|
|
||
|
* the non-nested parameters have to have the same values in all co-occuring calls: `f(5, f(5, 6, 7), 7)`
|
||
|
|
||
|
In all other cases, the nested call may cause undefined behaviour.
|
||
|
|
||
|
## Recursion safety
|
||
|
|
||
|
A function is recursive if it calls itself, either directly or indirectly.
|
||
|
|
||
|
Since most automatic variables will be overwritten by the inner call, the function is recursive-safe if:
|
||
|
|
||
|
* parameters are no longer read after the recursive call is made
|
||
|
|
||
|
* an automatic variable is not read from without reinitialization after each recursive call
|
||
|
|
||
|
* all the other variables are stack variables
|
||
|
|
||
|
In all other cases, the recursive call may cause undefined behaviour.
|
||
|
|
||
|
The easiest, but unoptimal way to make a function recursion-safe is to make all local variables stack-allocated
|
||
|
and assigning all parameters to variables as soon as possible. This is slow though, so don't do it unless really necessary.
|
||
|
|
||
|
## Interrupt safety
|
||
|
|
||
|
A function is interrupt-safe if it can be safely called, either directly or indirectly,
|
||
|
simultaneously by the main code and by an interrupt routine.
|
||
|
|
||
|
The only way to make a function interrupt-safe is to have no parameters and make all local variables stack-allocated.
|
||
|
|
||
|
# Reentrancy safety violations
|
||
|
|
||
|
Each of the following things is a violation of reentrancy safety rules and will cause undefined behaviour with high probability:
|
||
|
|
||
|
* calling a non-nesting-safe function without extra precautions as above while preparing another call to that function
|
||
|
|
||
|
* calling a non-recursion-safe function from within itself recursively
|
||
|
|
||
|
* calling a non-interrupt-safe function from both the main code and an interrupt
|