Re: CheckAbort inside MathLink functions?
- To: mathgroup at smc.vnet.net
- Subject: [mg118138] Re: CheckAbort inside MathLink functions?
- From: Todd Gayley <tgayley at wolfram.com>
- Date: Thu, 14 Apr 2011 04:48:26 -0400 (EDT)
- References: <000501cbf98b$537aacb0$bc0a440a@lehin>
At 10:31 PM 4/12/2011, Alexey Popkov wrote:
>Hello,
>
>It seems that that such MathLink functions as LinkWrite and LinkRead have
>something like its own internal CheckAbort that absorbs user-generated
>aborts, and does not propagate them further.
>
>This can be easily shown with LinkRead:
>
>link = LinkLaunch[First[$CommandLine] <> " -mathlink"];
>LinkRead[link];
>LinkWrite[link, Unevaluated[Pause[100]]];
>CheckAbort[AbortProtect[Print[LinkRead[link]]], Print["!!"]]
>
>If we press Alt+. after evaluating the above code we get only the following
>output:
>
>During evaluation of In[6]:= ReturnPacket[$Aborted]
>
>But Print["!!"] is not evaluated although we have generated Abort[] exactly
>inside CheckAbort.
>
>One can see that the abort we made by pressing Alt+. was absorbed by
>LinkRead.
>
>My problem is that it breaks my own flow control of evaluation based on
>CheckAbort.
>
>Is there a way to intercept aborts absorbed by such functions as LinkRead
>and LinkWrite for having ability to catch user-generated aborts?
Alexey,
The behavior you describe is a feature, and writing correct
Mathematica programs that call external programs via MathLink would
be difficult without it. The behavior you have discovered is this: If
a user abort is detected while the kernel is blocking in a LinkRead
call, the abort is not handled like a normal abort request, but
instead it is propagated to the external program (specifically, a
MathLink MLAbortMessage is sent over the link that the kernel is reading from).
To see why this is useful, consider the following pseudocode user
program that calls an external program via MathLink:
LinkWrite[link, something];
While[haventReceivedFinalPacket,
LinkRead[link]
]
Consider what happens if a user of this program hits Alt-. to trigger
an abort while it is running, say between calls to LinkRead. The
kernel will exit from the While loop, leaving unread packets on the
link. The external program is now effectively broken, because future
attempts to use it will end up reading packets left over from the
previous call.
The correct way to fix this is to wrap the entire operation in
AbortProtect, making it atomic. Once you start an transaction with
the external program, there is no way the user can inadvertently
prevent it from finishing by requesting an abort at an inopportune time.
AbortProtect[
LinkWrite[link, something];
While[haventReceivedFinalPacket,
LinkRead[link]
]
]
We have now fixed a bug in our program by making sure it won't break
horribly if a user tries to abort it. But what we really want is to
honor the user's request for an abort. As we've just discussed, we
cannot do this by simply backing out of a LinkRead. The sensible
thing is for LinkRead to forward the abort request to the external
program in the hopes that the external program will pay attention to
it. After all, the external program "owns" the computation at this
point. MathLink programs have a means of detecting abort messages
arriving over the link, and can clean up and return an appropriate result.
For all this to work properly, AbortProtect must not prevent LinkRead
from forwarding the abort, and it does in fact behave this way.
In the case of your program, Alexey, this behavior is not "breaking
your flow control", it is absolutely required to avoid problems. When
you execute the program you showed, and hit Alt-. and get back
ReturnPacket[$Aborted], you are seeing the behavior I described
above. The abort is forwarded to the slave kernel, which aborts its
evaluation of Pause[100] and returns $Aborted. If you want to have
the Alt-. propagate all the way back up to abort the entire top-level
program that was executing, which is typically the right behavior,
what you need to do is look for $Aborted returning from the slave
kernel and then call Abort[] yourself:
AbortProtect[
LinkWrite[link, Unevaluated[Pause[100]]];
result = LinkRead[link];
If[result === ReturnPacket[$Aborted],
(* Re-fire the Abort, which will trigger as soon as the
AbortProtect block ends. *)
Abort[]
]
]
If you want to use CheckAbort to catch the re-fired Abort, that will work fine.
--Todd