57
$\begingroup$

For many questions on this site answers are given which suggest to use Block and, especially for more advanced topics, Internal`InheritedBlock, (which isn't documented).

More than once those answers have triggered followups about how safe it is to use those, especially when overwriting system functionality (that is symbols in the "System`" or another "reserved" context). Latest example is this answer, which motivated my question. At first sight these seem to only affect the system in a very local way, and thus are considered to be relatively safe. But I have my doubts, which are fortified by the following example:

x = 5;
Dynamic[x -> DateString[], UpdateInterval -> 1]

and then, in an extra cell:

Internal`InheritedBlock[{Rule}, SetAttributes[Rule, HoldFirst]; Pause[5]]

What you'll see is that the dynamically evaluated code will be affected by the changes of the InheritedBlock. Depending on the code in the Dynamic this can lead to wrong results, error messages, dynamic aborts, extensive memory usage and crashes of kernel and frontend. In fact I have a deep feeling that many of the problems that I have seen when using Dynamic language constructs extensively and which usually are very hard to reproduce and debug could be related to this mechanism.

The example shown above can be solved by wrapping the InheritedBlock with PreemptProtect, but as that makes the Dynamic effectively stop updating while the InheritedBlock evaluates, I'm not sure whether that couldn't also be a source of potential problems (it certainly prevents the Dynamic from working the way it was actually meant to). I would also like to emphasize that Block has the same problem and probably is much less "exotic"...

My interpretation of this is that the preemptive links see the current state of Blocked symbols when they interrupt the main evaluation. I personally consider this to be a design oversight and would wish it would be different or at least addressed somewhere in the documentation. Anyway, here are my questions:

  • Are there other known cases where the effect of such uses of Block and InheritedBlock affect more than the local evaluation of their bodies.
  • Does anyone know better/additional means to protect against such "leaking" of changes meant to only be applied very locally.
  • Are there any other suggestions and tips to stay out of problems when one really needs such functionality?
$\endgroup$
4
  • 6
    $\begingroup$ A question with remarkably many votes and remarkably few responses. While doing things like this are exciting, and sometimes seem promising for solving some problems, it's very difficult to come up with bullet-proof patterns. What I don't like about Mathematica is that it's so difficult to foresee the consequences of doing something non-standard. Personally I avoid these as much as I can ... (but it's difficult to resist the temptation sometimes). $\endgroup$
    – Szabolcs
    Commented May 24, 2013 at 19:35
  • 1
    $\begingroup$ @Szabolcs: thanks for your comment, a reaction, at least :-). I absolutely agree and also try to avoid non-standard stuff. Using 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 of Dynamic in all versions since 6.0 which could be related to this, but also could have completely different origins. $\endgroup$ Commented May 24, 2013 at 22:25
  • $\begingroup$ For everyone looking at this now I would like to emphasize that despite the excellent information in Todds answer that I have accepted and my own lengthy answer I still am interested in any experience and ideas of others about the topic... $\endgroup$ Commented May 27, 2013 at 21:46
  • $\begingroup$ Another use of the methods under discussion: (4626) $\endgroup$
    – Mr.Wizard
    Commented Jul 15, 2015 at 7:07

2 Answers 2

50
+200
$\begingroup$

You are correct about the behavior of computations done from preemptive links. So-called "preemptive evaluations" have been around since version 6. They are a class of evaluations that all work through the same mechanism. When Mathematica checks to see if a user interrupt has been requested, which it does at a high rate most of the time, it also looks to see if a preemptive evaluation is waiting to be serviced. If so, it temporarily interrupts the main evaluation and runs the preemptive evaluation. The kernel sets, and later restores, only a few small things to give the preemptive evaluation a "clean" environment in which to run. These include turning off tracing if it is on, and resetting $MessageList to {}. No attempt is made to "unblock" the values of symbols, or more complex resetting (like undoing InheritedBlock), and thus the evaluation sees almost the exact kernel state that the interrupted evaluation was seeing at the moment it was interrupted. It can very reasonably be argued that this behavior is a desirable feature, not a bug.

Off the top of my head, I can think of the following types of preemptive evaluations. I think this might be the complete list:

  • Evaluations sent on preemptive links. This includes Dynamic calls from the front end, and also some computations arriving from Java and .NET programs (J/Link and .NET/Link both have preemptive links back to the kernel). Users can also create their own preemptive links with the advanced (and undocumented) function MathLink`AddSharingLink[..., MathLink`AllowPreemptive -> True].

  • ScheduledTasks (and also the older Periodicals API in version 7 and earlier).

  • AsynchronousTasks (e.g., look up guide/AsynchronousTasks). These are part of the WolframLibrary interface, and you can probably expect to see more about them in the future.

One important fact about preemptive evaluations is that they themselves cannot be preempted. Therefore you should avoid doing anything time-consuming in a preemptive evaluation, because things like Dynamic will hang until your preemptive evaluation completes.

It is easy to envision scenarios where a program breaks horribly because it is preempted by another piece of code that modifies the kernel state in some unexpected way. In practice, though, I think such situations are extremely rare, for two reasons. First, most Mathematica programs don't rely on global state, and are inherently reentrant. Second, most user-level programs don't get called preemptively.

If the question is "How safe is InheritedBlock?", the answer, of course, depends on what you are doing inside the InheritedBlock. If you are altering the properties of a fundamental System symbol like Rule, then you have the potential for trouble even in the absence of preemptive computations. Significant parts of Mathematica are written in top-level Mathematica code, and you never know when that sort of code might execute. For example, if your program issues a message, a large number of lines of system-level Mathematica code will execute as the message cell is formatted, the link to the relevant documentation page is checked and constructed, etc. Some of that code might break if you change the behavior of Rule. There are many other examples of this.

The way to think about InheritedBlock is just as a means to ensure that changes to values and properties of symbols get undone no matter how the block is exited. The mere fact that the changes are guaranteed to be undone doesn't confer any fundamental safety on the changes you are making, because top-level code that doesn't appear in your own program can execute at unknown times for many reasons.

The larger question is whether the existence of preemptive evaluations means we must start writing all our programs to be preemption-safe (which is almost identical to the principle of thread-safety in languages with multiple threads). The majority of Mathematica programs are already preemption-safe. The majority of ones that aren't don't need to care because they will never be executed preemptively (you're not calling your program from a Dynamic that might interrupt a shift-enter evaluation of the same program). But for some types of programs, yes, you need to be careful about preemption-safety. J/Link is an example of a component of Mathematica that has many lines of top-level code and that needs to be bulletproof. Furthermore, it is "system-level" code that is not at all unlikely to be called from a preemptive evaluation. It relies on global state in the form of the MathLink to Java. Therefore J/Link uses PreemptProtect in a few places internally to ensure that it can't clobber itself by being called reentrantly at a sensitive time. Most user programs don't need that level of rigor, but it's available if you want to use it.

Addendum (reply to Albert Retey's comment)

I've been asked to elaborate on why preemptive evaluations inheriting the current execution state of the kernel might be considered a feature rather than a bug. Consider the possible sets of behaviors for preemptive evaluations in that regard. First, each preemptive evaluation might be cordoned off into a completely separate execution state, as if it were done in a fresh kernel. I think people would agree that for most uses this isn't desirable. At the other end of the spectrum, represented by the current behavior, the preemptive evaluation could inherit essentially everything about the current state, including the values of Block'ed variables. An intermediate type of behavior would have the original evaluation maintain some sort of "execution context" that contained changes to the kernel state that would be considered "local" to the evaluation. A preemptive evaluation that interrupted it would be given its own execution context that did not reflect those local changes. But what sort of things would be localized in the execution context? Perhaps the values of Block'ed or InheritedBlock'ed symbols. What about non-Blocked symbols? If my program sets x = 1, do I want preemptive code to see x as 1? Almost undoubtedly yes (if not, we are moving toward the cordoned-off extreme for preemptive evaluations that renders them practically useless). If yes, then what is so different about a Block'ed x? After all, I used Block instead of Module in my program precisely because I wanted its value to be non-local to the fragment of code in which it appeared. Let's say that I write some program-monitoring code that runs as a ScheduledTask, periodically interrupting the kernel to print some information about the state of the computation. I might want to use Block for some local variables specfically so that this monitoring code could see (and possibly alter) their values. When you call URLFetchAynchronous you provide a callback function that might execute preemptively. The examples on the tutorial/AsynchronousTasks help page use the global variables done and progress to communicate information into and out of this callback function. A programmer might prefer to use Block for these variables, rather than making them global.

If we want to have a notion of execution context, what other sorts of things do we want localized into it? The Protect/Unprotect state of a symbol, perhaps. Now you could start creating a long list of things that might or might not make sense to localize, and for every one there could be an argument about whether it's a useful or undesirable thing. Of course, just because it might be hard to make decisions doesn't mean an idea is a bad one, and I'm not attempting to make a forceful defense that the current behavior is necessarily perfect. I'm just saying that an argument in favor of the current behavior can be made. Personally, I think that this behavior is sensible and, at the very least, simple to understand.

Note that I haven't spoken about the technical feasibility of any of this. The current behavior exists in no small part because it is relatively straightforward to implement.

As for "action at a distance", and "decent code broken by completely unrelated code", I don't think any of that is going on here. In the original question, InheritedBlock was used to alter the properties of the fundamental System symbol Rule. One of the points I was making is that this operation is dangerous even without considering preemptive evaluations, because so much of the Mathematica system is written in top-level Mathematica. Every time you hit shift-enter, there is the potential for many, many lines of Mathematica code to execute that don't appear in your program. Lots of things could break if you mess around with built-in symbols, even if you undo your changes at the end. I know that Albert and the other Mathematica pros who hang out in this forum (and are still reading this long-winded exposition) are well aware of this. People can do all sorts of clever and useful things with tricks like this, but no one should be surprised if something breaks.

The one important concern I tried to point out about decent code breaking is not about someone else breaking your own code, but rather about making sure one's own programs are safe from clobbering themselves. It's true that preemptive computations have introduced a new dynamic (pun intended) into Mathematica, the ramifications of which have not been fully digested by most programmers. If your program relies on global state (like non-local variables, streams, MathLinks, etc.), and there is any chance that some part of your program could execute preemptively, you need to make sure that your program won't break if a second instance of it preempts a running instance. Either design things so that this won't break anything, or use PreemptProtect judiciously to protect only those segments that must be guarded from being preempted.

The vast majority of Mathematica programs are preemption-safe, and you have to put in a little work to think of one that isn't, or at least one that isn't deliberately created to break. A simple example is when a MathLink is in use. For example, if you do Install["addtwo"], then Dynamic[AddTwo[3,4], UpdateInterval->0.1], and then execute While[True, AddTwo[5,6]], things will break within a few seconds. You would need to use PreemptProtect[AddTwo[5,6]] in the While loop to prevent a preemptive call from the Dynamic from trying to use the same link while it was in the middle of being used from a call in the While loop.

$\endgroup$
7
  • $\begingroup$ Thanks a lot for that extensive answer, I think I'll have to digest it well before I can appreciate every detail. You are saying that it can be argued "that this behavior is a desirable feature, not a bug". Could you elaborate on that? I see some possible use cases, but none of them would justify to enable an "action at distance" which can make decent code be broken by completely unrelated code, even if that is "very unlikely" (sorry if that sounds provocative). Considering your experience in the field I think you will agree that things which can go wrong will go wrong... $\endgroup$ Commented May 25, 2013 at 16:15
  • $\begingroup$ What's the proper way to reply if it needs to be much longer than is allowed for a comment? Should I add to my original answer? $\endgroup$ Commented May 25, 2013 at 18:28
  • 1
    $\begingroup$ I'm not sure if @AlbertRetey will see this if you don't @ ping him. As to your question: I'd say add an update to your post marked as such and referring to albert's comment. $\endgroup$ Commented May 25, 2013 at 18:45
  • 1
    $\begingroup$ @ToddGayley: I don't consider myself an expert for proper behavior on this site, but it seems common that answers (and questions) are edited, probably with a remark so that people can see what has been added (I have seen extra sections starting with "Edit:" quite often). I'll appreciate either form... $\endgroup$ Commented May 25, 2013 at 21:55
  • 1
    $\begingroup$ @telefunkenvf14: I'm not sure how Evaluate would help. Maybe you could elaborate. Hold, perhaps, but that just prevents anything from happening. ;-) $\endgroup$ Commented May 27, 2013 at 2:11
30
$\begingroup$

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 Dynamics 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 Dynamics 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.

$\endgroup$
6
  • $\begingroup$ I really appreciate the info and effort. In regard to your suggestion of using an extra kernel for potentially 'offensive code': Would you mind asking (and perhaps also answering) a question along the lines of 'how to properly configure/use' such a setup? (or point me to a resource if I'm blindly missing something) I've long thought this kind of setup might be useful, but never really could figure out the appropriate way to get started. Potential pitfalls also concerned me. (its frustratingly easy to choose the wrong approach and end up wasting large blocks of time in MMA) $\endgroup$ Commented May 27, 2013 at 21:09
  • $\begingroup$ @telefunkenvf14: if interested why don't you just ask that question yourself? I only have played with using MathLink in that way so my experience with it is limited and I don't feel I can contribute very much that wouldn't be covered by the documentation. I honestly doubt that you will end up with more reliable code with such an approach and it would be the very last thing for me to try. It is most probably one of those cases where you end up wasting large blocks of time. I definitely have wasted my share the last few days and won't have any time to explore it in the near future... $\endgroup$ Commented May 27, 2013 at 21:43
  • $\begingroup$ I frequently hesitate to ask questions because of all the rules around these stack sites and the difficulty of posing questions in the right way (so as to attract a good answer). In particular, what qualifies as a duplicate question has always seemed rather ambiguous, especially because (1) so many ways to formulate a solution to any given issue in MMA and (2) such a broad list of fields MMA is used in. I'm sure I'll get around to asking the question eventually... $\endgroup$ Commented May 27, 2013 at 22:14
  • 1
    $\begingroup$ @telefunkenvf14: I think I understand what you try to say. My experience on this particular site is that people will be very helpful to improve your question in case that's necessary, especially if the topic seems to be of some interest, which I think is certainly the case for your question. $\endgroup$ Commented May 27, 2013 at 22:52
  • $\begingroup$ Superb answer, +1! @telefunkenvf14 please don't be put off by the rules. Really they exist just to keep the site somewhat orderly, and a good question will certainly not be closed on a technicality. I agree with you though that asking good questions is difficult (and I haven't asked any so far), but if the site is working properly, that shouldn't be because of the rules. $\endgroup$ Commented May 28, 2013 at 10:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.