Dasynq manual

This is the manual for the Dasynq event loop library (version 1.2.4).

Introduction

An event loop library provides a means for waiting on events that occur asynchronously. One good example is network input/output; in a server with multiple client connections, a mechanism is needed to wait until data is available, or until it is possible to write data, to one or more of the current connections (and to be able to identify which connections are ready). Dasynq is a multi-platform, thread-safe C++ library which provides such functionality.

Note that an event loop generally supports managing various different kinds of event. Dasynq can be used for detecting:

It also supports one-shot and periodic timers.

Dasynq is fully thread safe, allowing events to be polled and processed on any thread. There are some limitations on the use the Dasynq API in a multi-threaded application. However, when used in a single-thread application, the API is just about as straight-forward as the API of most other event loop libraries.

Dasynq is also intended to allow development of extremely robust client applications. Where possible, it allows pre-allocation of resources to prevent allocation failures from occurring at inopportune moments during program execution.

Caveat

It should be possible for an application (whether using Dasynq or not) to be able to gracefully handle all error situations that it could be faced with. This is a necessary pre-requisite for a truly stable system—one where important programs do not terminate unexpectedly. Part of handling errors gracefully is being able to control when they can occur; it needs to be possible to allocate resources up-front, rather than just as they are needed. Dasynq is designed to allow, as much as possible, for exactly that, and to have well-defined error modes so that it is possible to produce robust and stable software. However, unfortunate external implementation and interface details can prevent this goal from being realised properly (regardless of whether Dasynq is used).

One of these external details is related to an aspect of Dasynq that is, arguably, avoidable. The Itanium C++ ABI, which is the basis for the C++ ABI on many platforms (for instance it is the ABI which the Sys V ABI for x86-64 defers to), has one very unfortunate flaw: throwing an exception requires making a heap allocation, and if the allocation fails, the process terminates. Implementations have an "emergency allocation pool" for exceptions which should mean that the allocation fails only in truly exceptional circumstances, but the possibility is there. Unfortunately Dasynq at this stage sometimes uses exceptions (in some cases exposing them as part of its API, in others handling them internally) and in theory could trigger process termination on these platforms. I consider this a bug in the ABI and in the platforms implementing it, however a future version of Dasynq may endeavour to avoid use of exceptions entirely (or at least make it possible for the client to avoid unwittingly triggering exceptions).

Another unfortunate externally-imposed limitation is that the event polling mechanisms used by Dasynq typically have no means for pre-sizing. Adding a file descriptor to an epoll set, or to a kqueue, is an operation that may fail due to lack of resources (kernel memory, etc): but it fails at the wrong time, when we have already accepted the network connection (or opened the device, etc). Dasynq can't pre-determine the number of descriptors that can be added to an event set, and so it must potentially return an error condition (or throw an exception) when the operation fails, and the application must deal with it then, without reasonable warning.

I remain hopeful that such limitations can one day be lifted—but that requires changes outside of Dasynq itself. So, I suppose, we shall have to wait and see.