Reputation: 12966
I'm working on a program that is supposed to act like a server and continuously read from a message queue and process the received messages.
The main loop looks something like this:
while (1) {
/* Receive message */
if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
//more code here
}
The problem I'm having is that I cannot figure out a way to gracefully exit this loop without relying on a client to send a message to the server indicating that it should stop. I do a lot of resource clean-up after the loop, and my code can never get to that point because the loop will not end.
One thing I tried to do was listen for a SIGINT to end the loop....
volatile sig_atomic_t stop;
void end(int signum) {
stop = 1;
}
int main(int argc, char* argv[]) {
signal(SIGINT, end);
//some code
while (!stop) {
/* Receive message */
if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
//more code here
}
//cleanup
}
...but since the loop is hanging on the system call itself, this doesn't work, and just results in perror
printing out msgrcv: Interrupted system call
, instead of terminating the loop and cleaning up my resources.
Is there a way I can terminate a system call and gracefully exit my loop?
SOLUTION:
Thanks to rivimey, I was able to solve my problem. Here is what I did to make it work:
volatile sig_atomic_t stop;
void end(int signum) {
stop = 1;
}
int main(int argc, char* argv[]) {
signal(SIGINT, end);
//some code
while (!stop) {
/* Receive message */
if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) {
if (errno == EINTR) break;
else {
perror("msgrcv");
exit(1);
}
}
//more code here
}
//I can now reach this code segment
}
Upvotes: 1
Views: 2568
Reputation: 16540
the code could have the following implemented:
have the msgflg parameter contain 'IPC_NOWAIT'
then, the next line in the code should check 'errno'
for the value 'EAGIN'
when errno is EAGIN, either loop to recall msgrcv() or exit
the loop due to some other criteria.
optionally the code could nanosleep() for a while
before jumping back to the top of the loop
extracted from the man page for msgrcv()
"EAGAIN No message was available in the queue
and IPC_NOWAIT was specified in msgflg."
Upvotes: 0
Reputation: 931
You'd do well to go look at existing software that does this; it's a very common pattern and not as simple to get right as you'd hope. However the basics are:
For a non-trivial program you'd be better using th 'poison' method of killing the loop. Send yourself a message using msgsend that says kill me. That way, you get predictable results.
HTH, Ruth
Upvotes: 1