fd_watcher, fd_watcher_impl

    // Members of dasynq::event_loop<T> instantiation:

    class fd_watcher;

    template <class Derived> class fd_watcher_impl; // : public fd_watcher;

fd_watcher

Brief: fd_watcher is a member type of the event_loop template class. It represents an event watcher for file-descriptor readiness events; a registered fd_watcher will receive callbacks when its associated file descriptor is ready for reading or writing (or both, if supported by the event loop back-end). The fd_watcher class should not be subclassed directly; the fd_watcher_impl template provides a means for subclassing.

Members

Types

Constructors

Functions

Protected functions

Details and Usage

Note: also see the watcher constraints section.

A file descriptor often represents an input or output channel (or both) for which I/O readiness is a detectable event. For example, a connected network socket becomes ready for input when data is received over the network, and ready for output when the output buffer is not full (or is below some threshold) due to data being successfully transmitted. An fd_watcher implementation receives notification of readiness events on a file descriptor.

When registering an fd_watcher you specify which events you are interested in using either of the IN_EVENTS and OUT_EVENTS flags. Some event loop backends support watching for both input and output readiness on the same file descriptor using a single watcher (epoll style) — this is supported by specifying both flags, as IN_EVENTS | OUT_EVENTS — and others require separate watchers for input and output (kqueue style). To avoid compatibility issues it is recommended to use bidi_fd_watcher rather than fd_watcher in order to watch for both input and output events on a single file descriptor.

Note that backends do not generally support more than one watcher on the same file descriptor (or more than one watcher for either reading or writing). Attempting to register multiple watchers for the same file descriptor has unspecified behaviour if not supported by the backend.

Subclassing fd_watcher

To specify callback behaviour, fd_watcher can be subclassed — however, it should not be directly subclassed; instead, use the fd_watcher_impl implementation wrapper template.

Regular files

In general readiness notification for regular files is not supported by event loop backends, and arguably a file is always ready for reading and writing (although the operation may block). However in some cases it is convenient to treat such file descriptors as if they were connected to pipes or sockets; fd_watcher by default supports emulation of readiness events for regular files, which assumes they are always ready; this can be prevented using the "noemu" variant of the watch registration functions.

Note that some backends do support regular files. In particular, kqueue supports read but not write watches on regular files, though with slightly modified semantics: at end-of-file, the file descriptor no longer reports as being ready-for-read. On FreeBSD, kqueue also directly supports the preferred semantics where a file is always considered ready for reading. Portable code should not rely on any particular semantics for the end-of-file condition if emulation is disabled.

Listening sockets

An fd_watcher can be used to detect incoming connections on a listening socket. New connections will be signalled as input readiness.

A special problem, not specific to Dasynq, can occur when the process has reached its file descriptor limit before a new connection arrives on a listening socket. In this case, it will be impossible to accept the new connection and it will remain pending, causing input readiness to be reported repeatedly. There are no solutions to this problem that are satisfactory in all cases; it is an issue inherent in the POSIX API.

Closing files

Any watchers for a file descriptor should be removed or disabled before closing the file descriptor. This is due to limitations in back-ends.

There are two specific cases which are problematic:

If the above situations can be avoided, it may be safe to close file descriptors before removing their watchers. However, it is recommended to avoid this if possible.

add_watch (#2)

// member of dasynq::event_loop<T>::fd_watcher
template <typename T>
static fd_watcher<event_loop_t> *add_watch(event_loop_t &eloop, int fd, int flags, T watch_hndlr)

This variant of the add_watch function can be used to create and register a dynamically-allocated fd_watcher. The first three parameters are the same as for add_watch(#1). The watch_hndlr parameter is a function or lambda of the form:

[](event_loop_t &eloop, int fd, int flags) -> rearm { ... }

It acts as the callback function for the generated watcher. The watcher will delete itself when it is removed from the event loop.

This function can throw std::system_error or std::bad_alloc on failure.


fd_watcher_impl

Brief: The fd_watcher_impl provides a basis for implementing fd_watcher, using the "curiously recurring template pattern". Instead of subclassing fd_watcher directly, subclass an instantiation of fd_watcher_impl with the template parameter specified as the subclass itself. For example:

class my_watcher : public event_loop_t::fd_watcher_impl<my_watcher>
{
    // ...
}

Details and Usage

The callback function must be provided in the subclass and named fd_event, with a signature compatible with the following:

rearm fd_event(event_loop_t & loop, int fd, int flags);

The fd_event function must be public, but need not be virtual. It will be called with the following parameters:

Error conditions (flagged by the presence of dasynq::ERR_EVENTS) include, for example, when the file descriptor refers to a socket to which data has been written but the other end of the connection has been closed. An error condition may not relate to the specific read or write operation that is otherwise appropriate for the flags, however, an error condition will not be signalled without either of dasynq::IN_EVENTS or dasynq::OUT_EVENTS also being set.

The return value specifies the rearm action.

An fd_watcher_impl instantiation has no public or protected members, other than those inherited from fd_watcher, which it publicly derives from.