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

Torsten Rennett Torsten at Rennett.de
Mon Feb 17 17:25:02 UTC 2003


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




More information about the tuxCPProgramming mailing list