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