From OrganicDesign Wiki
1 General
What is multiplexing?
2 Multiplexing in the Nodal Model
3 Server-specific multiplexing
- The multiplexing server in peerd is defined in io.c
Some other projects are seeing the benefits of multiplexed event-based models too...
Developers often wish to write applications which serve as a mediator between several logical interfaces simultaneously; in fact, most applications work this way. For example, a browser application might wish to maintain a user interface while also managing a network connection and occasionally exchanging data with the local filesystem. A server application might be communicating with several clients at once while also occasionally receiving a signal from the administrator directing it to reload its configuration. A multiplayer game might want to maintain several active user interfaces at once.
Furthermore, each of these interfaces may be quite complex, sufficiently so to merit shared code modules which specialize in managing the interface. Widget sets deal with the details of the X protocol and graphical user interface management; "curses" deals with the arcana of character-based terminals; WWW libraries offer high-level access to whole families of Internet transfer protocols; standard I/O and database routines manage filesystem data.
However, the existing techniques available for multiplexing interface code are very poor. Most of these libraries work in "blocking" fashion; once instructed to complete a task (such as downloading a file, or presenting a dialog to the user), they do not return until the task is complete (or failed), even though this may mean waiting an arbitrary amount of time for some external agent (such as the user or the network) to respond. Some of the better systems are able to manage several concurrent tasks internally, but cannot work with other components.
Developers are thus left with several unpalatable choices:
- Accept "blocking" operation. User interfaces stop functioning while the application waits for the network; one network client's access is stalled while another client performs a transaction. As more data moves from local storage (where access is fast enough that blocking is acceptable) to delay-prone networked media, this is becoming less and less acceptable.
- Use multiple threads for concurrency. While this is a good solution for some problems, developers who choose this route must struggle with relatively immature and unportable threading models, and deal with the many libraries which are not thread-safe; furthermore, threaded programming requires thought-intensive and error-prone synchronization.
- Use multiple processes ("forking") for concurrency. This can also work, but requires all communication between modules to use some form of inter-process communication, which increases complexity and decreases performance. Forking itself is a slow operation, leading to complex "pre-forking" schemes for better performance. Worst of all, each process must somehow multiplex IPC from other processes with whatever I/O task it had to accomplish in the first place; this brings back the very problem forking was designed to address.
- Attempt to multiplex each library's I/O operations directly in a master "select loop". This requires the developer to understand intimately the exact details of each library's I/O interactions, thus breaking modularity, fostering unhealthy dependency and leading to a single central snarl through which all I/O must pass.
The paucity of options is reflected in the quality of applications. How many programs hang unpleasantly while performing simple network operations like hostname resolution? How many user interfaces are unnecessarily "modal"? How many simple servers fork for no good reason? How many network applications simply don't exist because it's so difficult to write them?
4 See also