0

I have 3 processes and two pipes. Process Producer generates a character and sends it to process Buffer via the Input pipe. Process Buffer stores the data in an array, then sends it to process Consumer via the Output pipe. Consumer then prints the data. Is it possible to do this in a nonblocking way?

Currently, the Consumer process only reads once a second and the Producer process outputs once every 0.3 seconds. The Consumer call blocks the Buffer process and the multiple Producer outputs are not stored in the buffer, staying in the Input pipe until the next loop. The program currently runs for 5 seconds, which can be modified by the length variable at the beginning of main().

#include "stdio.h"//get from and print to console
#include "stdlib.h"//free and malloc
#include "unistd.h"//fork(), exec(), sleep(), and pid_t
#include "sys/types.h"//pid_t
#include <string.h>//strtok
#include "sys/wait.h"//waitpid()
#include <time.h>//time()
#include <fcntl.h>//O_NONBLOCK, fd manipulation
#include <sys/stat.h>//mkfifo()

const int BUFFSIZE = 1000;

void buffer(time_t start, int len, char* buff, const char *ppipe, const char *cpipe);
void producer(time_t start, int len, const char *ppipe);
void consumer(time_t start, int len, const char *cpipe);

void main() {
    int length = 5;//length of program (seconds)
    char *buff = malloc(BUFFSIZE*sizeof(char));
    for(int i = 0;i<BUFFSIZE;i++) {
        buff[i] = '*';
    }
    //create the needed pipes
    const char *pfifo1 = "producer1";
    const char *cfifo1 = "consumer1";
    mkfifo(pfifo1, 0666);
    mkfifo(cfifo1, 0666);

    time_t start = time(NULL);
    srand(start);

    pid_t buf_p = fork();//fork off the buffer
    if(buf_p == 0) {
        buffer(start, length, buff, pfifo1, cfifo1);
    }

    pid_t prod_p = fork();//fork off the producer
    if(prod_p == 0) {
        producer(start, length, max_p, pfifo1);
    }

    pid_t con_p = fork();//fork off the consumer
    if(con_p == 0) {
        consumer(start, length, max_c, cfifo1);
    }

    pid_t wpid;//wait til the processes are finished
    while((wpid=wait(NULL))!=-1) {
    }
    unlink(pfifo1);//delete the pipes
    unlink(cfifo1);
    free(buff);
    printf("All processes terminated.\n");
}

void buffer(time_t start, int len, char *buff, const char *ppipe, const char *cpipe) {
    char input;
    int check;//checks if the pipe has a value
    int exitind = 0;
    int inputind = 0;
    int infd = open(ppipe, O_RDONLY, O_NONBLOCK);
    int exfd = open(cpipe, O_WRONLY, O_NONBLOCK);
    while(time(NULL)-start<len) {//loop until the program time ends
        //get the product from the producers
        if(buff[inputind]=='*') {//if the buffer is not full
            check = read(infd, &input, 1);//read from the input pipe
            if(check !=-1) {//ie there was input in the pipe
                buff[inputind] = input;//add it to the buffer
                inputind=(inputind+1)%BUFFSIZE;//inputind loops.
            }
        }

        //write from the array to the exit pipe
        if(buff[exitind]!='*') {//if the buffer has data
            char output = buff[exitind];
            check = write(exfd, &output, 1);
            if(check!=-1) {
                buff[exitind] = '*';//clear the entry
                exitind=(exitind+1)%BUFFSIZE;//exitind loops.
            }
        }
    }
    for(int i = 0;i<BUFFSIZE;i++) {
        printf("%c ", buff[i]);
        if((i+1)%40 ==0) {
            printf("\n");
        }
    }
    close(infd);
    close(exfd);
    printf("Buffer ended.\n");
    exit(0);
}

//function that each producer process runs.
void producer(time_t start, int len, const char *ppipe) {
    int fd = open(ppipe, O_WRONLY);
    while(time(NULL)-start<len) {//loop until the program time ends
        char output = 'A'+(rand()%26);//generate random character,
        printf("Producer value = %c\n", output);
        write(fd, &output, 1);//write it to the input pipe
        usleep(300000);//0.3 seconds of sleep
    }
    close(fd);
    printf("Producer ended\n");
    exit(0);
}

//function that each consumer process runs.
void consumer(time_t start, int len, const char *cpipe) {
    int fd = open(cpipe, O_RDONLY);
    char input;
    while(time(NULL)-start<len) {//loop until the program time ends
        sleep(1);
        //read a character from the buffer
        read(fd, &input, 1);
        printf("Consumer, value = %c\n", input);
    }
    close(fd);
    printf("Consumer ended\n");
    exit(0);
}
Ploof
  • 17
  • 6
  • OT: All of the header files that are being included via ".,." around the file name are system header files, therefore, they should be included via <...> – user3629249 May 04 '20 at 02:39
  • What do you want to be 'non-blocking'? The Buffer process will normally block on a read until the Producer writes some data; it can then arrange to send what it's got to the Consumer. The Consumer should probably simply block on reading from the Buffer; it does work when there's some data to be read. The Producer, therefore, controls the processing speed — unless one of the other processes is too busy with what the Producer has sent. If you mean you want the Buffer process, for example, to run free, you have to know how to poll for data available on the input, or space available on the output. – Jonathan Leffler May 04 '20 at 02:39
  • OT: although visual studio will allow a lot of bad programming, there are only two valid signatures to `main()` They are: `int main( void )` and `int main( int argc, char *argv[] )` – user3629249 May 04 '20 at 02:40
  • OT: regarding: `char *buff = malloc(BUFFSIZE*sizeof(char));` 1) the expression: `sizeof(char)` is defined in the C standard as 1, multiplying any thing by 1 has no effect and just clutters the code. Suggest removing that expression. 2) when calling any of the heap allocation functions: `malloc()` `calloc()` and/or `realloc()` always check (!=NULL) the returned value to assure the operation was successful. If not successful, call `perror( "your error message");` to output both your error message and the text reason the system thinks the error occurred to `stderr` – user3629249 May 04 '20 at 02:45
  • OT: regarding: `srand(start);` the function: `srand()` expects a parameter with type: `unsigned int` NOT `time_t` so the value in `start` should be cast, i.E. `srand( (unsigned)start);` – user3629249 May 04 '20 at 02:58
  • When compiling, ALWAYS enable the warnings, then fix those warnings. ( for `gcc`, at a minimum use: `-Wall -Wextra -Wconverson -pedantiic -std=gnu11` ) Note: other compilers use different options to produce the same results – user3629249 May 04 '20 at 02:59
  • The variables: `max_p` and `max_c` are not defined in the posted code. – user3629249 May 04 '20 at 03:03
  • OT: regarding: ` pid_t prod_p = fork()` and similar statements: The function: `fork()` returns 3 kinds of values: <0 means an error occurred ==0 means in the child process >0 means in the parent process. The code needs to check for all three conditions after each call to `fork()` – user3629249 May 04 '20 at 03:05
  • regarding: `while(time(NULL)-start – user3629249 May 04 '20 at 03:11
  • regarding: `check = read(infd, &input, 1);//read from the input pipe if(check !=-1)` 1) the function: `read()` returns type `ssize_t`, not `int`. 2) it can also return 0, as when the writer to the pipe closes the connection AND `read()` can exit due to some error. The code needs to be checking for all three conditions – user3629249 May 04 '20 at 03:14
  • regarding: `if(buff[inputind]=='*')` since `inputind` rolls over back to 1, this statement will NOT determine if there is more '*' to receive, – user3629249 May 04 '20 at 03:19
  • thanks for the readthrough @user3629249 . Lots of ways I can clean up my code. – Ploof May 04 '20 at 03:20
  • regarding: `const int BUFFSIZE = 1000;` This great for C++, but in C it is much better to have: `#define BUFFSIZE 1000` – user3629249 May 04 '20 at 03:21
  • @user3629249: Be cautious. Microsoft designates `void main(void)` as a standard signature; it is not incorrect to use it with MSVC on Windows. Since the question is tagged Linux, your complaint is valid as long as you stipulate "on Linux". As long as you've got the declaration of `srand()` in scope (`#include `), the compiler will use the prototype to convert `time_t` to `unsigned int` automatically — the cast is not necessary. That's what prototypes are for (or, at least, one of the things they're for). – Jonathan Leffler May 04 '20 at 04:52
  • @JonathanLeffler, What microsoft allows does NOT mean that it is a valid part of the C language, nor that it is portable – user3629249 May 04 '20 at 15:41

0 Answers0