When a GSAPI client tries to stop GS by returning a negative value from the polling callback function, GS enters an infinite loop handling cascading "interrupt" exceptions (assuming the normal user_errors == 0 parameter to gsapi_run_string_*()). The problem exists, but (given the default error handler) is not apparent, in TRUNK rev 6909 and earlier. It surfaced approx. at TRUNK rev 6914, after the recent changes in stdio handling. Because of it, GSView is hardly usable with current GS (after opening a file, one needs a Ctrl+Alt+Del to close it). To reproduce the error, open a PS/EPS file in GSView and close the GSView window by clicking the "X" button. With many files, GSView waits forever for the PostScript interpreter to quit. GSView is otherwise responsive; typing "M" shows GS's output, which looks like: "GSview 4.6 2004-01-11 AFPL Ghostscript SVN PRE-RELEASE 8.55 (2006-05-20) Copyright (C) 2006 artofcode LLC, Benicia, CA. All rights reserved. This software comes with NO WARRANTY: see the file PUBLIC for details. Displaying non DSC file " ... "/gs/lib/showpage.ps Unrecoverable error: interrupt in interrupt Operand stack: --nostringval-- false Unrecoverable error: interrupt in interrupt Operand stack: --nostringval-- false Unrecoverable error: interrupt in interrupt Operand stack: --nostringval-- false Unrecoverable error: interrupt in interrupt ..." (At some point, "interrupt" alternates with "execstackoverflow".) This error happens with most files in examples\, and even with lib\showpage.ps. The error cannot be reproduced with GS alone, even with "-B", because it is strictly connected to stopping the interpreter via the polling callback. Details --- - The interrupt is detected in interp.c::interp() (rev 6912) line #1647 "set_code_on_interrupt(imemory, &code);". - interp() returns code == e_interrupt to interp.c::gs_call_interp() line #485. - Given the normal user_errors == 0, gs_call_interp() reflects the error back at PostScript level, and the normal PS error handler starts. - interp() is called again, and the error handler attempts to print an error message, dump the stacks and then quit. - The error handler is long enough for ticks_left to reach 0. - When this happens, the polling callback is called and returns < 0 again (the GSAPI client still wants the interpreter to stop!). - A new interrupt is detected (while handling the old one), so the interrupt handler is reentered. - This whole process is repeated forever. Why this didn't happen in rev 6909 and earlier --- - Each time the error handler was writing something to stdout, interp() was exited with code == e_NeedStdout, then entered again. - When interp() reenters, ticks_left is reinitialized to 100. This is a step that cannot happen in rev 6914 and later. - Between 2 consecutive e_NeedStdout requests, the default error handler was executing too little operations for ticks_left to reach 0, so the error handler was essentially executed without checking for interrupts. This allowed it to finish normally and quit. A different error handler may make this problem happen even with GS revisions before 6909. Fix and workaround --- I don't think processing e_interrupt the same way as normal PS errors (obey user_errors, etc.) is a good idea; afterall, the GSAPI client requests the interpreter to abort, and the error handler may do anything, even attempt to continue the PS program indefinitely. The simplest fix is to change interp.c::gs_call_interp() to return when it gets an e_interrupt, and this is what the suggested patch does. Disadvantage: the GSAPI client actually sees an e_interrupt (<0, but not among the special values previously listed in API.htm), but I don't think a GSAPI client is interested in printing error messages/ etc. after it requested an abort. I also thought to return as if an e_Quit happened, but then what to put into *perror_object? Of course, a workaround is for the GSAPI client to return < 0 only once, then return 0 until gsapi_run_string_*() returns. However, I don't think this is what API.htm intends to say.
Created attachment 2390 [details] Suggested fix. Return e_interrupt as soon as an interrupt is detected. An attempt to mention the e_interrupt in the docs is included. (There were some "<b></b>" missing in those paragraphs, too.)
assigning to ray for review. Looks ok to me.
I'll commit the patch soon because Ray is too busy.
Bumping priority for problems with a suggested patch.
Patch to HEAD : http://ghostscript.com/pipermail/gs-cvs/2007-January/007210.html