Hello-

I'm trying to write a classic producer/consumer program using signals. My parent process (producer.c) forks and executes a new child program (consumer.c). The producer copies values from an input file (100 total int values to copy) to a common file, then sends a signal to the child to copy the value from the common file into an output file. Then the producer pauses. After it copies a value from the common file and puts it into the output file, the consumer signals the producer to put the next value in the common file, then pauses. Then the producer handles this signal and copies the next value from the input file, and so on. As it stands now, the transfer is inconsistent, but I don't know why. It usually quits after only a couple of values have been copied, sometimes 30, sometimes 50, etc. But rarely do all 100 values get copied from the input file. It seems random. I don't know how to make it consistently copy all 100 values without terminating prematurely. As you can see from my signal set, I've blocked every signal except SIGUSR1 & SIGUSR2. I thought this was the key, but the point of failure always occurs right after the child has handled the SIGUSR2 signal and is about to continue its loop to copy more values from common. I thought I covered my bases with this one. I've had my nose in books for hours trying to find the answer to this problem + now I'm so dizzy I can't think. If anybody could offer a hint, I'd be grateful.

Thank you.

/* producer.c
 *
 */
#include <stdio.h>
.....
.....
.....

void signalhandler();

int main(int argc, char *argv[])
{        
    const char *common_file = "/tmp/common";
    const char *input_file = /home/echobase/input";
    const char *child_path = /home/echobase/consumer";
    
    int     input_file_desc;
    int     common_file_desc;
    int     save_errno;
    int     bytes_to_read = 4;
    int     n;  //bytes to transfer    
    char    buf[BUFSIZ];
    
    pid_t   pid;
    pid_t   child_pid;
    
    struct sigaction sighandler;
    sigset_t    blocked;
    void        sigusrhandler();
    
    sighandler.sa_handler = sigusrhandler;
    sighandler.sa_flags = SA_RESTART;
    
    sigfillset(&blocked);
    sigdelset(&blocked, SIGUSR1);    
    sighandler.sa_mask = blocked;
    
   if(sigaction(SIGUSR1, &sighandler, 0) == -1)
        perror("sigaction");
    
    
    /* open the input file */
    input_file_desc = open(input_file, O_RDONLY);
    if(input_file_desc == -1) {
        save_errno = errno;
        printf("Open failed with error %d\n", save_errno);
    }
    
    
    /* create child process */
    if((child_pid = fork()) < 0) {
        fprintf(stderr, "fork error");
    }else if(child_pid == 0) {        
        execlp(child_path, "consumer", 0);       
    }
    
    /* loop to transfer values from input file to common file */
    while((n = read(input_file_desc, buf, bytes_to_read)) > 0)
    {
        printf("parent is in loop1\n");
        if((common_file_desc = open(common_file, O_RDWR)) == -1){
            perror("open");
            exit(1);
        }
        
        lseek(common_file_desc, 0L, 0);
        write(common_file_desc, buf, n);        
        
        close(common_file_desc);
        
        printf("parent is in loop2\n");
        
        kill(child_pid, SIGUSR2);
    
        
        pause();        
    }     
        
    
    close(input_file_desc);
    printf("parent test successful\n");
    exit(0);

}

void sigusrhandler()
{
    printf("parent called with signal\n");
    //sleep(2);
    //printf("parent done handling signal\n");
}



/* consumer.c
 *
 */

#include <stdio.h>
....
....
....

void signalhandler();

int main(int argc, char *argv[])
{    
    const char *common_file = "/tmp/common";
    const char *output_file =/home/output";
    
    int     common_file_desc;
    int     output_file_desc;    
    int     save_errno;
    int     bytes_to_read = 4;
    int     n;  //bytes to transfer    
    char    buf[BUFSIZ];
    int     tries = 100;    
    
    pid_t   parent_id = getppid();
    
    struct sigaction sighandler;
    sigset_t    newmask;
    void        sigusrhandler();
    
    sighandler.sa_handler = sigusrhandler;
    
    sighandler.sa_flags = SA_RESTART;
    
    sigfillset(&newmask);
    sigdelset(&newmask,SIGUSR2);
    
    sighandler.sa_mask = newmask;
    
    
    if(sigaction(SIGUSR2, &sighandler, 0) == -1)
        perror("sigaction");
        
    /* open the output file */    
    if((output_file_desc = open(output_file, O_WRONLY, 0)) == -1) {
        save_errno = errno;
        printf("Open failed with error %d\n", save_errno);
        exit(1);
    }
    
    pause();
    
    
/* loop to copy values from common file to output file */
    while(tries--){
        
        //pause();
        
        /* open the common file */    
        if((common_file_desc = open(common_file, O_RDWR)) == -1){
            perror("open");
            exit(1);
        }
        
        /* copy bytes from common file to output file */
        if((n = read(common_file_desc, buf, bytes_to_read)) > 0){                
                write(output_file_desc, buf, n);
        }
        
        close(common_file_desc);
        
        printf("child is in loop\n");
        
        kill(parent_id, SIGUSR1);
        
       pause();

        
    }    
        
    printf("child test successful\n");   
    
    close(output_file_desc);
    exit(0);
}

void sigusrhandler()
{
    printf("child called with signal\n");
    //sleep(2);
    //printf("child done handling signal\n");
}

Why do you do that with signals, signals supposed to be only for some system events, use pipes, or sockets. And if you anyway use files, nothing would become slower when you use files to signal.

Nothing wrong about exercise but using signals in that case is just senseless. The following code is tested, works correctly, and the transfer is not inconsistent. Look how much more simple and better organised it is using sockets, and i'm not dizzy after writing it :)

#include <stdio.h>
#include <sys/socket.h>

int sv [2];

int main ()
{
  socketpair (AF_UNIX,
    SOCK_STREAM, 0, sv);
  if (fork ()) { /* parent */
    char buf [MAX_INPUT]; 
    char sbuf [MAX_INPUT];
    FILE *f;

    f = fopen ("prodfile", "w");
    while (1) {
      if (!fgets (buf, MAX_INPUT, stdin))
        break;
      fputs (buf, f);
      fflush (f);
      send (sv [0], "read",
        MAX_INPUT, 0);
      while (recv (sv [0], sbuf, 
        MAX_INPUT, MSG_DONTWAIT) == -1)
        usleep (10000);
      if (strcmp (sbuf, "send")) break;
    }
  } else { /* child */
    char buf [MAX_INPUT];
    char sbuf [MAX_INPUT];
    FILE *f;

    f = fopen ("prodfile", "r");
    while (1) {
      while (recv (sv [1], sbuf, 
        MAX_INPUT, MSG_DONTWAIT) == -1)
        usleep (10000);
      if (strcmp (sbuf, "read")) break;
      if (!fgets (buf, MAX_INPUT, f))
        break;
      fputs (buf, stdout);
      send (sv [1], "send",
        MAX_INPUT, 0);
    }
  }
  return 0;
}

Nothing wrong about exercise but using signals in that case is just senseless. The following code is tested, works correctly, and the transfer is not inconsistent. Look how much more simple and better organised it is using sockets, and i'm not dizzy after writing it :)

That's nice, but he doesn't need it. He said: "I'm doing an exercise that requires the use of signals".

I would gladly help him with signals, but only with some more reasonable example, the present one is too abhorrent.

Please add shutdown (sv [0], SHUT_RDWR) and shutdown (sv [1], SHUT_RDWR) respectively in the end of both threads in my example. If we look at the unix domain sockets with lsof -U, two sockets remain otherwise hanging, which is not exactly what we should expect by standard, ie closing all open files on program exit. There are some compiler flags which force that behavior also for sockets, but this again depends on compiler.

This article has been dead for over six months. Start a new discussion instead.