[comp.sys.ibm.pc] signal

dhesi@bsu-cs.UUCP (Rahul Dhesi) (08/20/87)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	signal.c
#	signal.man
# This will give you the C source and the manual for an implementation
# of signal() for Turbo C.  Only SIGINT is currently recognized.
#
# This archive created: Thu Aug 20 00:29:27 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'signal.c'" '(2380 characters)'
if test -f 'signal.c'
then
	echo shar: "will not over-write existing file 'signal.c'"
else
sed 's/^X//' << \SHAR_EOF > 'signal.c'
X/*
Xsignal.c - a signal package for Turbo C 1.0.
X
XThis program and accompanying documentation, henceforth collectively known as
X"this software", are copyrighted thus:  (C) Copyright 1987 Rahul Dhesi, all
Xrights reserved.  This software may be used and distributed in any way
Xwhatsoever, whether commercial or noncommercial, with the following
Xexceptions:  (a) this paragraph and this copyright notice must be included
Xunchanged;  (b) it is forbidden to copy this software for distribution as
Xpart of any collection over which a compilation copyright is claimed.
XNotwithstanding the above, there are no restrictions of any kind on the
Xmachine-readable object code produced by compiling this program with a C
Xcompiler.
X
X                                   -- Rahul Dhesi 1987/08/08
X*/
X
X/*
XNote:  When assigning an address to the variable `handler', a race
Xcondition can theoretically occur if a user interrupt occurs during
Xthe assignment.  However, current versions of MS-DOS cause a user
Xinterrupt to take effect only during system calls, so in practice it
Xwon't happen.
X*/
X
X/* The following include files are already supplied with Turbo C */
X#include <signal.h>
X#include <errno.h>
X
X/*
XNOTE:  For best results, edit your <signal.h> file, provided with
XTurbo C, to include the following declaration at the end:
X     int (*_Cdecl signal (int sig, int (*action)())) ();
X*/
X
Xvoid ctrlbrk (int (*fptr)(void));
X
Xstatic int (*handler)() = SIG_DFL;
Xint main_handler();
X
Xint (*_Cdecl signal (int sig, int (*action) ())) ()
X{
X    int (*retval) ();
X    static int installed = 0;
X    if (sig != SIGINT) {
X        errno = EINVAL;
X        return SIG_ERR;                 /* error return */
X    }
X    if (!installed) {
X        ctrlbrk (main_handler);         /* ctrlbrk() is in Turbo C library */
X        installed = 1;
X    }
X
X    retval = handler;
X    handler = action;
X    return (retval);
X}
X
X#define ABORT_PGM   0
X#define RESUME_PGM  1
X
X/* Every keyboard interrupt is handled here first */
Xint main_handler()
X{
X    int (*old_handler)(void);
X
X    if (handler == SIG_IGN)
X        return (RESUME_PGM);
X    if (handler == SIG_DFL)
X        return (ABORT_PGM);
X    old_handler = handler;          /* Save user's handler address */
X    handler = SIG_DFL;              /* Reset handler, like System V does */
X    (*old_handler)();               /* call the user's handler */
X    return (RESUME_PGM);
X}
SHAR_EOF
fi
echo shar: "extracting 'signal.man'" '(2962 characters)'
if test -f 'signal.man'
then
	echo shar: "will not over-write existing file 'signal.man'"
else
sed 's/^X//' << \SHAR_EOF > 'signal.man'
X--------------------------------------------------------------------
Xsignal
X--------------------------------------------------------------------
X
XNAME
X
X     signal -- implements trapping of user interrupts
X
XUSAGE
X
X     int (*signal (int sig, int (*action))) ()
X
XPROTOTYPE IN
X
X     signal.h /* but you must add it yourself */
X
XDESCRIPTION
X
XThe function `signal' is used to establish an action routine for servicing a
Xuser interrupt.  Under MS-DOS, typing Ctrl-C causes a user interrupt.  (On
XIBM-compatible systems the Ctrl-Break key will cause the same user
Xinterrupt, and may work when a Ctrl-C fails.)
X
XThe first argument to `signal', sig, is a number identifying the signal for
Xwhich an action is established.  The value for sig must be SIGINT.
X
XThe second parameter, `action', specifies the action to be taken when SIGINT
Xoccurs.  It is either the name of a user-defined function or one of the
Xconstants SIG_DFL (default, which aborts the process) or SIG_IGN (ignore,
Xwhich causes SIGINT to be ignored;  however, in the case of MS-DOS, a '^C'
Xis always echoed to the controlling device).
X
XThere are no special restrictions on what the action function may do,
Xincluding continuing execution, returning normally or with a longjmp
Xstatement, or terminating executing with exit().  If the function returns
Xnormally, execution continues where it was interrupted by the occurrence of
Xthe SIGINT signal, except that any MS-DOS system call that was in progress
Xmay need to be restarted.
X
XBefore the user-defined function is called, the action on the event is
Xrestored to be SIG_DFL.  Thus a second SIGINT event will cause termination
Xof the process unless the signal handler takes care to restore its action
Xwith a call to `signal'.  Even if it does so, however, a race condition
Xstill exists and two SIGINTs occurring rapidly will terminate the process.
X(But nobody types that fast.)
X
XRETURN VALUE
X
XIf the specified signal is not SIGINT, the return value is SIG_ERR, which is
Xdefined in `signal.h', and errno is set to EINVAL.  Otherwise the return
Xvalue is the value of `action' that was supplied to `signal' in the most
Xrecent legal call, or SIG_DFL if `signal' has never been called.
X
XNOTE
X
XA header file `signal.h' is already provided with Turbo C.  Revise it by
Xadding the following line at the end:
X
X     int (*_Cdecl signal (int sig, int (*action)())) ();  /* R.D */
X
XAUTHOR
X
X     Rahul Dhesi
X
XEXAMPLE
X
X#include <stdio.h>
X#include <signal.h>
X
Xint my_handler()
X{
X   signal (SIGINT, SIG_IGN);      /* ignore signals for now */
X   fflush (stdout);
X   printf ("\n<Break>\n");
X   fflush (stdout);
X   signal (SIGINT, my_handler);   /* reinstall signal handler */
X}
X
Xunsigned _stklen = 4000;          /* reserve stack */
X
Xmain()
X{
X   int c;
X   signal (SIGINT, my_handler);
X   printf ("Control C is being trapped\n");
X   printf ("Type Q then <return> to exit\n");
X   for (;;) {
X      c = getchar();
X      if (c == 'q' || c == 'Q' || c == EOF)
X         break;
X   }
X}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

darrylo@hpsrla.HP.COM (Darryl Okahata) (08/24/87)

In comp.sys.ibm.pc, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:

> #! /bin/sh
> # This is a shell archive, meaning:
> # 1. Remove everything above the #! /bin/sh line.
> # 2. Save the resulting text in a file.
> # 3. Execute the file with /bin/sh (not csh) to create:
> #	signal.c
> #	signal.man
> # This will give you the C source and the manual for an implementation
> # of signal() for Turbo C.  Only SIGINT is currently recognized.
> #
  [ ... ]

     Thanks to Rahul Dhesi for writing this!  However, there are little
"gotchas" that one has to be careful of:

     1. Any routine called through the handler set up by the ctrlbreak()
	call uses the stack given to it by DOS.  The handler does not set
	up a stack before calling the specified routine.  It *appears* that
	the stack used is the one that was being used when the DOS function
	that was called was interrupted.  Because of this, there may or
	may not be enough room on the stack for the ctrl-break handler to
	properly run (there may not be enough room for local variables,
	recursive calls, etc.).

     2. As the stack that is being used may not be the one that Turbo C
	uses, the Turbo C program may terminate if stack overflow checking
	is turned on.

In my case, a TSR (Chris Dunford's PCED) was doing some processing when I
hit CTRL-C.  Because PCED sets up a local stack and because I had turned on
stack overflow checking in my Turbo C program, the Turbo C program was
aborting with a "stack overflow" error upon entry to main_handler().

     -- Darryl Okahata
	UUCP: { hplabs!hpcea, hpfcla }!hpsrla!darrylo
	CompuServe: 75206,3074

Disclaimer: the above is the author's personal opinion and is not the
opinion or policy of his employer or of the little green men that
have been following him all day.

dhesi@bsu-cs.UUCP (Rahul Dhesi) (08/26/87)

In article <2670002@hpsrla.HP.COM> darrylo@hpsrla.HP.COM (Darryl Okahata) 
writes about my signal() for Turbo C:
>     Thanks to Rahul Dhesi for writing this!  However, there are little
>"gotchas" that one has to be careful of:
>
>     1. Any routine called through the handler set up by the ctrlbreak()
>	call uses the stack given to it by DOS.  The handler does not set
>	up a stack before calling the specified routine.  It *appears* that
>	the stack used is the one that was being used when the DOS function
>	that was called was interrupted.

When I tested the signal() handler code, I did run out of stack space.
Then I initialized _stklen to a bigger value and the program ran
correctly.  So any shortage of stack space is occurring simply because
the Turbo C stack is not big enough.  The default stack, if you don't
create _stklen, is only 128 bytes, which is peanuts.

To set up the stack (this is not given in the Turbo C manual but was on
a readme file on the disk) just declare a global variable thus:

     unsigned _stklen = 3000;  /* set up 3000 byte stack */

Also note that I believe that the small model start-up code for Turbo C
does not properly handle _stklen to set a bigger stack.  I changed and
recompiled the startup module for the small memory model to fix this
apparent bug.  I made the following changes in C0.ASM:

1.   Search for the line

          _stklenOK     label   near

     and after it insert the statement:

			mov  bx,di

2.   The following statements occur in two places in C0.ASM:

			cmp  bp,di
			ja   ExcessOfMemory

     Comment these out the FIRST time they occur.


For the small model startup code at least, the above changes seemed to
solve two problems:  (a) It seemed to me that _stklen wasn't working;
(b) Even when I thought I had _stklen working, the startup code was
allocating as much stack space as was available, even if you specified
less.  The above is only for the small model code, and only for version
1.0 of Turbo C (but note that all versions of Turbo C are called
version 1.0, so the startup code for your version 1.0 could be
different from mine).

I have a lot of weasel words here because I didn't exhaustively test
to see if what I thought happened really did happen.  But I'm happy
with the results of the changes I made, and signal() works perfectly
now.
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

ralf@b.gp.cs.cmu.edu (Ralf Brown) (08/28/87)

In article <1059@bsu-cs.UUCP> dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
>In article <2670002@hpsrla.HP.COM> darrylo@hpsrla.HP.COM (Darryl Okahata) 
>writes about my signal() for Turbo C:
>>	[...]
>
>When I tested the signal() handler code, I did run out of stack space.
>Then I initialized _stklen to a bigger value and the program ran
>correctly.  So any shortage of stack space is occurring simply because
>the Turbo C stack is not big enough.  The default stack, if you don't
>create _stklen, is only 128 bytes, which is peanuts.
NO!  The default stack length is 4096 bytes (there is a module in the 
library whose sole purpose is to define _stklen).  The 128 bytes is the
initial startup stack, which gets moved as soon as C0.ASM figures out
where to place it.  No matter what value you give _stklen, the startup
stack stays 128 bytes.

>Also note that I believe that the small model start-up code for Turbo C
>does not properly handle _stklen to set a bigger stack.  I changed and
>recompiled the startup module for the small memory model to fix this
>apparent bug.  I made the following changes in C0.ASM:

Actually, the bug is in determining how much memory to allocate to the
program.  The problem is that when less than 64K is available in tiny
model, the code does not use all available memory, but only 
CODE+DATA+minSTACK, where minSTACK is 256 bytes.  My fix for this
(posted some time ago) is to change the two lines in C0.ASM between
the two "ja ExcessOfMemory" statements to
	mov	di,bp
	cmp	bp,bx
DI contains the amount to allocate, BP contains the amount available, and BX
contains the minimum amount needed.  (Which is too little, because it only
allocates 256 bytes for the stack).

Note that this patch can also be applied to the .COM file, as the 
instructions are the same size as those they replace.

>-- 
>Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

-- 
-=-=-=-=-=-=-=-= {harvard,seismo,ucbvax}!b.gp.cs.cmu.edu!ralf =-=-=-=-=-=-=-=-
ARPAnet: RALF@B.GP.CS.CMU.EDU            BITnet: RALF%B.GP.CS.CMU.EDU@CMUCCVMA
AT&Tnet: (412) 268-3053 (school)         FIDOnet: Ralf Brown at 129/31
	        DISCLAIMER?  Who ever said I claimed anything? 
"I do not fear computers.  I fear the lack of them..." -- Isaac Asimov

darrylo@hpsrlc.HP.COM (Darryl Okahata) (08/31/87)

In comp.sys.ibm.pc, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:

> In article <2670002@hpsrla.HP.COM> darrylo@hpsrla.HP.COM (Darryl Okahata) 
> writes about my signal() for Turbo C:
> >     Thanks to Rahul Dhesi for writing this!  However, there are little
> >"gotchas" that one has to be careful of:
> >
> >     1. Any routine called through the handler set up by the ctrlbreak()
> >	call uses the stack given to it by DOS.  The handler does not set
> >	up a stack before calling the specified routine.  It *appears* that
> >	the stack used is the one that was being used when the DOS function
> >	that was called was interrupted.
> 
> When I tested the signal() handler code, I did run out of stack space.
> Then I initialized _stklen to a bigger value and the program ran
> correctly.  So any shortage of stack space is occurring simply because
> the Turbo C stack is not big enough.  The default stack, if you don't
> create _stklen, is only 128 bytes, which is peanuts.

     [ ... ]

> To set up the stack (this is not given in the Turbo C manual but was on
> Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi
> ----------

     When main_handler() is entered, the stack that the routine uses MAY
NOT be that of the Turbo C program.  With Chris Dunford's PCED (a TSR)
loaded, I'm using a Turbo C routine to read a line from the console via DOS
function 0x09 or 0x0a (I can't remember which -- it is the function to read
a complete line from the console).  If I hit ^C while performing the DOS
call (which is intercepted by PCED), main_handler() gets the stack used by
PCED; not only is the offset different, but the stack SEGMENT is totally
different.  main_handler() may or may not have enough stack room in which
to work.  Everything may work fine, or your system may crash.

     I think the bug here is in Turbo C -- the library routines should
handle messy details like making sure that the stack is correct.  I don't
know any easy way around this.  In fact, the only way that I can think of
to get around this is to write a short assembly routine to set up a local
playground (stack) before calling main_handler().  Of course, the problem
with this is that main_handler() will have to eventually return.

     -- Darryl Okahata
	{hplabs!hpcea!, hpfcla!} hpsrla!darrylo
	CompuServe: 75206,3074

Disclaimer: the above is the author's personal opinion and is not the
opinion or policy of his employer or of the little green men that
have been following him all day.

dhesi@bsu-cs.UUCP (09/02/87)

In article <3320049@hpsrlc.HP.COM> darrylo@hpsrlc.HP.COM (Darryl Okahata) 
writes:
[about problems caused by Turbo C ctrlbrk() apparently not switching to
Turbo C's own stack]:
>If I hit ^C while performing the DOS
>call (which is intercepted by PCED), main_handler() gets the stack used by
>PCED; not only is the offset different, but the stack SEGMENT is totally
>different.  main_handler() may or may not have enough stack room in which
>to work.  Everything may work fine, or your system may crash.
>
>     I think the bug here is in Turbo C -- the library routines should
>handle messy details like making sure that the stack is correct.

I looked at Microsoft's official guide to MS-DOS, written by Ray
Duncan.  The description for the control-C trapping system call, which
Turbo C's ctrlbrk() function must be using, says nothing about which
stack is used.  Can anybody check any other documentation about this?

But the description given by Duncan does say that the control-C handler
can do anything that the rest of the program does.  This does seem to
imply that the control-C handler is not executing on an interrupt
stack, but is using the original application program's stack.  This
would be consistent with Microsoft attempting (albeit rather weakly) to
provide facilities similar to those provided by UNIX.

I wonder if the bug is in MS-DOS?  I heard somebody say somewhere that
the signal() function provided by one of the C compilers (Lattice's or
Microsoft's) uses some undocumented features of MS-DOS.  Why would it
do so, unless it were necessary?  Why would it be necessary, if MS-DOS
sets up the stack correctly before passing control to the handler?

On the other hand, could it be that PCED is doing something funny with
the stack?  It does bypass MS-DOS, so it's hard to guarantee that it
will coexist with all generic MS-DOS software.
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

darrylo@hpsrlc.HP.COM (Darryl Okahata) (09/03/87)

In comp.sys.ibm.pc, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:

> In article <3320049@hpsrlc.HP.COM> darrylo@hpsrlc.HP.COM (Darryl Okahata) 
> writes:
> [about problems caused by Turbo C ctrlbrk() apparently not switching to
> Turbo C's own stack]:
> >If I hit ^C while performing the DOS
> >call (which is intercepted by PCED), main_handler() gets the stack used by
> >PCED; not only is the offset different, but the stack SEGMENT is totally
> >different.  main_handler() may or may not have enough stack room in which
> >to work.  Everything may work fine, or your system may crash.
> >
> >     I think the bug here is in Turbo C -- the library routines should
> >handle messy details like making sure that the stack is correct.
> 
     [ ... ]
> 
> But the description given by Duncan does say that the control-C handler
> can do anything that the rest of the program does.  This does seem to
     [ ... ]
> 
> I wonder if the bug is in MS-DOS?  I heard somebody say somewhere that
     [ ... ]
> 
> On the other hand, could it be that PCED is doing something funny with
> the stack?  It does bypass MS-DOS, so it's hard to guarantee that it
> will coexist with all generic MS-DOS software.
> -- 
> Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi
> ----------

     Hmmm.  Come to think of it, the bug could be in PCED.  Originally, I
had set a breakpoint at the entry to main_handler(), and noticed that the
stack given to main_handler() was that of PCED.  I'll look into it more
this weekend.

     -- Darryl Okahata
	{hplabs!hpcea!, hpfcla!} hpsrla!darrylo
	CompuServe: 75206,3074

Disclaimer: the above is the author's personal opinion and is not the
opinion or policy of his employer or of the little green men that
have been following him all day.