libzmq/doc/zmq_ppoll.txt
E. G. Patrick Bos 36e4c9b474
add zmq_ppoll
zmq_ppoll mostly mimics zmq_poll behavior, except for the added feature of being able to specify a signal mask. Signals in this mask will be blocked during execution of zmq_ppoll. Switching of the process' active signal mask happens atomically with the actual poll call, so that no race conditions can occur. This behavior is useful when one wants to gracefully handle POSIX signals without race conditions. See e.g. the discussion below https://250bpm.com/blog:12/ for an explanation.

Also includes two new tests:
1. test_zmq_ppoll_fd does the same thing as test_zmq_poll_fd, demonstrating backwards compatibility with zmq_poll when used with a default signal mask.
2. test_zmq_ppoll_signals demonstrates the use of zmq_ppoll with a signal mask, blocking out SIGTERM everywhere except in zmq_ppoll, allowing to handle the signal in one place without having to worry about race conditions.
2021-09-24 11:04:20 +02:00

141 lines
4.8 KiB
Plaintext

zmq_poll(3)
===========
NAME
----
zmq_ppoll - input/output multiplexing with signal mask
SYNOPSIS
--------
*int zmq_ppoll (zmq_pollitem_t '*items', int 'nitems', long 'timeout', const sigset_t '*sigmask');*
DESCRIPTION
-----------
The relationship between _zmq_poll()_ and _zmq_ppoll()_ is analogous to the
relationship between poll(2) and ppoll(2) and between select(2) and
pselect(2): _zmq_ppoll()_ allows an application to safely wait until either a
file descriptor becomes ready or until a signal is caught.
When using _zmq_ppoll()_ with 'sigmask' set to NULL, its behavior is identical
to that of _zmq_poll()_. See linkzmq:zmq_poll[3] for more on this.
To make full use of _zmq_ppoll()_, a non-NULL pointer to a signal mask must be
constructed and passed to 'sigmask'. See sigprocmask(2) for more details. When
this is done, inside the actual _ppoll()_ (or _pselect()_, see note below)
system call, an atomic operation consisting of three steps is performed:
1. The current signal mask is replaced by the one pointed to by 'sigmask'.
2. The actual _poll()_ call is done.
3. The original signal mask is restored.
Because these operations are done atomically, there is no opportunity for race
conditions in between the calls changing the signal mask and the poll/select
system call. This means that only during this (atomic) call, we can unblock
certain signals, so that they can be handled *at that time only*, not outside
of the call. This means that effectively, we extend our poller into a function
that not only watches sockets for changes, but also watches the "POSIX signal
socket" for incoming signals. At other times, these signals will be blocked,
and we will not have to deal with interruptions in system calls at these other
times.
NOTE: The _zmq_ppoll()_ function may be implemented or emulated using operating
system interfaces other than _ppoll()_, and as such may be subject to the
limits of those interfaces in ways not defined in this documentation.
NOTE: There is no _ppoll_ or _pselect_ on Windows, so _zmq_ppoll()_ is not
supported in Windows builds. It is still callable, but its 'sigmask' has void
pointer type (because 'sigset_t' is also not available on Windows) and
_zmq_ppoll()_ will return with an error (see error section below).
THREAD SAFETY
-------------
The *zmq_pollitem_t* array must only be used by the thread which
will/is calling _zmq_ppoll_.
If a socket is contained in multiple *zmq_pollitem_t* arrays, each owned by a
different thread, the socket itself needs to be thead-safe (Server, Client, ...).
Otherwise, behaviour is undefined.
RETURN VALUE
------------
Upon successful completion, the _zmq_ppoll()_ function shall return the number
of *zmq_pollitem_t* structures with events signaled in 'revents' or `0` if no
events have been signaled. Upon failure, _zmq_ppoll()_ shall return `-1` and set
'errno' to one of the values defined below.
ERRORS
------
*ETERM*::
At least one of the members of the 'items' array refers to a 'socket' whose
associated 0MQ 'context' was terminated.
*EFAULT*::
The provided 'items' was not valid (NULL).
*EINTR*::
The operation was interrupted by delivery of a signal before any events were
available.
*EINTR*::
The operation was interrupted by delivery of a signal before any events were
available.
*ENOTSUP*::
_zmq_ppoll()_ was not activated in this build.
EXAMPLE
-------
.Polling indefinitely for input events on both a 0MQ socket and a standard socket.
See the _example section_ of linkzmq:zmq_poll[3]. One only needs to replace
the _zmq_poll_ call with _zmq_ppoll_ and add a _NULL_ argument for the 'sigmask'
parameter.
.Handle SIGTERM during _zmq_ppoll_ (and block it otherwise).
----
// simple global signal handler for SIGTERM
static bool sigterm_received = false;
void handle_sigterm (int signum) {
sigterm_received = true;
}
// set up signal mask and install handler for SIGTERM
sigset_t sigmask, sigmask_without_sigterm;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGTERM);
sigprocmask(SIG_BLOCK, &sigmask, &sigmask_without_sigterm);
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = handle_sigterm;
// poll
zmq_pollitem_t items [1];
// Just one item, which refers to 0MQ socket 'socket' */
items[0].socket = socket;
items[0].events = ZMQ_POLLIN;
// Poll for events indefinitely, but also exit on SIGTERM
int rc = zmq_poll (items, 2, -1, &sigmask_without_sigterm);
if (rc < 0 && errno == EINTR && sigterm_received) {
// do your SIGTERM business
} else {
// do your non-SIGTERM error handling
}
----
SEE ALSO
--------
linkzmq:zmq_poll[3]
linkzmq:zmq_socket[3]
linkzmq:zmq_send[3]
linkzmq:zmq_recv[3]
linkzmq:zmq[7]
Your operating system documentation for the _poll()_ system call.
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.