Why block signals then unblock them? - c

When reading certain source code that involves v4l2 APIs, I stumbled upon these segments:
First:
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigaddset(&set, SIGALRM);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGHUP);
pthread_sigmask(SIG_BLOCK, &set, &old);
Then:
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
In between:
if (s->pframe >= 0) {
if (xioctl(s->fd, VIDIOC_QBUF, &s->buf) == -1) {
motion_log(LOG_ERR, 1, "%s: VIDIOC_QBUF", __FUNCTION__);
return -1;
}
}
memset(&s->buf, 0, sizeof(struct v4l2_buffer));
s->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
s->buf.memory = V4L2_MEMORY_MMAP;
if (xioctl(s->fd, VIDIOC_DQBUF, &s->buf) == -1) {
/* some drivers return EIO when there is no signal,
driver might dequeue an (empty) buffer despite
returning an error, or even stop capturing.
*/
if (errno == EIO) {
s->pframe++;
if ((u32)s->pframe >= s->req.count) s->pframe = 0;
s->buf.index = s->pframe;
motion_log(LOG_ERR, 1, "%s: VIDIOC_DQBUF: EIO (s->pframe %d)", __FUNCTION__, s->pframe);
return 1;
}
motion_log(LOG_ERR, 1, "%s: VIDIOC_DQBUF", __FUNCTION__);
return -1;
}
s->pframe = s->buf.index;
s->buffers[s->buf.index].used = s->buf.bytesused;
s->buffers[s->buf.index].content_length = s->buf.bytesused;
My honest guess is that blocking signal prevents, in this particular case, the setup process being interrupted. But I'm completely unsure. Please help?

https://support.sas.com/documentation/onlinedoc/sasc/doc750/html/lr1/zlocking.htm
your guess is correct, it is supposed to prevent thread interruptions.
the link above isnt specific to pthreads but the concept is the same and is explained well i think
edit:
pthreads explanation on blocking signals
http://maxim.int.ru/bookshelf/PthreadsProgram/htm/r_40.html

Related

how to induce semop call that fails with EINTR in linux?

I m trying to induce EINTR failure with semop call.
key_t semkey;
int semid;
struct sembuf sbuf;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
struct semid_ds ds;
/* Get unique key for semaphore. */
if ((semkey = ftok("/tmp", 'a')) == (key_t) -1) {
perror("IPC error: ftok"); exit(1);
}
/* Get semaphore ID associated with this key. */
if ((semid = semget(semkey, 0, 0)) == -1) {
/* Semaphore does not exist - Create. */
if ((semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | S_IRUSR |
S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != -1)
{
/* Initialize the semaphore. */
arg.val = 0;
sbuf.sem_num = 0;
sbuf.sem_op = 2; /* This is the number of runs without queuing. */
sbuf.sem_flg = 0;
if (semctl(semid, 0, SETVAL, arg) == -1
|| semop(semid, &sbuf, 1) == -1) {
perror("IPC error: semop"); exit(1);
}
}
else if (errno == EEXIST) {
if ((semid = semget(semkey, 0, 0)) == -1) {
perror("IPC error 1: semget"); exit(1);
}
goto check_init;
}
else {
perror("IPC error 2: semget"); exit(1);
}
}
else
{
/* Check that semid has completed initialization. */
/* An application can use a retry loop at this point rather than
exiting. */
check_init:
arg.buf = &ds;
if (semctl(semid, 0, IPC_STAT, arg) < 0) {
perror("IPC error 3: semctl"); exit(1);
}
if (ds.sem_otime == 0) {
perror("IPC error 4: semctl"); exit(1);
}
}
sbuf.sem_num = 0;
sbuf.sem_op = -1;
sbuf.sem_flg = SEM_UNDO;
while (semop(semid, &sbuf, 1) == -1)
{
if (errno != EINTR)
{
perror("IPC Error: semop"); exit(1);
break;
}
}
Most i get is Resource unavailable failure or Resource busy. I even tried multiple semaphores running in two different threads or two different processes. but i dint able to get EINTR failure. i even tried sending signal as SIGCHLD to the process when sometime semop is waiting for the semaphores.
As per zwol suggestion,
Here is what i tried but it still dint work, i mean i can't get EINTR.
int g_global_variable = 0;
void *sigusr1_block_thread (void *vargp)
{
while (1)
{
sleep (10);
printf ("sigusr1_block_thread\n");
}
return NULL;
}
void *semop_wait_thread (void *vargp)
{
int sem;
struct sembuf sops[2];
if((sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600))==-1){
return NULL;
}
if(semctl(sem,0,SETVAL,2)==-1){
exit(1);
}
sops[0].sem_num=0;
sops[0].sem_op=-1;
sops[0].sem_flg=0;
sops[1].sem_num=0;
sops[1].sem_op=0;
sops[1].sem_flg=0;
g_global_variable = 1;
printf ("Starting semop call \n");
if(eintr_check_semop(sem, sops,2)<0)
printf("Error semop\n");
return NULL;
}
int main()
{
pthread_t tid, tid1, tid2, tid3, tid4;
sigset_t set;
int s;
pthread_create(&tid, NULL, semop_wait_thread, NULL);
pthread_create(&tid2, NULL, semop_wait_thread, NULL);
pthread_create(&tid3, NULL, semop_wait_thread, NULL);
pthread_create(&tid4, NULL, semop_wait_thread, NULL);
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGCHLD);
s = pthread_sigmask(SIG_BLOCK, &set, NULL);
if (s != 0)
printf ("Error during pthread_sigmask");
pthread_create(&tid1, NULL, sigusr1_block_thread, NULL);
while (1)
{
sleep (1);
if (g_global_variable == 1)
{
sleep (10);
printf ("Send SIGUSR1/SIGCHLD signals \n");
/* Send signal */
pthread_kill( tid, SIGCHLD);
pthread_kill( tid2, SIGCHLD);
pthread_kill( tid3, SIGCHLD);
pthread_kill( tid4, SIGCHLD);
pthread_kill( tid1, SIGCHLD);
pthread_kill( tid1, SIGUSR1);
break;
}
else
continue;
}
pthread_join(tid, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_join(tid4, NULL);
return 0;
}
eintr_check_semop just a function in which semop error and return values are checked. if EINTR it prints the message saying same.
if i send sigusr1 to blocking thread (t, t2, t3, t4) semop call break and comes of the loop.
I didnt get EINTR by any means. Then i checked kernel source code.
https://elixir.bootlin.com/linux/latest/source/ipc/sem.c
During EINTR, i see they are looping and not reporting the same.
EINTR only happens when a process receives a signal while it's blocked on a blocking system call, and that signal has a handler, and that handler is configured to interrupt rather than restart system calls. (There are a few exceptions to this principle, but none of them involve semop.) Your program hasn't got any signal handlers, so EINTR won't happen, even if you do send it signals.
I can't tell you exactly how to do this off the top of my head, but the overall pattern that should work is:
Establish a signal handler for some signal. if you have no reason to pick some other specific signal, use SIGUSR1 . Use sigaction to do this, not signal, and do not include SA_RESTART in sa_flags. The handler doesn't have to do anything; it just has to exist.
If the program has more than one thread, use pthread_sigmask to block SIGUSR1 in every thread but one.
In the thread that has SIGUSR1 unblocked, perform a semop operation that will block (a "wait-for-zero" on a semaphore with a nonzero value, without IPC_NOWAIT).
After the above thread is definitely blocked on semop, from another thread within the program, use pthread_kill to send SIGUSR1 to the blocked thread. Or, from outside the program, use regular kill to send SIGUSR1 to the whole process; because the signal is unblocked in exactly one thread, that thread will receive the signal.
The hardest part is being sure that the thread is blocked on semop before the signal is sent. I'm not sure this is possible from inside the program, without a race condition.

Fork() working incorrectly, returns 3 times

I am designing a multi-threaded, multi-process application in C. The program is being tested in Ubuntu 10.04.4 LTS with Linux kernel 2.6.31 and glibc version 2.11.1.
I am attempting to fork so that I can start bash as a child process to create a virtual shell. The problem is, sometimes fork() behaves very strangely by returning 3 times. As I understand it, fork() should return twice: once for the parent process and once for the child process. Unfortunately in my case, fork() returns 3 times: once for the parent process and TWICE for the child process. In htop the result looks like this http://i59.tinypic.com/20fdjci.png
In the image above PID 4320 is in the correct spot, but PID 4316 has been started as an orphaned process and is consuming all of the processor time.
Here is the code that produced that result:
#include "v_shell.h"
/*executes bash and attaches stdin and stdout to pipes*/
int vshell_init() {
int rc;
FILE * rf;
/*initialize pipes*/
// mknod(STDIN_PIPE, S_IFIFO | 0666, 0);
// mknod(STDOUT_PIPE, S_IFIFO | 0666, 0);
mkfifo(STDIN_PIPE, 0666);
mkfifo(STDOUT_PIPE, 0666);
pid_t result = fork();
if (result == 0) { //this is the child
prctl(PR_SET_NAME, "SHELL_INPUT", 0, 0, 0); //set name of the child process
/*set thread priority*/
int ret;
struct sched_param params;
params.sched_priority = sched_get_priority_max(SCHED_RR) - 10;
ret = pthread_setschedparam(pthread_self(), SCHED_RR, &params);
if (ret != 0) {
// Print the error
record("Unsuccessful in setting thread realtime prio\n");
return 0;
}
/*redirect standard input and output*/
record("Redirecting stdin and stdout\n");
rf = fopen(STDIN_PIPE, "r");
if (rf == NULL) record("Error opening named pipe\n");
rf = fopen(STDOUT_PIPE, "w");
if (rf == NULL) record("Error opening named pipe\n");
/*Close stdin and stdout to make sure*/
rc = fclose(stdout);
if (rc == EOF) record("Failed to close stdout\n");
fclose(stdin);
if (rc == EOF) record("Failed to close stdin\n");
/*Copy stdin and stdout to named pipes*/
rf = freopen(STDIN_PIPE, "r", stdin); //Redirect standard input
if (rf == NULL) record("Failed to redirect stdin\n");
rf = freopen(STDOUT_PIPE, "w", stdout); //Redirect standard output for new process
if (rf == NULL) record("Failed to redirect stdout\n");
rf = freopen(STDOUT_PIPE, "w", stderr); //Redirect standard error for new process
if (rf == NULL) record("Failed to redirect stderr\n");
record("Starting shell...\n");
//Start shelld -- this one uses bash. the ./bashrc file should be used
// if (execlp("bash", "bash", "--noprofile", "--rcfile", "bashrc", "-i", (char *) 0) == -1) {
if (execlp("bash", "bash", "--rcfile", "bashrc", "-i", "-s", (char *) 0) == -1) {
record("ERROR in starting virtual shell!\n");
}
return -1; //shouldn't return if it worked correctly
}
return result;
}
Does anyone know what could make fork() produce this result?
It looks from the png as if one process has reparented itself to init (normally meaning its parent has died), but is not a zombie.
Two possible explanations:
You are calling vshell_init more than once. I'm betting you would have caught that one though.
(more likely) the bash shell is itself forking. Bash forks more than you might think (for example a script with things in () will fork).
To debug
strace -f -s2048 -o trace.out yourprogramnamehere
then search for execlp and see what's happening.
If this is intermittent, you might want to find out what on earth 4316 is doing, e.g.:
strace -f -s2048 -o trace.out -p 4316
When fork()-ing, make sure you close all the FDs you aren't passing through (here's an example which closes all but one pipe)
{
int i;
char *devnull = "/dev/null";
....
for (i = getdtablesize () - 1; i >= 0; i--)
{
if (!pipedata || (i != pipefd[0]))
close (i);
}
i = open (devnull, O_RDWR);
if (i == -1)
{
fprintf (stderr, "Unable to open /dev/null\n");
_exit (1);
}
if (pipedata)
{
dup2 (pipefd[0], 0);
}
else
{
i = open (devnull, O_RDONLY);
if (i != 0)
{
dup2 (i, 0);
close (i);
}
}
i = open (devnull, O_WRONLY);
if (i != 1)
{
dup2 (i, 1);
close (i);
}
i = open (devnull, O_WRONLY);
if (i != 2)
{
dup2 (i, 2);
close (i);
}
...
}
And set up your signal masks in a way bash will understand:
{
...
sigset_t set;
struct sigaction sa;
...
/* Set up the structure to specify the new action. */
memset (&sa, 0, sizeof (struct sigaction));
sa.sa_handler = SIG_DFL;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
sigaction (SIGINT, &sa, NULL);
sigaction (SIGTERM, &sa, NULL);
sigaction (SIGPIPE, &sa, NULL);
sigaction (SIGCHLD, &sa, NULL);
sigaction (SIGHUP, &sa, NULL);
sigaction (SIGUSR1, &sa, NULL);
sigaction (SIGUSR2, &sa, NULL);
/* unblock all signals */
sigfillset (&set);
pthread_sigmask (SIG_UNBLOCK, &set, NULL);
...
}

How to use sigsuspend with virtual timer

I want to use a virtual timer to pause a while loop till the timer expires. The goal is to have a function run periodically given a time interval.
I read on the gnu.org site that using pause() can be troublesome and they suggest using sigsuspend instead. I am using the code given in the example. My program looks as follows.
void handler_SIGALRM(int signo)
{
signo = 0; /* Get rid of warning "unused parameter ‘signo’" (in a portable way). */
puts(" ***********Cleared Alarm");
return;
}
int main(int argc, char *argv[]) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler_SIGALRM;
if (-1 == sigaction(SIGALRM, &sa, NULL ))
{
perror("sigaction() failed");
exit(EXIT_FAILURE);
}
struct itimerval it_val; /* for setting itimer */
it_val.it_value.tv_sec = 3; // INTERVAL/1000;
it_val.it_value.tv_usec = 0;//simData.misc
it_val.it_interval = it_val.it_value;
if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) {
perror("error calling setitimer()");
exit(1);
}
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask,SIGALRM);
/* Wait for a signal to arrive. */
sigprocmask(SIG_BLOCK, &mask, &oldmask);
sigsuspend(&oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
puts("unblocked");
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(1)
{
//...
sigsuspend(&oldmask);
//...
}
sigprocmask(SIG_UNBLOCK, &mask, NULL);
return 0;
}
The code above accomplishes the "pause" that I need. However, when I run it embedded in another program (threaded). It just hangs, and just keeps calling _handler_SIGALRM_.
Am I doing something wrong? Why could my program be hanging?

Can C library functions (specifically sigaddset()) fail with an error not mentioned in standard?

In my program,sigaddset() is failing with error:
sigaddset: Invalid Seek
which corresponds to ESPIPE in errno.h but this error is not mentioned in the standard definition of sigaddset().
The complete code is too long, I am posting the required parts:
The signal handling routine:
void* Ctrl_C_handler(void *arg)
{
int *sock_fd_ptr = (int*)arg;
sigset_t set;
int err;
err = sigemptyset(&set); // Clear the signals holder set
if(!err) perror("sigemptyset(&set)");
err = sigaddset(&set, SIGINT); // Add the SIGINT: Ctrl+C
if(!err) perror("siaddset(&set, SIGINT)");
err = sigthreadmask(SIG_UNBLOCK, &set, NULL); // Set mask as to unblock SIGINT
if(!err) perror("sigthreadmask(SIG_SETMASK, &set, NULL)");
while(1) // Ctrl+C
{
sig = sigwait(&set); // Wait for signal to occur
if(sig == SIGINT)
{
printf("Ctrl+C detected by server !!\n");
printf("No more connections will be accepted!!");
if(*sock_fd_ptr > 0)
{
close(server_sock_fd);
*sock_fd_ptr = -1;
break;
}
}
else
{
printf("Error handling the signal, SigHandlerThread exiting..\n")
perror("sigwait(&set)");
break;
}
}
return NULL;
}
Inside main():
/*********** Signal Handling *************/
sigset_t set; // declare a set to hold the signals
err = sigfillset(&set); // Fill the set with all the signals
if(!err) perror("sigfillset(&set)");
err = sigthreadmask(SIG_BLOCK, set, NULL); // Block/Mask the signals in the set
if(!err) perror("sigthreadmask(SIG_BLOCK, &set, NULL)");
err = pthread_create(&sig_handler_tid, NULL, Ctrl_C_handler, (void *)&sock_fd);
// Create a thread for handling signals
if(!err) perror("pthread_create");
What am I missing or doing wrong or understanding wrong?
P.S.: I have recently started programming using signals.
Operating Sys: SLES 10 SP3 x86-64
Your error testing is wrong. These functions return 0 when they're successful, -1 when there's an error. Your test:
if (!err) perror(...);
is equivalent to:
if (err == 0) perror(...);
which means you call perror when the calls are successful. As a result, you're printing the value of errno left over from some past system call that failed (often this is something internal to the stdio functions).
You should do:
if (err == -1) perror(...);

Catching SIGCHLD using sigtimedwait() on BSD

I am having trouble using sigtimedwait() to catch SIGCHLD signals on FreeBSD. The following source works well on Debian GNU/Linux 7 but gives me a Resource temporarily unavailable on FreeBSD 9.1:
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
int main() {
sigset_t set;
pid_t pid;
printf("SIGCHLD is %i\n", SIGCHLD);
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, NULL);
pid = fork();
if(pid == -1) {
printf("fork failed: %s\n", strerror(errno));
exit(1);
} else if(pid) {
sigset_t set2;
siginfo_t siginfo;
struct timespec timeout = {3, 0};
int signal;
sigemptyset(&set2);
sigaddset(&set2, SIGCHLD);
signal = sigtimedwait(&set2, &siginfo, &timeout);
if(signal == -1) {
printf("sigtimedwait failed: %s\n", strerror(errno));
exit(2);
} else {
printf("received signal %i from %i with status %i\n", signal, siginfo.si_pid, siginfo.si_status);
}
} else {
sleep(1);
exit(123);
}
return 0;
}
Output on Linux:
SIGCHLD is 17
received signal 17 from 27600 with status 123
Output on FreeBSD:
SIGCHLD is 20
sigtimedwait failed: Resource temporarily unavailable
Using signal() works fine on BSD but this is not quite what I want. What am I missing?
I think this is a kernel/library bug in FreeBSD. It looks like sigtimedwait is not reporting the signal because it's ignored by default. So you could do 2 things
Install a signal handler for SIGCHLD event. Even it's never getting called since you're blocking the signal, it will workaround the bug.
Use kqueue with EVFILT_SIGNAL which definitely works in this case but is not portable (so you'd need an ifdef)
For 2, here is the equivalent code
int kq = kqueue();
struct kevent ke;
EV_SET(&ke, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
kevent(kq, &ke, 1, NULL, 0, NULL);
if (kevent(kq, NULL, 0, &ke, 1, &timeout) == 1) {
signal = ke.ident;
}
else {
// Catches errors in the add, timeout, and kevent wait
signal = -1;
}
close(kq);
// note that siginfo is not populated, there is no way to populate it using kqueue.

Resources