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

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.

Related

process exit automatically when signal delivered before pthread_cond_wait

#include "apue.h"
#include <pthread.h>
int quitflag;
sigset_t mask;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait = PTHREAD_COND_INITIALIZER;
void *
thr_fn(void *arg)
{
int err, signo;
for ( ; ; ) {
err = sigwait(&mask, &signo);
if (err != 0)
err_exit(err, "sigwait failed");
switch (signo) {
case SIGINT:
printf("\ninterrupt\n");
break;
case SIGQUIT:
pthread_mutex_lock(&lock);
quitflag = 1;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&wait);
return 0;
default:
printf("unexpected signal %d\n", signo);
exit(1);
}
}
}
int
main(void)
{
int err;
sigset_t oldmask;
pthread_t tid;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
err_exit(err, "SIG_BLOCK error");
err = pthread_create(&tid, NULL, thr_fn, 0);
if (err != 0)
err_exit(err, "can not create thread");
**sleep(20);** /* added code here */
pthread_mutex_lock(&lock);
while (quitflag == 0)
pthread_cond_wait(&wait, &lock); /* first unlock, then suspend */
pthread_mutex_unlock(&lock);
quitflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
exit(0);
}
This is the code of apue that examine "sigwait", i insert "sleep(20)" before pthread_cond_wait is invoked, start the program with ./a.out, then thread thr_fn start, before "20 seconds", i send "signal quit" when ctrl+\, then thread exited, then after a few time, the main process exit automatically,
i don't know why? could somebody explain this? my platform is centos, thanks

Why block signals then unblock them?

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

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