flux_pollevents(3)

SYNOPSIS

#include <flux/core.h>

int flux_pollevents (flux_t *h);

int flux_pollfd (flux_t *h);

DESCRIPTION

flux_pollevents() returns a bitmask of poll flags for handle h.

flux_pollfd() obtains a file descriptor that becomes readable, in an edge triggered fashion, when flux_pollevents() has poll flags raised.

Valid poll flags are:

FLUX_POLLIN
Handle is ready for reading.
FLUX_POLLOUT
Handle is ready for writing.
FLUX_POLLERR
Handle has experienced an error.

These functions can be used to integrate a flux_t handle into an external event loop. They are analogous to the ZMQ_FD and ZMQ_EVENTS socket options provided by ZeroMQ.

RETURN VALUE

flux_pollevents() returns flags on success. On error, -1 is returned, and errno is set appropriately.

flux_pollfd() returns a file descriptor on success. On error, -1 is returned, and errno is set appropriately.

ERRORS

EINVAL
Some arguments were invalid.

EXAMPLES

Here is an example of a libev "composite watcher" for a Flux handle using the hooks provided above. This code, more or less, is used internally to integrate flux handles into the Flux reactor, which is based on libev. Refer to the libev documentation for background on how libev works.

There are a total of four different types of libev watcher in the composite watcher. libev "prepare" and "check" callbacks are executed just before and just after libev blocks internally in the poll(2) system call. Here they are used to test flux_pollevents(), make user callbacks, and enable/disable no-op "io" and "idle" watchers. The io watcher watches for EV_READ on flux_pollfd() file descriptor. The idle watcher, if enabled, is always ready and thus causes the event loop to spin.

When flux_pollevents() has poll flags asserted, the idle watcher is enabled. When flux_pollevents() has no poll flags asserted, the idle watcher is disabled and the io watcher is enabled. While the idle and io watchers have no callbacks, if either is enabled and ready, the event loop must execute the prepare and check callbacks.

The net results are 1) the edge-triggered notification provided by flux_pollfd() is integrated with libev's level-triggered watcher processing; 2) the handle is able to give control back to the event loop between handle event callbacks to preserve fairness, i.e. it doesn't have to consume events until they they are gone in one callback; and 3) the event loop is able to sleep when there are no handle events pending.

// ev_flux.h
#include <ev.h>

struct ev_flux;

typedef void (*ev_flux_f)(struct ev_loop *loop,
                          struct ev_flux *w, int revents);

struct ev_flux {
    ev_io       io_w;
    ev_prepare  prepare_w;
    ev_idle     idle_w;
    ev_check    check_w;
    flux_t      *h;
    int         pollfd;
    int         events;
    ev_flux_f   cb;
    void        *data;
};
// ev_flux.c
static int get_pollevents (flux_t *h)
{
    int e = flux_pollevents (h);
    int events = 0;
    if (e < 0 || (e & FLUX_POLLERR))
        events |= EV_ERROR;
    if ((e & FLUX_POLLIN))
        events |= EV_READ;
    if ((e & FLUX_POLLOUT))
        events |= EV_WRITE;
    return events;
}

static void prepare_cb (struct ev_loop *loop, ev_prepare *w,
                        int revents)
{
    struct ev_flux *fw = (struct ev_flux *)((char *)w
                       - offsetof (struct ev_flux, prepare_w));
    int events = get_pollevents (fw->h);

    if ((events & fw->events) || (events & EV_ERROR))
        ev_idle_start (loop, &fw->idle_w);
    else
        ev_io_start (loop, &fw->io_w);
}

static void check_cb (struct ev_loop *loop, ev_check *w,
                      int revents)
{
    struct ev_flux *fw = (struct ev_flux *)((char *)w
                       - offsetof (struct ev_flux, check_w));
    int events = get_pollevents (fw->h);

    ev_io_stop (loop, &fw->io_w);
    ev_idle_stop (loop, &fw->idle_w);

    if ((events & fw->events) || (events & EV_ERROR))
        fw->cb (loop, fw, events);
}

int ev_flux_init (struct ev_flux *w, ev_flux_f cb,
                  flux_t *h, int events)
{
    w->cb = cb;
    w->h = h;
    w->events = events;
    if ((w->pollfd = flux_pollfd (h)) < 0)
        return -1;

    ev_prepare_init (&w->prepare_w, prepare_cb);
    ev_check_init (&w->check_w, check_cb);
    ev_idle_init (&w->idle_w, NULL);
    ev_io_init (&w->io_w, NULL, w->pollfd, EV_READ);

    return 0;
}

void ev_flux_start (struct ev_loop *loop, struct ev_flux *w)
{
    ev_prepare_start (loop, &w->prepare_w);
    ev_check_start (loop, &w->check_w);
}

void ev_flux_stop (struct ev_loop *loop, struct ev_flux *w)
{
    ev_prepare_stop (loop, &w->prepare_w);
    ev_check_stop (loop, &w->check_w);
    ev_io_stop (loop, &w->io_w);
    ev_idle_stop (loop, &w->idle_w);
}