[LC++]Interrupt signals and streams - a bad mix?

Dr Mark H Phillips mark at austrics.com.au
Tue Feb 18 12:55:02 UTC 2003


Hi Torsten,

Your email has been very helpful and informative.  Thank you!

I have tried using the SA_RESTART flag and the problem appears
to go away.  So thank you.

However an experienced collegue has suggested that this is only
curing the symptom, not the cure.  He says that, although what you 
have written is perfectly correct, being able to use
^C to interrupt a system call should be a very rare event
indeed.  Why, then, am I able to interrupt a system call
time and time again?

He suggested I write a minimal program, attempting to reproduce
the problem.  I have tried this but I can't replicate the error.

My collegue suspects that somewhere in my code I am overwriting
illegal memory and that somehow this is showing up in the problems
I've been having.  Thus your solution fixes the symptom, but I should
be trying to cure the underlying cause.

Would you (and others) agree with my collegue's assesment?

Thanks,

Mark.


On Mon, 2003-02-17 at 19:52, Torsten Rennett wrote:
> Hi Mark,
> 
> here is some background info on your "problem" (which isn't really one,
> since it is standard Unix behavior).
> 
> Dr Mark H Phillips wrote:
> > I did a test on the cout stream near where the problem occurs
> > and it seems the ^C interrupt puts cout into a "failed state"!
> 
> That's right! If a process caught a signal while the process was blocked
> in a "slow" system call, the system call will be interrupted. The system
> call returns an error and 'errno' is set to 'EINTR'.
> 
> "Slow" system calls are those that can block forever and read/write
> operations from/to files, pipes, terminals, network devices, etc.
> usually fall into this category. 
> 
> 
> > This raises two questions.  Firstly, why should jumping to an
> > interrupt handler put cout into a failed state.  Secondly,
> > how can this problem be overcome, ie how can one ensure that
> > interrupt handling doesn't cause cout to fail?  Thirdly, if
> > it is possible for cout to fail, does this mean that we really
> > should be putting tests for failure throughout our code?!!  This
> > would be a huge amount of work and very messy?!!!
> 
> The behavior described above is true for the early Unix systems and on
> those systems you really have to check the return code of all "slow"
> system calls and act accordingly, if 'errno' is set to 'EINTR'!
> 
> Nowadays all modern Unix systems allow for an automatic restart of
> certain interrupted system calls (firstly introduced in 4.2BSD).
> 
> I do not have the time to explain all the details here, but I recommend
> to read chapter 10 (Signals) of the bible on this topic:
> 
>    W. Richard Stevens: "Advanced Programming in the Unix Environment"
>    Addison-Wesley Professional Computing Series
> 
> Here is a (very) short summary concerning your "problem":
> 
>    R.Stevens: AdvProg p276f: automatic restart of interrupted system
> calls
> 	SysV never restarts interrupted system calls by default.
> 	4.2BSD introduced the automatic restarting of certain interrupted
> 	system calls.  4.3BSD allows to disable this on a per-signal basis.
> 	sigaction(2) can be used with the SA_RESTART option to specify that
> 	system calls should be restarted. This works equivalently for SysV and
> 	BSD.
> 
>    Linux man-page signal(2): resetting the signal handler to SIG_DFL
> 	The original Unix signal(2) would reset the handler to SIG_DFL, and
> 	System V (and the Linux kernel and libc4,5) does the same.
> 	BSD does not reset the handler, but blocks new instances of this signal
> 	from occurring during a call of the handler.  The glibc2 library
> 	follows the BSD behaviour.
> 
>    Linux man-page signal(2):
> 	Trying to change the semantics of signal(2) using defines and includes
> 	is not a good idea. It is better to avoid signal(2) altogether, and use
> 	sigaction(2) instead.
> 
> There are also some other areas which can cause problems when working
> with signals, especially when you want to write your programs portable:
>   - reliable vs unreliable signals
>   - SIGCHLD vs SIGCLD (different semantics on differnt Unix systems)
>   - (... other?)
> 
> It's probably a good thing to write a wrapper function which hides some
> of the messy details. Something like the following:
> 
> ---------------------- ibr_signal.h -----------------------------------
> /*
>    Purpose: A portable and reliable signal(2) function which has the
> same
>             semantics on Unix SysV and BSD systems.
> 	    Non-Unix systems are supported (signal() conforms to ANSI C), but
> 	    then the exact semantics may differ on different platforms.
> */
> 
> #ifdef HAVE_CONFIG_H
> #  include <config.h>		/* RETSIGTYPE, IBR_HAVE_siginfo_t */
> #endif /* HAVE_CONFIG_H */
> #include <signal.h>
> 
> #ifdef __cplusplus
> #  define EXTERN_C	  extern "C"
> #  define EXTERN_C_BEGIN  extern "C" {
> #  define EXTERN_C_END    }
> #else
> #  define EXTERN_C
> #  define EXTERN_C_BEGIN
> #  define EXTERN_C_END
> #endif /* __cplusplus */
> 
> 
> typedef RETSIGTYPE (*ibr_sighandler)(int signo);
> 
> /* This function extends and combines the functions 'signal()' and
>    'signal_intr()' presented in "R.Stevens: AdvProg p298f".
>    signo	signalno to be caught
>    fct		signal handler to be called
>    restartFlag	1: SysCalls interrupted by this signal are automatically
> 		   restarted.
> 		0: Do not automatically restart interrupted SysCalls.
> 		   Interrupted SysCall will return with an error and set
> 		   'errno=EINTR'.
>  */
> EXTERN_C ibr_sighandler ibr_signal(int signo, ibr_sighandler fct,
> 				   int restartFlag);
> 
> #ifdef IBR_HAVE_siginfo_t
> typedef void (*ibr_sigactfct)(int signo, siginfo_t *pSiginfo, void *);
> 
> /* Same as 'ibr_signal()' but with passing the 'struct siginfo' (=
> siginfo_t)
>    as pointer in the 2nd arg to the signal handler (note the new type
>    'ibr_sigactfct' of the signal handler).
>  */
> EXTERN_C ibr_sigactfct ibr_sigact(int signo, ibr_sigactfct fct, int
> restartFlag);
> #endif /* IBR_HAVE_siginfo_t */
> 
> /* Return string describing signal 'signo' in a portable way.
>  */
> EXTERN_C const char *const ibr_signo2str(int signo);
> 
> 
> ---------------------- ibr_signal.c -----------------------------------
> #include "ibr_signal.h"
> 
> 
> ibr_sighandler ibr_signal(int signo, ibr_sighandler fct, int
> restartFlag)
> {
> #ifdef __unix__
>   struct sigaction act,
> 		   oldact;
> 
>   act.sa_handler = fct;
>   sigemptyset(&act.sa_mask);
>   act.sa_flags = 0;
>   if (signo == SIGALRM  ||  restartFlag == 0)
>   {
> #ifdef SA_INTERRUPT
>     act.sa_flags |= SA_INTERRUPT;	/* SunOS */
> #endif
>   }
>   else
>   {	/* restartFlag == 1 */
> #ifdef SA_RESTART
>     act.sa_flags |= SA_RESTART;		/* SVR4, 4.3+BSD */
> #endif
>   }
>   if (signo == SIGCHLD)
>     act.sa_flags |= SA_NOCLDSTOP;
> 
>   if (sigaction(signo, &act, &oldact) < 0)
>     return SIG_ERR;
> 
>   return oldact.sa_handler;
> 
> #else  /* __unix__ */
> 
>   restartFlag = signo;	/* makes the compiler happy ... */
>   return signal(signo, fct);
> #endif /* __unix__ */
> }
> 
> 
> #ifdef IBR_HAVE_siginfo_t
> ibr_sigactfct ibr_sigact(int signo, ibr_sigactfct fct, int restartFlag)
> {
>   struct sigaction act,
> 		   oldact;
> 
>   act.sa_sigaction = fct;
>   sigemptyset(&act.sa_mask);
>   act.sa_flags = 0;
>   if (signo == SIGALRM  ||  restartFlag == 0)
>   {
> #ifdef SA_INTERRUPT
>     act.sa_flags |= SA_INTERRUPT;	/* SunOS */
> #endif
>   }
>   else
>   {	/* restartFlag == 1 */
> #ifdef SA_RESTART
>     act.sa_flags |= SA_RESTART;		/* SVR4, 4.3+BSD */
> #endif
>   }
>   if (signo == SIGCHLD)
>     act.sa_flags |= SA_NOCLDSTOP;
> 
>   act.sa_flags |= SA_SIGINFO;		/* SVR4 */
> 
>   if (sigaction(signo, &act, &oldact) < 0)
>     return (ibr_sigactfct)SIG_ERR;
> 
>   return oldact.sa_sigaction;
> }
> #endif /* IBR_HAVE_siginfo_t */
> 
> 
> #if !defined(SYS_SIGLIST_DECLARED)
> #  if defined(__linux__)
>      extern const char *const sys_siglist[_NSIG];
> #  else  /* __linux__ */
> #    include <stdio.h>		/* sprintf() */
> #  endif /* __linux__ */
> #endif /* SYS_SIGLIST_DECLARED */
> 
> const char *const ibr_signo2str(int signo)
> {
> #if defined(SYS_SIGLIST_DECLARED)  ||  defined(__linux__)
>   return sys_siglist[signo];
> #else  /* SYS_SIGLIST_DECLARED || __linux__ */
>   static char sigstr[10];
>   sprintf(sigstr, "%d", signo);
>   return sigstr;
> #endif /* SYS_SIGLIST_DECLARED || __linux__ */
> }
> 
> -----------------------------------------------------------------------
> NOTE: This code fragment will probably not work "out-of-the-box"!
>       Adapt to your needs!
> -----------------------------------------------------------------------
> 
> 
> I hope this is of some help, good luck,
> Torsten
> 
> --
> Ingenieurbuero RENNETT      -- innovative Software-Entwicklung --
> Torsten Rennett              
> Ludwig-Thoma-Weg 14         E-Mail:     mailto:Torsten at Rennett.de
> D-85551 Heimstetten         Telefon:              +49-89-90480538
> 
> _______________________________________________
> This is the Linux C++ Programming List
> : http://lists.linux.org.au/listinfo/tuxcpprogramming List
-- 
Dr Mark H Phillips
Research Analyst (Mathematician)

AUSTRICS - smarter scheduling solutions - www.austrics.com

Level 2, 50 Pirie Street, Adelaide SA 5000, Australia
Phone +61 8 8226 9850
Fax   +61 8 8231 4821
Email mark at austrics.com.au




More information about the tuxCPProgramming mailing list