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.