.. ot-topic:: sysprog.eventloop.poll :dependencies: sysprog.eventloop.problem .. include:: I/O Multiplexing ================ .. topic:: Trainer's note Continue with the code that we left :ref:`in the introduction ` The Paradigm ------------ * Watch multiple file descriptors * Input * Output * Exceptional conditions * *Block* until at least one of these conditions is true - e.g. data can be read from a socket *without blocking* * Perform the desired action (read from socket) .. image:: paradigm.svg :scale: 40% Enter ``poll()`` ---------------- .. topic:: Documentation * `man -s 2 poll `__ .. code-block:: console #include struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; int poll(struct pollfd *fds, nfds_t nfds, int timeout); * ``timeout`` in milliseconds (0 for "don't block", -1 for infinite) * Original I/O multiplexing system call (since 4.2 BSD UNIX): ``select()`` (`man -s 2 select `__) * ``poll()`` was added to POSIX later * |longrightarrow| More resource (and developer) friendly .. _sysprog-eventloop-poll-events: Possible Events --------------- From `man -s 2 poll `__ (reformatted and condensed): .. list-table:: :align: left * * ``POLLIN`` * There is data to read. * * ``POLLOUT`` * Writing is now possible, though a write larger than the available space in a socket or pipe will still block (unless ``O_NONBLOCK`` is set). * * ``POLLPRI`` * Some exceptional condition on the file descriptor, for example out-of-band data on a TCP socket (`man -s 7 tcp `__). * * ``POLLRDHUP`` * Stream socket peer closed connection, or shut down writing half of connection. * * ``POLLERR`` * Error condition (``revents`` only). For example watching the write-end of a pipe for input. * * ``POLLHUP`` * Hang up (``revents`` only). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed. * * ``POLLNVAL`` * Invalid request: fd not open (``revents`` only). .. _sysprog-eventloop-poll-solution: Solution: Read From Two Input Sources ------------------------------------- Continuing from :ref:`where we left ` ... * Two input sources * Standard input * UDP socket * Use ``poll()`` to wait for both (no ``timeout`` |longrightarrow| -1) * Multiplex on return .. image:: flowchart.svg :scale: 40% .. literalinclude:: code/main.cpp :language: c++ :caption: :download:`code/main.cpp` .. _sysprog-poll-db-testing: Testing ------- * In one terminal, start the program |longrightarrow| sits and waits in ``poll()`` .. code-block:: console $ ./sysprog-eventloop-db-poll-solution * Feed it a record on standard input and watch it entered into the database .. code-block:: console 42 Joerg Faschingbauer insert id=42, firstname=Joerg, lastname=Faschingbauer * In another terminal, send a UDP packet containing another record |longrightarrow| inserted too .. code-block:: console $ nc -4 --udp localhost 1234 666 Bad Guy * ``Crtl-d`` On standard input .. code-block:: console ... commit $ Afterword --------- * Of course this works for more than two sources * Many possible kinds of events; for example: * :doc:`../exercise-graceful-termination/index` * :doc:`../exercise-commit-rollback/index` * |longrightarrow| Code complexity ahead * |longrightarrow| best to encapsulate (see :doc:`here <../poll-cpp/index>` for a naive C++ eventloop implementation)