clinuxpipeforkinter-process-communicat

Linux-C: reading from pipe returns first buffer written to it


This program simulates a variant of Dijkstra's Producer/Consumer problem. A pipeline is first created followed by a child process using fork(). The child will then write to the pipe a crudely done randomly generated piece of "stock market ticker information". After waiting for the child/producer process to write this information, the parent/consumer process will read it out.

The first output is correct:

Produced: FPOO 57.83 +0.43
Consumed: FPOO 57.83 +0.43

Any output following this however will always say "Consumed: info from first read":

Produced: RJII 71.30 -2.71
Consumed: FPOO 57.83 +0.43

I am unsure as to why this would be occurring because my tickerInfo is changing. This is why I suspect I'm reading the pipe incorrectly or perhaps something is improperly structured with my forked processes.

I have compiled the code in g++. It takes an argument as the number of seconds you want the program to run for.

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

// generate info for each stock entry
void generateTickerInfo(char * info) {
    int i;
    int randNum = 0;
    srand(time(NULL)); // generate seed for random

    // generate 4 characters for STOCK sym
    for(i = 0; i < 4; i++) {
        randNum = rand() % 26 + 65;
        info[i] = (char)(randNum);
    }
    info[4] = ' ';

    // generate price traded
    for(i = 5; i < 7; i++) {
        randNum = rand() % 8 + 1;
        info[i] = '0' + randNum;
    }
    info[7] = '.';
    for(i = 8; i < 10; i++) {
        randNum = rand() % 9;
        info[i] = '0' + randNum;
    }
    info[10] = ' ';

    // determine if + or - for change amount
    randNum = rand();
    if(randNum % 2 == 1) {
        info[11] = '+';
    }
    else {
        info[11] = '-';
    }

    // generate change amount
    randNum = rand() % 9;
    info[12] = '0' + randNum;
    info[13] = '.';
    for(i = 14; i < 16; i++) {
        randNum = rand() % 9;
        info[i] = '0' + randNum;
    }
}

// ** constant and global variables **
const int BUFFER_SIZE = 25;

// ** main code **
int main(int argc, char *argv[]) {
    pid_t cpid; // child process id
    int myPipe[2]; // [0] read, [1] write
    char * tickerInfo; // hold current tickerInfo
    tickerInfo = new char[BUFFER_SIZE]; // info passed through pipe
    char buf[BUFFER_SIZE];
    time_t currentTime, stopTime;

    // initialize time variables
    if(argc < 2) {
        printf("Invalid arg. Type as 'foo.out (# seconds)'");
        exit(0);
    }
    else {
        currentTime = time(NULL);
        stopTime = time(NULL) + (time_t)atoi(argv[1]);
    }

    int pipeReturn = pipe(myPipe);

    if(pipeReturn == -1) { // handle pipe creation error
        perror("pipe error...");
        exit(0);
    }   

// main loop; continue until desired time has elapsed
    while(currentTime < stopTime) {             
        cpid = fork();

        if(cpid < 0) { // handle process creation error
            perror("forking error...\n");
            exit(0);
        }
        else if(cpid == 0) { // child process
            close(myPipe[0]); // child does not need to read
            generateTickerInfo(tickerInfo);     
            write(myPipe[1], tickerInfo, BUFFER_SIZE); 
            printf("Produced: %s\n", tickerInfo);
            exit(0);
        }
        else if(cpid > 0) { // parent process
            wait(0);
            close(myPipe[1]); // parent does not need to write
            read(myPipe[0], buf, BUFFER_SIZE);
            printf("Consumed: %s\n", buf);
        }
        sleep(1);
        currentTime = time(NULL);
    }
    return 0;
}

Solution

  • After the first child is done and you close the write end of the pipe in the parent process, you go back to the top of the loop and create another child. This child doesn't inherit a write fd. The write fd is gone. The write in the second child fails, but you didn't check its return value for error so you don't notice.

    The parent then reads an EOF from the pipe since there are no remaining writers. Since you didn't check the read return value either (bad habit you've got!) you don't notice that, and just print the buffer which still contains its previous contents.