c++libeventevent-based-programming

fd0 vs fd1 on libevent event_base


I have a simple event_base setup where I create an event to do a callback whenever data on fd 0 is ready

void callback(int fd, short event, void* arg) { // (1)
  string s;
  cin >> s;
  cout << "I am in callback" << endl;
}

int main(int argc, char* argv[]) {
  event_base* eb = event_base_new();
  event* event = event_new(eb, 0, EV_READ | EV_PERSIST, callback, NULL);
  event_add(event, nullptr);
  event_base_dispatch(eb);
  return 0;
}

Now in this case, when I run the program and enter a keyword, "hello", callback() gets called as expected and it prints out "I am in callback", however, since this print happens on the shell, which is where fd0, fd1 point to, why doesn't the callback trigger again because data is ready for fd0 again right since fd0 points to shell only?

I understand that STDIN and STDOUT are 2 different things, but they both point to shell only


Solution

  • When you execute your program from a shell running in a terminal emulator, assuming you didn't do any extra redirection, you'll end up with file descriptors 0, 1, and 2 (stdin, stdout, and stderr) all referencing the slave end of a pseudo-terminal device (abbreviated pty) that the terminal emulator owns the master end of.

    That is, you have this: A process and its file descriptors referencing a pty device

    A pseudo-terminal is essentially a bidirectional pipe (with some extra functionality like raising SIGINT when a ^C character is written to the master end). That is, data that gets written to the master end can be read from the slave end, and data that gets written to the slave end can be read from the master end. That is, data flows like this: Data flow through a pty device

    It DOES NOT flow like this: Wrong data flow through a pty

    That means that when your process writes to its stdout or stderr (or stdin, but you shouldn't) file descriptors the terminal emulator can read that data and print it out to the screen, and when you type data into the terminal emulator, it will write that data to its end of the pty device and your process can read that data via its stdin (or stdout or stderr, but again, you shouldn't) file descriptor.

    Data written to the slave end of the pty cannot be read back from the slave end though. Only from the master end. That means that data your writes to its stdout file descriptor is not available to be read from its stdin file descriptor, since both reference the slave end of the pty.

    Notice that this pty device is completely separate from the way the terminal emulator presents the data. The fact that data you type into your terminal emulator and data your process writes to the pty device usually get written to the same window is irrelevant. The terminal emulator could just as well send its output to a teletype to be printed onto paper or it could commission a skywiter to fly over your house and write your process's output in smoke and the pty mechanism by which your process exchanges data with the terminal emulator would be completely unchanged.


    Returning to your original question: callback does not get executed when you write to stdout because you've configured libevent to call callback when data is available to be read from stdin, and data written to stdout is not available to be read from stdin. It can only be read from the master end of the pty device, and only the terminal emulator has a file descriptor referencing that. stdout and stdin both reference the slave end of the pty, and data written to the slave end cannot be read back from the slave end.