We also believe we've fixed the crash related to read-during-write of
certain properties, by altering `BLField`'s invalidation to only
invalidate the non-persistent cache when it isn't suppressed.
This has no effect on the non-persistent cache, which is invalidated
correctly anyway during the write() that suppression is defined for;
however, by not stripping it away while that write() is being
implemented by Blender, CPython's builtin atomic `dict` operations
(especially `.get()`) manage to provide the thread-safety that we seem
to have been missing.
We tested the race-condition by the usual "drive everything from one
parameter", and weren't able to trigger one. Here's to hoping.
We've also started to become large fans of the "`Value` from `Func`"
principle, which both allows `Value` to pick up on fully realized
symbols in preceding flows, while also guaranteeing that we're not
maintaining duplicate code-paths.
However, the cost of the organizational reduction is that it is admittedly
slower (in the interactive hot-loop) than we'd like - in large part due
to having to realize a `FuncFlow` every time. However, certain
optimizations have a lot of potential:
- Reducing extraneous invalidations of `.lazy_func` on the `ExprSocket`,
though some cleverness with `.output_sym` is needed.
- Pure performance work on all `Flow` objects. Surprisingly, they seem
to mean a lot.