process exit automatically when signal delivered before pthread_cond_wait - c

#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

Related

display all blocked and pending signals at one time

The following program checks if a signal is pending. I use the sigpending function to return blocked (or waiting) signals. The problem is that I don't want this, I would like to display all the blocked and pending signals at some point, how can I do that? What should I change?
code:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void catcher(int signum) {
puts("inside catcher!");
if (signum != 0)
perror("signum error");
}
void check_pending(int signum, char * signame) {
sigset_t sigset;
if (sigpending( & sigset) != 0)
perror("sigpending() error");
else if (sigismember( & sigset, signum))
printf("a %s signal is pending\n", signame);
else
printf("no %s signals are pending\n", signame);
}
int main() {
struct sigaction sigact;
sigset_t sigset;
sigemptyset( & sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
if (sigaction(SIGUSR1, & sigact, NULL) != 0)
perror("sigaction() error");
else {
sigemptyset( & sigset);
sigaddset( & sigset, SIGUSR1);
if (sigprocmask(SIG_SETMASK, & sigset, NULL) != 0)
perror("sigprocmask() error");
else {
puts("SIGUSR1 signals are now blocked");
kill(getpid(), SIGUSR1);
printf("after kill: ");
check_pending(SIGUSR1, "SIGUSR1");
sigemptyset( & sigset);
sigprocmask(SIG_SETMASK, & sigset, NULL);
puts("SIGUSR1 signals are no longer blocked");
check_pending(SIGUSR1, "SIGUSR1");
}
}
}
Use sigignore to dispose the signal. Program below will check pending signals in 1s intervals and display theirs numbers. Terminate with sending SIGTERM.
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int i;
sigset_t sigset;
printf("my pid is %d\n", getpid());
sigfillset(&sigset);
sigprocmask(SIG_SETMASK, &sigset, NULL);
while (1) {
sigpending(&sigset);
for (i = 1; i < 32; ++i) {
if (sigismember(&sigset, i)) {
printf("signal %d pending\n", i);
sigignore(i);
if (i == SIGTERM) {
exit(0);
}
}
}
sleep(1);
}
return 0;
}

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.

I have a handler to sigchld signal,but when sigtimedwait() returns success handler not called. Why?

int exe(int sec)
{
const char *buf;
int timeout_sec=sec;
int i=0,j=0;
sigset_t mask;
sigset_t orig_mask;
sigset_t pset;
siginfo_t siginfo;
struct timespec timeout;
pid_t pid;
int fd,status=-1;
int fl;
sig_init();
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("sigprocmask");
goto error;
}
if (( pid=fork())<0)
{
perror ("fork");
goto error;
}
else if (pid == 0)
{
printf("In child pid=%d\n",getpid());
if(execv(ptr[0],ptr)==-1)
{
printf("execv failed in %s: error: %s\n", __FUNCTION__, strerror(errno));
_exit(1);
}
printf("Exiting child\n");
_exit(0);
}
else
{
timeout.tv_sec = timeout_sec;
timeout.tv_nsec = 0;
do {
if ((ss=sigtimedwait(&mask, &siginfo, &timeout)) < 0) {
if (errno == EINTR) {
// Interrupted by a signal other than SIGCHLD.
printf("Continue\n");
continue;
}
else if (errno == EAGAIN) {
printf("%s %s failed in:%s KILLING\n",ptr[0],ptr[1],__FUNCTION__);
kill (pid, SIGKILL);
}
else {
perror ("sigtimedwait");
goto error;
}
}
break;
} while (1);
if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) < 0) {
perror ("sigprocmask");
goto error;
}
sleep(10);
}
return 0;
error:
printf("%s failed\n",__FUNCTION__);
return -1;
}
Here I am trying to block the SIGCHLD and the in child process executing execv, for SIGCHLD status I have a handler for it(not mentioned here in code) which is working fine when sigtimedwait() returning error but when sigtimedwait() success the handler is not called, and if I am using waitpid directly in parent instead of handler then in both of cases it is woking fine.
Please help me out. I just found one clue in man page of sigtimedwait i.e. "sigwaitinfo() removes the delivered signal from the calling process's list of pending signals and returns the signal number as its function result"
Curious to know the reason and too much in need.Thanks :)

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?

pthread_join doesn't work

What I want to accomplish:
In the main function are created two threads. They increment a global variable with the number 5. And send a signal to consumer thread that decrements the variables.In the consumer thread between each decrementation the current value is displayed. The main thread has to wait until all the threads are finished and then exit.
What I get:
Some times the main function exits before the consumer had a chance to display the results. I'm using pthread_join, but it returns error code 3.
Any Ideas how to get the wanted results?
The code is bellow.
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
static pthread_mutex_t mtx;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *producer(void *arg);
void *consumer(void *arg);
static int avail = 0;
int main(int argc, char *argv[]){
pthread_t cons1, prod1, prod2;
int status;
int t1;
int t2;
int t3;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutexattr_t mtxAttr;
pthread_mutexattr_settype(&mtxAttr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&mtx, &mtxAttr);
t1 = pthread_create(&prod1, &attr, producer, NULL);
if(t1 != 0){
perror("problem1");
}
t2 = pthread_create(&prod2, &attr, producer, NULL);
if(t2 != 0){
perror("problem2");
}
t3 = pthread_create(&cons1, &attr, consumer, NULL);
if(t3 != 0){
perror("problem3");
}
status = pthread_join(t1, NULL);
if(status != 0){
perror("can't join1");
}
status = pthread_join(t2, NULL);
if(status != 0){
perror("can't join2");
printf("\n%d\n", status);
}
status = pthread_join(t3, NULL);
if(status != 0){
printf("%s",strerror(errno));
}
printf("\nend result \t%d\n",avail);
printf("fin\n");
//while(1){}
return 0;
}
void *producer(void *arg){
int s;
printf("producer\n");
s = pthread_mutex_lock(&mtx);
avail+=5;
s = pthread_mutex_unlock(&mtx);
s = pthread_cond_signal(&cond);
pthread_exit(NULL);
}
void *consumer(void *arg){
int s;
while(1) {
s = pthread_mutex_lock(&mtx);
if(s !=0 ){
perror("lock err");
}
while (avail == 0) {
s = pthread_cond_wait(&cond, &mtx);
}
while (avail > 0) {
avail--;
printf("Temp: %d \n",avail);
}
s = pthread_mutex_unlock(&mtx);
}
printf("done");
pthread_exit(NULL);
}
Don't join on t1, t2 or t3. Those are the return codes of the pthread_create() function. Use pthread_join() on prod1, prod2 and cons1 instead. And please compile with -Wall -Wextra.

Resources