In this Question, Leonid explained that module variables can get garbage collected from a chain of asynchronous tasks unless their Temporary attribute is cleared either by calling ClearAll on them or by Holding them. This works for preserving a variable that refers to the function that creates asynchronous task. The next step is to create a variable that Catches a value Thrown inside an asynchronous task, but the same technique does not seem to work, as I illustrate below.
First, a synchronous version that works as I need. This has an internal synchronous task wrapped in a Catch. By default, the Catch returns a symbolic value that is Unique; that is the default, "OK" case. Internally to the Catch, the task bumps a state variable, checks that the state value is ok, prints it if so, throws it if not, and schedules the next iteration of the task up to a maximum state value. "Ok" in this contrived example means that state's value is less than 2; in a real application, it would be based on some unpredictable outcome, hence the whole reason for Catch. The following code implements the scheme above and works fine, printing 1 and then ERROR:
Module[{
state = 0,
runNextTask,
catchResult,
defaultCatchResult = Unique[]},
runNextTask = Function[
catchResult = Catch[
If[state < 4,
state = state + 1;
If[state < 2, Print[state], Throw[state]];
runNextTask[],
{0.10}];
defaultCatchResult];
If[catchResult =!= defaultCatchResult, Print["ERROR"]]];
runNextTask[]]
Now, I want to do the same asynchronously, attempting the following code, wherein the only differences to the synchronous versions are marked with comments:
Module[{
state = 0,
runNextTask,
catchResult,
defaultCatchResult = Unique[]},
(* the following device removes the "Temporary"
attributes from "runNextTask" and "catchResult." *)
ClearAll[runNextTask, catchResult];
runNextTask = Function[
catchResult = Catch[
If[state < 4,
(* the following invocation techniques make the
body here asynchronous. *)
StartScheduledTask@
CreateScheduledTask[
state = state + 1;
If[state < 2, Print[state], Throw[state]];
runNextTask[],
(* This is the amount of time with which to
separate asynchronous invocations. *)
{0.10}]];
defaultCatchResult];
If[catchResult =!= defaultCatchResult, Print["ERROR"]]];
runNextTask[]]
The problem, now, is that the Thrown value is not caught, as revealed in the message box:
1
Throw::nocatch: Uncaught Throw[2] returned to top level.
This isn't the end of the world, since at least the repeating chain of asynchronous tasks is interrupted and I can wrap the entire thing in another catch and make it behave well inside other code. But it's another case where I am uncomfortable proceeding when I don't understand what's going on. I expected one result and got another!
I will be grateful for advice, suggestions, solutions!
I'll be grateful for any advice, clues, solutions.
Functionhas a third parameter (apparently undocumented!). Wow! Big thanks again. – Reb.Cabin Apr 02 '12 at 01:01HoldAlland other attributes forFunction) actually is documented, but indeed rather scarcely. Also, in place of aFunction, you can use a pattern-defined symbol. All that matters is the lazy evaluation.Functionis just one way to achieve it. And I just noticed that because of lazy evaluation plus code-as-data, we may exchange chunks of unevaluated code between different evaluation stacks, which was the key for matching the stacks here. – Leonid Shifrin Apr 02 '12 at 01:08