Lunar Mushrooms
Lunar Mushrooms

Reputation: 8918

Linux: Can a signal handler excution be preempted?

I came across the following signal handler code that stores the errno variable so that it wont affect main thread's errno handling.

void myhandler(int signo)
{
    int esaved;
    esaved = errno;
    write(STDOUT_FILENO, "Got a signal\n", 13);
    errno = esaved;
}

But this really serves the purpose ? what happens if another thread check for the shared errno varible just after write() and before restoring errno ? Will that thread get wrong errno value due to race condition?

Or a signal handler executes atomically with respect to a thread/process, so that once the signal handler executes , kernel wont schedule the thread back until the signal handler finishes?

Putting in other words -Once started, do a signal handler executes without being interrupted by:

 - 1) Scheduler (process/threads),  or  
 - 2) Other signals,  or
 - 3) Hardware interrupt handlers  ?

Upvotes: 5

Views: 5561

Answers (3)

Jorge Israel Peña
Jorge Israel Peña

Reputation: 38576

On Linux, errno is a macro that expands into a function call returning a modifiable lvalue that is distinct for each thread.

Check out the man page:

errno is defined by the ISO C standard to be a modifiable lvalue of type int, and must not be explicitly declared; errno may be a macro. errno is thread-local; setting it in one thread does not affect its value in any other thread.

However, indeed the signal can be fired again mid-execution of the signal handler (if you used signal() instead of sigaction(), depending on your environment; these inconsistencies are why sigaction() is recommended instead), or another signal can interrupt execution of your handler. This is why typically when you set the signal disposition, you make it so that it blocks the signal(s, in the case of multiple signals with that handler) -- by adding it to the signal mask -- during execution of the signal handler. This prevents the signal handler from interrupting itself (in some cases, preventing an infinite loop).

References:

  • sa_mask component that can be passed to sigaction():

    sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used.

  • signal():

    The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.

    POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal().

    [...] Furthermore, rapid deliveries of the same signal could result in recursive invocations of the handler.

Upvotes: 4

Jonathan Leffler
Jonathan Leffler

Reputation: 753505

The variable errno is thread-specific — or, more accurately, in a threaded environment, is a thread-local or per-thread value — so what is done to errno in this thread won't affect errno in other threads.

The purpose of the code saving and restoring errno is to hide any error set by the write() system call in myhandler(). But if the write() fails, it may set errno to some new value — it won't be zero, but that's about all you can say — but the code you're asking about reinstates the value from before the call to write() after the call to write(), so that the fact that the write occurred is 'invisible' in the sense that it does not affect errno for this thread.

A signal handler function may itself be interrupted by signals that are not blocked by the signal mask for the signal that it is responding to. It could also be rescheduled. Hardware interrupts can occur too, but the code will be hard pressed to notice these effects.


On Linux, you may find /usr/include/bits/errno.h defining the macro errno (wrapped in more #ifdef code than is shown here):

extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif

Upvotes: 4

kronion
kronion

Reputation: 711

The signal handler can indeed be interrupted by another signal (assuming it isn't the same signal as the one which invoked the handler in the first place).

your handler can still be interrupted by delivery of another kind of signal. To avoid this, you can use the sa_mask member of the action structure passed to sigaction to explicitly specify which signals should be blocked while the signal handler runs. These signals are in addition to the signal for which the handler was invoked, and any other signals that are normally blocked by the process. See Blocking for Handler.

When the handler returns, the set of blocked signals is restored to the value it had before the handler ran. So using sigprocmask inside the handler only affects what signals can arrive during the execution of the handler itself, not what signals can arrive once the handler returns.

http://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html#Signals-in-Handler

Upvotes: 4

Related Questions