[comp.sys.amiga.tech] Need help with exception handling

ng@pur-phy (Nicholas J. Giordano) (09/03/88)

About a month ago there was a discussion of how to catch signals
like ^C, etc.  An example program was posted to the net (see below),
and people seemed to agree that it was the right way to go about
things, although one must be careful not too take control away
from certain system routines, without letting them finish their work.

Well, I was playing with this last night, and I ran into a problem which
I don't understand.  The program below catches ^C, ^D, ^F, and ^E,
prints a little message, and then returns.  If a ^C was typed, a global
variable is set, and the program finishes.  There is a busy loop
in which nothing is done [while {}] for illustrative purposes,
and the program works fine when this loop is empty.  However, when I added some
code inside this loop, the program crashes (gurus).  The code
I added was a simple printf, and is indicated below.  Can somebody
tell me why there is a guru, and what I need to do to get rid of it??

Thanks.

Nick

NOTE: I have removed some of the comments from the original program.
Also, this is compiled with Manx 3.6a with 16 bit ints.

/* An illustration of how to write an Exception Handler */
/* Bill Kinnersley - IPHWK@MTSUNIX1.BITNET - 7/23/88    */
/* Compiled using Aztec 3.4a, 16-bit integers.          */
/* ^D, ^E, ^F will print a message, ^C will abort.      */

#include <stdio.h>
#include <exec/types.h>
#include <exec/tasks.h>
#include <libraries/dos.h>
#include <functions.h>

extern short Enable_Abort;
long aborted, cursigs, mysigs, oldsigs;

struct Task *mytask;

main() {
	int i = 0;

        install();
        Enable_Abort = 0;

/* Busy loop simulating something useful...             */
/* Note that each Exception DOES break into this loop.  */
/* The aborted flag is polled only because              */
/* I want to generate a clean exit from main(),         */
/* not try to call cleanup from inside the handler.     */

        while (!aborted) {
		printf("%d\n",++i);  <----------- Adding this brings the guru	
	}

        cleanup();
}

APTR oldcode;

install() {
        int face();

        mysigs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
                SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F;
        mytask = FindTask(0L);

        Forbid();
        oldcode = mytask->tc_ExceptCode;
        mytask->tc_ExceptCode = (APTR)face;
        Permit();
        oldsigs = SetExcept(mysigs, mysigs);
}

cleanup() {
/* Restore the previous Exception Handler (ROM-wack?) */
        Forbid();
        mytask->tc_ExceptCode = oldcode;
        Permit();
        SetExcept(0L,mysigs);
}

#asm
_face:
;the signals will be passed to me in d0
        move.l  d0,-(a7)
;restore a4, which points to Aztec's small data segment */
        public  _geta4
        jsr     _geta4
        move.l  (a7)+,_cursigs

        jsr     _hdlr

;return signals to the system so they can be reset
        move.l  _cursigs,d0
        rts
#endasm

hdlr() {
/* Note that even AmigaDOS stuff can be done from here */
        printf("Exception %lx   Control-",cursigs);
        if (cursigs & SIGBREAKF_CTRL_C) {printf("C\n"); aborted=1;}
        if (cursigs & SIGBREAKF_CTRL_D) printf("D\n");
        if (cursigs & SIGBREAKF_CTRL_E) printf("E\n");
        if (cursigs & SIGBREAKF_CTRL_F) printf("F\n");
}

dillon@CORY.BERKELEY.EDU (Matt Dillon) (09/04/88)

	This is an easy one!  The problem is that C.LIB is NOT REENTRANT.

	While the Amiga may be a multitasking machine, this does not mean 
that linked-in libraries for a given task are reentrant... the run-time
libraries certainly are (in most cases), but most of the routines in the 
link libraries are not.

	Specifically, STDIO is not reentrant.  Of the run-time libraries,
the DOS.LIBRARY is not reentrant for a SPECIFIC PROCESS... i.e. many 
different processes can call it simultaniously, but a given process cannot
be in the middle of a DOS command, interrupt itself, and send another
DOS command in the interrupt handler.

	So, your problem is two fold:  (1) STDIO is not reentrant and you
are interrupting a printf() with the printf() in the exception handler, and
(2) DOS is not reentrant on a per-process basis and STDIO calls DOS. 

					-Matt

:        while (!aborted) {
:		printf("%d\n",++i);  <----------- Adding this brings the guru	
:	}

:hdlr() {
:/* Note that even AmigaDOS stuff can be done from here */
:        printf("Exception %lx   Control-",cursigs);
:        if (cursigs & SIGBREAKF_CTRL_C) {printf("C\n"); aborted=1;}
:        if (cursigs & SIGBREAKF_CTRL_D) printf("D\n");
:        if (cursigs & SIGBREAKF_CTRL_E) printf("E\n");
:        if (cursigs & SIGBREAKF_CTRL_F) printf("F\n");
:}

	

vkr@osupyr.mast.ohio-state.edu (Vidhyanath K. Rao) (09/05/88)

I am taking a wild guess here. So bear with me if I am wrong.

Manx 'Chk_Abort()' routine munges the task signals before looking at the
Enable_Abort variable. Now Printf calls Chk_Abort(). This must be causing
the interaction.
The solution would be to change the Chk_Abort routine. I was going to ask
Manx  about this but left on my summer travels. Now I have got finish
some other work and may not do this for a while. If any of you get some
info on this, I will appreciate E-mail.
-Nath
vkr@osupyr.mast.ohio-state.edu