Setitimer, Sigalrm & Multithread Process (Linux, C)

Block signals for complete process in a multithreaded environment

Your problem is that various signals can be directed at the whole process or a particular thread. When directed at the whole process then they will be delivered to any thread that doesn't have them blocked.

man (7) signal

A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

Because the signal masks are inherited by each thread from whatever thread created them, a fairly standard technique is to block them in the creator (for simplicity let's say that is main) and then let each spawned thread unblock signals as appropriate.

A common variation on this is to block them in main, and keep them blocked in all spawned threads except one. That one thread unblocks the signals and all process directed signals get aimed at it to handle.

The above is probably what you want to do. You probably are properly blocking SIGALRM in the thread which is running the signal handler. Unfortunately that isn't going to prevent SIGALRM from being delivered to a second thread.

C: SIGALRM - alarm to display message every second

Signal handlers are not supposed to contain "business logic" or make library calls such as printf. See C11 §7.1.4/4 and its footnote:

Thus, a signal handler cannot, in general, call standard library functions.

All the signal handler should do is set a flag to be acted upon by non-interrupt code, and unblock a waiting system call. This program runs correctly and does not risk crashing, even if some I/O or other functionality were added:

#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>

volatile sig_atomic_t print_flag = false;

void handle_alarm( int sig ) {
print_flag = true;
}

int main() {
signal( SIGALRM, handle_alarm ); // Install handler first,
alarm( 1 ); // before scheduling it to be called.
for (;;) {
sleep( 5 ); // Pretend to do something. Could also be read() or select().
if ( print_flag ) {
printf( "Hello\n" );
print_flag = false;
alarm( 1 ); // Reschedule.
}
}
}

C `SIGVTALRM` is not handled when `sigwait` or `pause`

If you send SIGVTALRM with kill, it should arrive just fine. The problem is not signal delivery but that you misunderstand what "virtual time" means. None passes while your thread is sleeping/blocked waiting for something, so the timer will never expire. Perhaps you wanted real rather than virtual time.

Create new signal or multiplex SIGALRM?

Yes. You want to look at the timer_create / timer_settime etc., family of calls.

The 2nd parameter of timer_create is a struct sigevent. The field within that, sigev_signo can be set to send a specific signal number on timer expiration.

Issue with timer with long signal handler (SIGALARM)

The signal is delivered to a particular thread, so the signal handler runs in a particular thread (the thread the signal was delivered to). If the signal is delivered to a thread writing out 222\n, then that thread must stop writing out 222\n and run the signal handler. Your example signal handler takes a full second to run, so that's a full second during which that thread may not write out 222\n.

Additionally, since you are using printf to write out all of these bytes, there is some locking being done in libc. Since printf is not an "async signal safe" function, it's actually undefined what happens if you use it in a signal handler. One possible explanation for the behavior you observe is this. If the signal is delivered to a thread while that thread holds the stdout lock, then no other thread will be able to write to stdout until the handler returns and the lock can be released by the "normal" code running in that thread. The signal handler can still write to stdout in this case, though, because the lock is an rlock which can be acquired repeatedly any particular thread. This may vary from depending on the specific platform, C library, thread library, or phase of the moon, though. Your example is easily converted to use write(2) though, which demonstrates more or less the same problem behavior, has more or less the same fix, and doesn't rely on undefined behavior.

If you SIG_BLOCK the timer signal in the 222\n threads, then the signal handler will always run in the main thread and you will continue to get 222\n output while the signal handler sleeps.

Seth also makes a great point about only using the safe functions in signal handlers. Using any others means your program's behavior is undefined.

c setitimer not sending signal when outside its call scope

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

24.4.4 Signals Arriving While a Handler Runs

What happens if another signal arrives while your signal handler function is running?

When the handler for a particular signal is invoked, that signal is automatically blocked until the handler returns. That means that if two signals of the same kind arrive close together, the second one will be held until the first has been handled. (The handler can explicitly unblock the signal using sigprocmask, if you want to allow more signals of this type to arrive; see Process Signal Mask.)

However, 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.

Portability Note: Always use sigaction to establish a handler for a signal that you expect to receive asynchronously, if you want your program to work properly on System V Unix. On this system, the handling of a signal whose handler was established with signal automatically sets the signal’s action back to SIG_DFL, and the handler must re-establish itself each time it runs. This practice, while inconvenient, does work when signals cannot arrive in succession. However, if another signal can arrive right away, it may arrive before the handler can re-establish itself. Then the second signal would receive the default handling, which could terminate the process.

reference:<http://www.gnu.org/software/libc/manual/html_node/Process-Signal-Mask.html#Process-Signal-Mask>

24.7.3 Process Signal Mask

The collection of signals that are currently blocked is called the signal mask. Each process has its own signal mask. When you create a new process (see Creating a Process), it inherits its parent’s mask. You can block or unblock signals with total flexibility by modifying the signal mask.

The prototype for the sigprocmask function is in signal.h.

Note that you must not use sigprocmask in multi-threaded processes, because each thread has its own signal mask and there is no single process signal mask. According to POSIX, the behavior of sigprocmask in a multi-threaded process is “unspecified”. Instead, use pthread_sigmask.

Function: int sigprocmask (int how, const sigset_t *restrict set, sigset_t *restrict oldset)

Preliminary: | MT-Unsafe race:sigprocmask/bsd(SIG_UNBLOCK) | AS-Unsafe lock/hurd | AC-Unsafe lock/hurd | See POSIX Safety Concepts.

The sigprocmask function is used to examine or change the calling process’s signal mask. The how argument determines how the signal mask is changed, and must be one of the following values:

SIG_BLOCK

Block the signals in set—add them to the existing mask. In other words, the new mask is the union of the existing mask and set.

SIG_UNBLOCK

Unblock the signals in set—remove them from the existing mask.

SIG_SETMASK

Use set for the mask; ignore the previous value of the mask.

The last argument, oldset, is used to return information about the old process signal mask. If you just want to change the mask without looking at it, pass a null pointer as the oldset argument. Similarly, if you want to know what’s in the mask without changing it, pass a null pointer for set (in this case the how argument is not significant). The oldset argument is often used to remember the previous signal mask in order to restore it later. (Since the signal mask is inherited over fork and exec calls, you can’t predict what its contents are when your program starts running.)

If invoking sigprocmask causes any pending signals to be unblocked, at least one of those signals is delivered to the process before sigprocmask returns. The order in which pending signals are delivered is not specified, but you can control the order explicitly by making multiple sigprocmask calls to unblock various signals one at a time.

The sigprocmask function returns 0 if successful, and -1 to indicate an error. The following errno error conditions are defined for this function:

EINVAL

The how argument is invalid.

You can’t block the SIGKILL and SIGSTOP signals, but if the signal set includes these, sigprocmask just ignores them instead of returning an error status.

Remember, too, that blocking program error signals such as SIGFPE leads to undesirable results for signals generated by an actual program error (as opposed to signals sent with raise or kill). This is because your program may be too broken to be able to continue executing to a point where the signal is unblocked again. See Program Error Signals.



Related Topics



Leave a reply



Submit