From b4910319e028f5853c968ac2556426a59f3d12fe Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 19 Dec 2018 12:24:16 -0800 Subject: [PATCH] ircd: Add various comments / documentations. --- include/ircd/README.md | 7 +++++ ircd/README.md | 62 ++++++++++++++++++++++++++++-------------- ircd/fs.cc | 19 +++++++++++++ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/include/ircd/README.md b/include/ircd/README.md index 0e65d7aca..128165857 100644 --- a/include/ircd/README.md +++ b/include/ircd/README.md @@ -48,3 +48,10 @@ standard include group which includes any wrapping to hide SpiderMonkey. - MAPI include group `` is the standard header group for modules. This group is an extension to the standard include group but has specific tools for pluggable modules which are not part of the libircd core. + +#### Invocation + +> `libircd` is **not thread-safe**. It does not protect exposed interfaces with +locks. Embedders must access `libircd` from a single thread. + +Initialize the library with a call to `ircd::init()`. diff --git a/ircd/README.md b/ircd/README.md index 4457c5d61..5d86172a9 100644 --- a/ircd/README.md +++ b/ircd/README.md @@ -5,31 +5,36 @@ This directory contains definitions and linkage for `libircd` The purpose of `libircd` is to facilitate the execution of a server which handles requests from end-users. The library hosts a set of pluggable modules which may introduce the actual application features (or the "business logic") -of the server. These additional modules are found in the `modules/` directory; +of the server. -##### libircd can be embedded in your application with very minimal overhead. - -Linking to libircd from your executable allows you to customize and extend the -functionality of the server and have control over its execution, or, simply use -library routines provided by the library without any daemonization. - -##### libircd runs only one server at a time. - -Keeping with the spirit of simplicity of the original architecture, `libircd` -continues to be a "singleton" object which uses globals and keeps actual server -state in the library itself. In other words, **only one IRC daemon can exist -within a process's address space at a time.** Whether or not this was a pitfall -of the original design, it has emerged over the decades as a very profitable -decision for making IRCd an accessible open source internet project. +> The executable linking and invoking `libircd` may be referred to as the +"embedding" or just the "executable" interchangably in this documentation. ##### libircd is single-threaded✝ -This methodology ensures there is an _uninterrupted_, _uncontended_, -_predictable_ execution. If there are periods of execution which are -computationally intense like parsing, hashing, cryptography, etc: this is -absorbed in lieu of thread synchronization and bus contention. Scaling this -system is done through running multiple independent instances which -synchronize with application logic. +The design of `libircd` is fully-asynchronous, single-thread-oriented. No code +in the library _blocks_ the process. All operations are conducted on top of +a single `boost::asio::io_service` which must be supplied by the executable +linking to `libircd`. That `io_service` must be orchestrated by the executable +at its discretion; typically the embedder's call to `ios.run()` is the only +place the process will _block_. + +> Applications are limited by one or more of the following bounds: +- Computing: program is limited by the efficiency of the CPU over time. +- Space: program is limited by the space available for its dataset. +- I/O: program is limited by external events, disks, and networks. + +`libircd` is dominated by the **I/O bound**. Its design is heavily optimized +for this assumption with its single-thread orientation. This methodology +ensures there is an _uninterrupted_, _uncontended_, _predictable_ execution +which is easy for developers to reason about intuitively with +sequential-consistency in a cooperative coroutine model. + +If there are periods of execution which are computationally intense like +parsing, hashing, cryptography, etc: this is absorbed in lieu of thread +synchronization and bus contention. This system achieves scale through running +multiple independent instances which synchronize at the application-logic +level. ✝ However, don't start assuming a truly threadless execution for the entire address space. If there is ever a long-running background computation or a call @@ -48,6 +53,21 @@ easy to follow. ✝ If there are certain cases where we don't want a stack to linger which may jeopardize the c10k'ness of the daemon the asynchronous pattern is still used. +##### libircd can be embedded in your application with very minimal overhead. + +Linking to libircd from your executable allows you to customize and extend the +functionality of the server and have control over its execution, or, simply use +library routines provided by the library without any daemonization. + +##### libircd runs only one server at a time. + +Keeping with the spirit of simplicity of the original architecture, `libircd` +continues to be a "singleton" object which uses globals and keeps actual server +state in the library itself. In other words, **only one IRC daemon can exist +within a process's address space at a time.** Whether or not this was a pitfall +of the original design, it has emerged over the decades as a very profitable +decision for making IRCd an accessible open source internet project. + ##### libircd leverages formal grammars We utilize the `boost::spirit` system of parsing and printing through formal grammars, diff --git a/ircd/fs.cc b/ircd/fs.cc index 0e71e4fb0..aa2d2ef87 100644 --- a/ircd/fs.cc +++ b/ircd/fs.cc @@ -672,6 +672,13 @@ ircd::fs::read(const string_view &path, return read(fd, bufs, opts); } +/// Read from file descriptor fd into buffers. The number of bytes read into +/// the buffers is returned. By default (via read_opts.all) this call will +/// loop internally until the buffers are full or EOF. To allow for a partial +/// read(), disable read_opts.all. Note that to maintain alignments (i.e when +/// direct-io or for special files read_opts.all must be false). By default +/// (via read_opts.interruptible) this call can throw if the syscall was +/// interrupted before reading any bytes. size_t ircd::fs::read(const fd &fd, const mutable_buffers &bufs, @@ -700,6 +707,12 @@ ircd::fs::read(const fd &fd, return ret; } +/// Lowest-level read() call. This call only conducts a single operation +/// (no looping) and can return a partial read(). It does have branches +/// for various read_opts. The arguments involve `struct ::iovec` which +/// we do not expose to the ircd.h API; thus this function is internal to +/// ircd::fs. There is no reason to use this function in lieu of the public +/// fs::read() suite. size_t ircd::fs::read(const fd &fd, const const_iovec_view &iov, @@ -941,6 +954,12 @@ ircd::fs::write(const fd &fd, return off; } +/// Lowest-level write() call. This call only conducts a single operation +/// (no looping) and can return early with a partial write(). It does have +/// branches for various write_opts. The arguments involve `struct ::iovec` +/// which we do not expose to the ircd.h API; thus this function is internal to +/// ircd::fs. There is no reason to use this function in lieu of the public +/// fs::read() suite. size_t ircd::fs::write(const fd &fd, const const_iovec_view &iov,