Preliminaries
As Todd has indicated in his answer which has a lot of excellent information, the situation where the described behaviour will actually trigger problems will be very rare. I also read from his answer that WRI doesn't consider this behavior to be a bug and my hopes that this might change in future versions are low or nonexistent. I show several examples in the following which are only meant to demonstrate certain aspects, of course they are oversimplified but I guess everyone to whom this question and answer are relevant will be able to generalize from those and convert them to useful code. To circumvent having to refer to something vague like "the described problem" I introduce the term "block leaking by preemption" and will use it in this answer along with the abbreviation "BLP"...
What I absolutely want to support is Todd's statement that "most user code doesn't need that level of rigor". So if you read this and get afraid I think it is important that you probably only need to care about BLP if you write code:
- for others to use/build on
AND
- that needs to be very robust
AND
- has a reasonable probability to be used in or in combination with preemptive evaluations.
Even then, you only will see problems due to BLP very rarely. I want to emphasize that "rare" also comes in two very different meanings here:
Rare in Theory:
Due to the nature of BLP an extra criterion is whether you expect your code to be used in an evaluation that comes over a preemtive link or while such an evaluation is done. Dynamic
is the most prominent example for such preemptive evaluations, but as Todd has explained is not the only possibility. He gives a probably complete list of such preemptive evaluatons in his answer. If you don't expect your code to be used in combination with any of these functionalities, you probably don't need to read on.
There are many cases, where even in theory BLP can not happen. That includes the case where both the code in the queued evaluation and the code in the preemptive evaluation correctly block the same variable.
Even in theory a potential problem arises only when the preemptive code makes use of a global symbol without blocking it that has temporarily been changed by the queued code (There is no problem the other way round!). That (unfortunately) includes the case when the preemptive code uses InheritedBlock. It also includes the case where both evaluations use the Villegas-Gayley trick for the same symbol (isn't it an interesting coincidence that it is Todd who was the only to answer up to now?). There is of course no problem if the preemptive code doesn't make use of exactly the same symbol. Most code trying to be some sort of robust should and probably will live in an own namespace (Context
) and/or will be properly localized in some way. That only leaves those cases as problematic where system symbols are blocked and of course that is (hopefully) only necessary and done in very rare cases.
One probably should be aware that many other constructs, e.g. Do
and Table
, "effectively" use Block
and in principle are also affected. For the mentioned reasons, I only consider the case where system symbols are blocked to be really problematic and these probably are not very likely used in other constructs than Block
and Internal`InheritedBlock
...
Rare in Practice:
Even in the case that in theory there could exist a problem, it seems very likely you'll (not|never|hardly ever) see it. This is because BLP only manifests when the preemptive evaluation interrupts the queued evaluation exactly while that is in the problematic Block
construct. This to happen is in many real situations very unlikely, in fact you might have noticed that to reproducably trigger BLP for illustrations I need extra tricks like Pause
and UpdateInterval
. While this is somewhat consoling, it is on the other hand also an extra problem: if a problem exists it will typically manifest in (very) rare random malfunctions (probably including crashes) that can't easily be reproduced and are almost impossible to track and debug. A program which is affected will most probably get a reputation for being slightly unstable and stay like that for a long time (and keep the reputation even longer). (every resemblance with recent products by WRI is of course purely incidential and unintentional)...
Here is an example where you can adjust the probability of seeing an influence of the queued evaluation on the preemptive one by adjusting the length of the Pause. 0.007 gave a chance of about 50% on my machine today, the value was different yesterday, probably due to a different load on it, you might need to play with that value on your machine:
Dynamic[f[RandomReal[]]]
Block[{f}, SetAttributes[f, HoldFirst]; f[x_, y_] := 2; Pause[0.008]]
Other interesting "features" are:
- the update of the
Dynamic
is only triggered once, when the blocked symbol is actually changed, but not when its original definitions are restored after the Block
exits.
- setting attributes doesn't trigger the
Dynamic
, that seems to happen only if one changes definitons (==*Values
).
What can be done is an explicit Clear
at the end of the Block
which will trigger the Dynamic
once more, if so desired:
Block[{f}, SetAttributes[f, HoldFirst]; f[x_, y_] := 2; Pause[0.007]; Clear[f]]
"It's a feature"
As Todd has mentioned, there are of course cases where you can make use of the given behavior, e.g. when monitoring the progress of a computation running in the queued kernel, even if that blocks the variable to be monitored:
Dynamic[i]
Block[{i = 1}, While[i < 10, i++; Pause[0.1]]]
This is basically the same as using a Do
loop, which as mentioned also localizes like Block
:
Do[Pause[0.2], {i, 1, 10}]
But still...
On the other hand I think everyone having some experience in writing computer programs will agree that if something can go wrong it will go wrong sooner or later and BLP certainly has the potential to create unexpected problems.
I also slightly disagree with the position that the given situation is not worse than when the evaluation of the body of a Block
might break unknown code that's evaluated in the process of calling system functionality within its body. Of course that can also create problems, but these are local to the evaluation that I've blocked and I'll only break the evaluation of that piece of code. So I can run tests to see whether the code runs as intended and probably eliminate most of the potential pitfalls, failure should to a large intent be deterministic and reproducable and certainly is related to the code I'm calling.
The situation is different with the leaking of such changes into preemptive evaluations: there my blocking in the queued evaluation might break code which is completely unrelated to my code (unrelated in the sense that my code doesn't call it and it doesn't call my code), and I have no possibility to recognize or circumvent the problem with testing (as I don't know which code it is that I might break and if it appears it might be very hard (=impossible) to reproduce). To me that sounds much more frightening.
It also has an issue with responsibility for failure: if I provide some code which runs from a graphical user interface via the preemptive link I can't do anything to protect that from being corrupted by something stupid that just happens to be evaluated in queued mode. Of course it is that codes fault that the preemptive evaluation fails, but it is very hard to a) find what made my code break and b) explain why I can't do anything about it. This doesn't sound like solid ground to build on to me.
Strategies for Premption-Safe Code
This part cares about code which is evaluated over the queued link and might be interrupted by preemptive evaluation. The goal is to write such code in such ways that it won't break the preemptive evaluation (again, this might not be necessary for the overwhelming majority of user code).
Avoid Block (and InheritedBlock)
The most simple strategy is to avoid usage of Block
and InheritedBlock
as much as possible. While there are use cases where you can't do without them, a majority of use cases where I have seen it, it is simply used for elegance or slight performance improvements. Here are some example:
Usage of Block
instead of Module
for performance reasons or to avoid memory leaks from Module. Both seem to not justified for code aiming to be preemption safe. There are techniques to work around the memory leaks that can be generated by Module elsewhere on this site.
Usage of Block
to avoid the need to pass arguments because that's inconvenient and less elegant. Alternatives: do pass values nevertheless.
Usage of Block
to ensure that symbols have no values. Alternatives: use combinations of Unevaluated
, Hold
, ReleaseHold
and friends or use formal symbols.
overwrite/add functionality of system symbols: in many cases it is better to define a new symbol for the purpose which delegates to the original system symbol when appropriate instead of adding additional definitions to the system symbol in e.g. an Internal`InheritedBlock
. Again this is often somewhat less convenient and elegant then changing the system symbol itself, but that seems not be a justification if your goal is preemption safe code.
PreemptProtect
There is PreemptProtect
which seems to exactly solve our problem as it inhibits the interruption of the queued evaluation. If you can guarantee that the body of the Block
will evaluate reasonably fast that might be the best and most simple way to achieve robustness. You should be aware that careless use of it also might cause undesired effects: If you for example press the following button while the Pause
within the PreemptProtect
waits, nothing happens, even though the button was explicitly set up for preemptive evaluation (which of course also would have been the default):
i = 1;
Button["preemptive", Print[i++], Method -> "Preemptive"]
now evaluate the following and click the above button:
PreemptProtect[Pause[3]]
It's probably interesting to see that the button clicks seem to be queued in the preemptive links while PreemptProtect
prevents their immediate evaluation. As Todd explained preemptive evaluations won't interrupt other preemptive evaluations.
Unlike for button presses the preemptive evaluations caused by Dynamic
seem to behave slightly different. In the following, the preemptive evaluations during the PreemptProtect
are either not even triggered or don't queue up but are dismissed (I hope that I interpreted this correctly?)
i = 1;
Dynamic[Print[i++]; "delete me to stop printing...",
UpdateInterval -> 1, TrackedSymbols -> {}]
and in an extra cell:
PreemptProtect[Pause[3]]
Block only where necessary
If you really need to block, only block extactly where necessary. It's tempting to use Block
for other localizations if it's there anyway and include much more as it safes some lines of code and makes things look slightly more simple/cleaner. Example:
Block[{$RecursionLimit = Infinity, x, y},
(* initialization *);
(* actual recursion *);
(* construct result *)
]
Somewhat more preemption-safe (but of course not entirely so):
Module[{x, y},
(* initialization *);
Block[{$RecursionLimit = Infinity},
(* actual recursion *);
];
(* construct result *)
];
When used in combination with PreemptProtect
the final version will provide a very safe and well behaved version:
Module[{x, y},
(* initialization *);
PreemptProtect@Block[{$RecursionLimit = Infinity},
(* actual recursion *);
];
(* construct result *)
];
$DynamicEvaluation
There is one technique which I thought up after asking the question, very similar to the Villegas-Gayley trick. I have not extensively tested it and don't know how well it will work in practice, but I think it is a promissing approach for some use cases. My main concern is preemptive evaluation triggered from dynamic interactivity, where $DynamicEvaluation
provides a documented way to get the information whether we are evaluated via the queued or the preemptive link. There might be similar variables for other preemptive evaluations (e.g. $ScheduledTask
) and I could well imagine that it's possible to find a way to find out whether our code is evaluated in a preemptive or queued way (I would certainly appreciate such information). However we get it, here is how we can make use of such information to avoid the problem:
Dynamic[f[RandomReal[]]]
This will reveal our typical problem:
Block[{f}, f[x_] := 5; Pause[1]; Clear[f]]
This will not:
Block[{f}, f[x_] /; ! $DynamicEvaluation := 5; Pause[1]; Clear[f]]
What it does is to add a definition which only can be matched in the queued evaluation and thus should not affect the dynamic evaluation (except if that would itself change $DynamicEvaluation
). It should be noted that this doesn't prevent the updates of the Dynamic
due to the changes in f
, but it at least will not see the definitions that are only meant to be done for the queued evaluation.
Here is an example which shows that this can even be done for the OwnValues
, although that seems to be quite unusual:
x = 5;Dynamic[Date[] -> x]
This will make the Dynamic
trigger (again the Dynamic
doesn't update when the original value of x is reset!):
Block[{x}, x = 1; Pause[1]; x]
This will not change the value which the preemptive evaluation sees (but still trigger its update):
x = 5;Dynamic[Date[] -> x]
Internal`InheritedBlock[{x},
OwnValues[x] = {HoldPattern[x /; Not[$DynamicEvaluation]] :> 1,
HoldPattern[x /; $DynamicEvaluation] -> x};
Pause[2];
x
]
To write a function which will work reliably in both the queued and the preemptive evaluation mode you'd of course have to explicitly only treat the case where the code is evaluated in the queued mode. Evaluations from the preemptive link can't be interrupted anyway and for them there is no need for any such precautions...
Extra Kernel
It would in principle be possible to run the "offensive" code in an extra kernel. As long as that won't have any preemptive links opened, no BLP should be able to occur. The kernels could be started with LinkOpen
and friends or we could just use the parallel functionality (with e.g. one worker kernel). I think that this is probably causing much more overhead and additional potential problems and thus doesn't think it's a good solution. It could on the other hand be an option if one uses parallel kernels of an extra kernel for other purposes anyway. And of course code that will only run in worker kernels might not be affected by BLP and can probably make use of Block with less concerns about robustness. I'm not sure whether there exist mechanism which also make use of preemptive evaluations on parallel kernels, but would guess that could well be the case.
Strategies to Write Robust Preemptive Code
This part mainly addresses those parts of code that are evaluated over the preemptive link and might interrupt queued code. As my own experience with other ways of preemptive evaluation is limited, the following concentrates on the various uses of the "Dynamic Interactivity Language" parts of Mathematica as the source of preemptive evaluations.
Localize as much as possible
Of course it is only global symbols which are potentially affected by the described problem. The fewer global symbols the dynamic code contains, the less probable it is that it's broken by (temporary) changes to such symbols in the queued evaluation.
Minimal Functionality in Dynamic
As Todd has emphasized, preemptive evaluations are meant for short immediate evaluations. From the point of preemption safe coding, the less these interrupting evaluations actually do, the less can go wrong. You'll also avoid malfunctions due to dynamic timeouts when only what really is necessary is done in the preemptive evaluation. The following is an example how that can be achieved. What we do here is to calculate something and show the result. A naive version would probably look like this:
longCalculation[] := (Pause[1]; Table[{x, Sin[x]}, {x, 0, \[Pi], \[Pi]/20}])
expensiveVisualization[data_List] := (Pause[1]; ListPlot[data])
DynamicModule[{result},
Column[{
Button["Run", result = longCalculation[], Method -> "Queued"],
Dynamic[expensiveVisualization[result]]
}]
]
It seems (not only from this point of view) much better to also do the expensive visualization in the queued evaluation and only do as minimal as possible in the preemptive evaluation, e.g.:
DynamicModule[{plot},
Column[{
Button["Run",
Module[{result}, result = longCalculation[];
plot = expensiveVisualization[result]
],
Method -> "Queued"
],
Dynamic[plot]
}]
]
There is now only almost no code which can be broken by BLP, and you'll also not run into problems when evaluating expensiveVisualization
takes longer than the dynamic timeout allows.
Take Control over when Dynamics Update
Another measure which will generally help to improve the stability of user interfaces considerably, especially when they become reasonably complex is to take as much control about when Dynamic
s actually trigger as possible. That this will also rule out many cases which could be problematic due to BLP is more or less just a side effect of that. Here is again an example:
f[x_, n_] := x^n;
DynamicModule[{n = 1},
Column[{
Slider[Dynamic[n], {1, 10}],
Dynamic[Plot[f[x, n], {x, 0, 1}]]
}]
]
Here is our typical problem, which triggers the above Dynamic
to show something (probably) unintended:
Internal`InheritedBlock[{f},
f[x_, y_] := "whatever";
Pause[1];
]
Of course we actually only wanted the plot to update when n
is changed, so we might want to let Mathematica know about that by using the TrackedSymbols
option:
f[x_, n_] := x^n;
DynamicModule[{n = 1},
Column[{
Slider[Dynamic[n], {1, 10}],
Dynamic[Plot[f[x, n], {x, 0, 1}], TrackedSymbols :> {n}]
}]
]
And now the above Dynamic
is at least not unintendedly triggered by our queued evaluation:
Internal`InheritedBlock[{f},
f[x_, y_] := "whatever";
Pause[1];
]
Experimenting with that mechanisms for system symbols reveals an interesting feature which will again explain why you'll only see such problems very rarely: a Dynamic
will obviously not trigger when system symbols are changed (that is: Dynamic
makes the reasonable expectation that these won't change). Thus our problem for system symbols can only arise when the preemptive evaluations are triggered by accident from other changes:
Here we trigger with UpdateInterval
, thus we can see BLP:
Dynamic[Plot[x, {x, 0, 1}], UpdateInterval -> 0.5]
Block[{Plot}, Pause[2]]
This won't trigger, obviously system symbols (by simple tests one can verify that it's not merely the context that's used to decide about that) are treated different than others in that respect:
Dynamic[Plot[x, {x, 0, 1}]]
Block[{Plot}, Pause[2]]
And even explicitly asking for Plot
to be tracked won't change that (All
or Full
don't change that as well, bug or feature?):
Dynamic[Plot[x, {x, 0, 1}], TrackedSymbols :> {Plot}]
Block[{Plot}, Pause[2]]
A last remark is that for complete control over when Dynamic
s fire you can use a technique I call trigger symbols and that completely circumvents the automatisms (with all problems and benefits this brings). It roughly works like this:
n = 1;
Dynamic[trigger; Plot[x^n, {x, 0, 1}], TrackedSymbols :> {trigger}]
Here it is used to trigger an update even though the change of Plot
would normally not (as we have learned from the above):
Block[{Plot}, Plot = List; trigger = AbsoluteTime[]; Pause[0.5]]
note that again we need a Pause
to ensure that the triggered preemptive evaluation has a chance to interrupt the queued evaluation before it is finished!
Here it is used to only update the Dynamic
once, although we make several changes :
Internal`InheritedBlock[{Plot}, SetOptions[Plot, Background -> LightGray];
n = 10; trigger = AbsoluteTime[]; Pause[0.5]]
Final remarks
Having know been thinking about and experimenting with "block leaking by preemption" several days and with the input of Todd my own resumee about it is:
Appearance of real problems through BLP are probably very very rare, there are already some precautions built in and we have several possibilities to circumvent it. With the mentioned measures it seems to be possible to create very reliable code, even if the default behavior seems to be questionable from that point of view (and I guess/hope we will be able to collect here some even better techniques over time).
There remains a very small chance to introduce problems when making use of Block
and especially InheritedBlock
. So if you are trying to write very robust code, try to avoid them and when necessary, use them with even more extra care as ever before.
The most difficult thing remaining for us users is to decide how much effort is adequate to avoid potential problems from this source for a given chunk of code. The majority of user code will probably not need any special care.
Some parts of my question and this answer are somewhat sarcastic and indicate that I'm not exactly happy with the behavior as it is. As Todd has well described, this is a design decision which has been well thought about and has to balance various aspects. For any such design decision there is no right vs. wrong, but only at most good vs. bad, and whether such a decision is to be considered good or bad needs some experience with using it for some time in practice. The outcome of such a rating depends on personal habits, use cases and is also a matter of taste. I'm afraid that my personal usage of Mathematica these days doesn't exactly match how the majority of users are using it and what it was mainly designed for. Thus I'm aware of the fact that my rating is most probably not very representative. I want to also emphasize that overall I'm a happy Mathematica user and appreciate not only the efforts that Todd and others WRI employees put into answering our questions on this site, but also their work on Mathematica which I consider to be of very good quality in general, even though I don't agree with every design decision in every detail and would hope for some parts to be implemented with reliability having a higher priority.
I probably also should add that while such problems are very unlikely to happen I have seen them in real life applications. One of those instances has cost me several days of frustrating debugging sessions and a lot of very unpleasant communication with disappointed and very unhappy users. The good news is that with the mentioned techniques I could solve the problems, the bad news is that I don't feel that I can guarantee this will not happen again (but hopefully I will need less time to find the source of error). That probably explains my motivation for the question and this lengthy answer.
Block
for what it is meant to seems to be something standard, though. Plus we know it is used in internal code as well, so I can't even be sure to not run into such problems when not using it myself. On the other hand -- I'm not sure how likely such problems really are. I have seen mysterious errors every once in a while when making extensive use ofDynamic
in all versions since 6.0 which could be related to this, but also could have completely different origins. $\endgroup$