3

There is something wrong with my code that uses IPC queue to communicate between threads. I need to handle SIGINT safely - let program finish all active threads when SIGINT appeared before shutting down. Though, I have serious problem of finding solution because even with sigaction for SIGINT with flag SA_RESTART the msgrcv function is getting EINTR error.

My question is - is there any way to avoid EINTR with msgrcv function other than specifying the error condition in some "if" such as:

if (msgrcv<0){
  if (errno == EINTR){
    errno = 0;
  }
}

Here is really simplified version of my program to demonstate problem :

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/msg.h>

#define IPC_KEY 11000
#define BUF_SIZE 128

//structure of IPC message
typedef struct msgbuf{
  long mtype;
  char mtext[BUF_SIZE];
} message_buf;


void handle_sigint(int sig){
  signal(SIGINT,SIG_IGN);
  /*
    some operation to handle sigint,
    here it's really simple setting 
    SIGINT to be ignored
  */ 

}

int main(){

  long ipc_id;
  message_buf msg;
  struct sigaction setup_action;
  sigset_t block_mask;

  //setup sigaction for SIGINT
  sigemptyset(&block_mask);
  sigaddset(&block_mask,SIGINT);
  setup_action.sa_handler = handle_sigint;
  setup_action.sa_mask = block_mask;
  setup_action.sa_flags = SA_RESTART;
  if (sigaction(SIGINT, &setup_action, 0) < 0){
    perror("sigaction");
    exit(1);
  }

  //creating the ipc queue
  if ((ipc_id = msgget(IPC_KEY, IPC_CREAT | IPC_EXCL | 0666))<0){
    perror("error in msgget");
    exit(1);
  }

  for(;;){
    if (msgrcv(ipc_id,&msg,BUF_SIZE,1,0)<0){
      perror("error in msgrcv");
      exit(1);
    }
    printf("received message : %s\n",msg.mtext);
  }

}

And here is simple program to clean up the IPC queue :

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/msg.h>

#define IPC_KEY 11000


int main(){

  long ipc_id;

  if ((ipc_id = msgget(IPC_KEY, IPC_CREAT | 0666 )) < 0) {
    perror("error in msgget");
    exit(1);
  }

  if (msgctl(ipc_id, IPC_RMID, 0) != 0){
    perror("error in msgctl");
    exit(1);        
  }


  return 0;

}

Thanks in advance for help! I really hope I didn't make duplicate question but I tried to look for a while for a solution and unfortunately didn't find any other than explicitly catching EINTR errno with if function.

tg_91
  • 43
  • 1
  • 3
  • You don't need to block SIGINT in your sigaction call — it will be blocked by default regardless of sa_mask unless you SA_NODEFER. – pilcrow Dec 21 '14 at 04:43

2 Answers2

3

From the (Linux) manual:

The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler:

....

  • System V IPC interfaces: msgrcv(2), msgsnd(2), semop(2), and semtimedop(2).

The way SA_RESTART is handled is a bit implementation defined. You didn't tag with a specific Unix flavor but I assume your Unix simply doesn't obey SA_RESTART for your specific system call.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • Thanks! I hoped I could do it "nicer" way with sigaction but now I see that it's impossible – tg_91 Dec 20 '14 at 23:06
2

@cnicutar beat me to that by 10 seconds (so +1), but I'd add that all you need to do is to wrap the call to msgrcv in a do/while loop, e.g.

int res;
do
{
  res = msgrcv(your parameters here);
} while ((res < 0 ) && (errno == EINTR));

You can of course define a tiny function to do that for you if you use msgrcv a lot.

Kevin
  • 28,963
  • 9
  • 62
  • 81
abligh
  • 24,573
  • 4
  • 47
  • 84
  • There is a non-standard [`TEMP_FAILURE_RETRY`](http://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html) macro provided by `GNU` `unistd.h` to help with this. – cnicutar Dec 20 '14 at 23:09
  • @cnicutar I think you mean `TEMP_FAILURE_RETRY` (from `unistd.h`) - mine at least doesn't have `NO_EINTR`. – abligh Dec 20 '14 at 23:13
  • @abligh not yet, I need 15 rep to do that. It's not like I'm going to beg, I don't really need that, I just think being able to upvote good answers is a nice thing to do and I would do it if I could. – tg_91 Dec 20 '14 at 23:16