Re: Missing in sigaction
Re: Missing in sigaction
- Subject: Re: Missing in sigaction
- From: "Stephen J. Butler" <email@hidden>
- Date: Tue, 27 Mar 2012 23:03:59 -0500
Because I'm bored, I spent a couple hours investigating this. If there
is a bug (and I'm not convinced there is -- there's a lot I don't know
about signal handling) then it's a bug when a SIGUSR1 arrives while
another SIGUSR1 is executing. Take the attached code, for example.
First, I tried to not do anything you aren't supposed to do in the
signal handler. I was still seeing some dropped SIGUSR1s and missing
pids. After reading a bunch of man pages, headers, and IEEE specs, I
asked myself "What happens if I unblock SIGUSR1 while handling it?"
Ahh ha, success! For me at least the attached code always handles all
signals, and preserves si_pid. There is a race condition in that you
could run into the previous problem if your signal arrives before
sigprocmask() is called. Of course, now you have to be extra careful
that your handler is re-entrant.
My understanding is that blocked signals should be delivered when they
are unblocked (ie: after the handler returns). I don't know why they
aren't in this case.
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#define G_INFO_SIZE 32
uint32_t g_info_idx = 0;
typedef struct {
int si_signo; /* signal number */
int si_errno; /* errno association */
int si_code; /* signal code */
pid_t si_pid; /* sending process */
} g_info_t;
g_info_t g_info[ G_INFO_SIZE ];
int g_info_wfd;
sigset_t g_usr1_sigset;
void handle_sigs( int signal, struct __siginfo *info, void *uap )
{
uint8_t idx;
pid_t wpid;
switch (signal)
{
case SIGUSR1:
sigprocmask( SIG_UNBLOCK, &g_usr1_sigset, NULL );
idx = __sync_fetch_and_add( &g_info_idx, 1 ) % G_INFO_SIZE;
g_info[ idx ].si_signo = info->si_signo;
g_info[ idx ].si_errno = info->si_errno;
g_info[ idx ].si_code = info->si_code;
g_info[ idx ].si_pid = info->si_pid;
/* we can't do anything about errors here, just write
* and hope for the best */
write( g_info_wfd, &idx, 1 );
sleep( 2 );
break;
case SIGCHLD:
while ((wpid = waitpid( -1, NULL, WNOHANG )) > 0)
{
idx = __sync_fetch_and_add( &g_info_idx, 1 ) % G_INFO_SIZE;
g_info[ idx ].si_signo = SIGCHLD;
g_info[ idx ].si_errno = 0;
g_info[ idx ].si_code = 0;
g_info[ idx ].si_pid = wpid;
/* we can't do anything about errors here, just write
* and hope for the best */
write( g_info_wfd, &idx, 1 );
}
break;
}
}
void pid_logf( const char *format, ... )
{
va_list args;
printf( "[%d] ", getpid() );
va_start( args, format );
vprintf( format, args );
va_end( args );
printf( "\n" );
}
int do_test( void )
{
pid_t child_pid;
pid_logf( "Forking a child" );
child_pid = fork();
if (child_pid == -1)
return -1;
else if (child_pid != 0)
return 0;
/* inside the child now */
sigaction( SIGUSR1, (struct sigaction *)SIG_DFL, NULL );
sigaction( SIGCHLD, (struct sigaction *)SIG_DFL, NULL );
pid_logf( "Sending SIGUSR1 to %d", getppid() );
if (kill( getppid(), SIGUSR1 ) == -1)
{
pid_logf( "ERROR: kill returned %d", errno );
_exit( 1 );
}
pid_logf( "Sleeping" );
sleep( 1 );
pid_logf( "Sending SIGUSR1 to %d", getppid() );
if (kill( getppid(), SIGUSR1 ) == -1)
{
pid_logf( "ERROR: kill returned %d", errno );
_exit( 1 );
}
pid_logf( "Sleeping" );
sleep( 1 );
pid_logf( "Exiting" );
_exit( 0 );
}
int main( void )
{
struct sigaction sa;
int pipe_fds[ 2 ] = { 0 };
int nchildren = 0;
pid_logf( "Creating pipe" );
if (pipe( pipe_fds ) == -1)
{
pid_logf( "ERROR: pipe returned %d", errno );
return 1;
}
g_info_wfd = pipe_fds[ 1 ];
sigemptyset( &g_usr1_sigset );
sigaddset( &g_usr1_sigset, SIGUSR1 );
sigemptyset( &sa.sa_mask );
sa.sa_sigaction = handle_sigs;
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDSTOP;
pid_logf( "Installing SIGUSR1 handler" );
if (sigaction( SIGUSR1, &sa, NULL ) == -1)
{
pid_logf( "ERROR: sigaction returned %d", errno );
return 1;
}
pid_logf( "Installing SIGCHLD handler" );
if (sigaction( SIGCHLD, &sa, NULL ) == -1)
{
pid_logf( "ERROR: sigaction returned %d", errno );
return 1;
}
if (do_test() == -1)
{
pid_logf( "ERROR: fork returned %d", errno );
return 1;
}
else
++nchildren;
if (do_test() == -1)
{
pid_logf( "ERROR: fork returned %d", errno );
return 1;
}
else
++nchildren;
while (nchildren > 0)
{
fd_set readset, errorset;
uint8_t idx = 0;
int nfds = 0;
int ret = 0;
FD_ZERO( &readset );
FD_ZERO( &errorset );
FD_SET( pipe_fds[ 0 ], &readset );
FD_SET( pipe_fds[ 0 ], &errorset );
if (pipe_fds[ 0 ] >= nfds)
nfds = pipe_fds[ 0 ] + 1;
pid_logf( "Waiting for a signal" );
ret = select( nfds, &readset, NULL, &errorset, NULL );
if (ret == -1)
{
if (errno != EINTR)
{
pid_logf( "ERROR: select returned %d", errno );
return 1;
}
}
else if (ret > 0)
{
if (FD_ISSET( pipe_fds[ 0 ], &errorset ))
{
pid_logf( "ERROR: pipe is bad" );
return 1;
}
while ((ret = read( pipe_fds[ 0 ], &idx, 1 )) == 0);
if (ret == -1)
{
pid_logf( "ERROR: read from pipe returned %d", errno );
}
pid_logf( "signal %d from { %d, %d, %d, %d }",
g_info[ idx ].si_signo,
g_info[ idx ].si_signo,
g_info[ idx ].si_errno,
g_info[ idx ].si_code,
g_info[ idx ].si_pid
);
if (g_info[ idx ].si_signo == SIGCHLD)
--nchildren;
}
}
return 0;
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden