0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-12 13:01:07 +01:00

Update various documentation and comments.

This commit is contained in:
Jason Volk 2017-09-12 09:37:44 -07:00
parent 7cc4ed56ab
commit 54d6793f59
36 changed files with 457 additions and 306 deletions

View file

@ -98,17 +98,5 @@ old code has been rewritten but with the same architecture and spirit of the ori
## Developers ## Developers
### Style * Generate doxygen using `/usr/bin/doxygen tools/doxygen.conf` the target
directory is doc/html. Browse to doc/html/index.html
#### Misc
* When using a `switch` over an `enum` type, put what would be the `default` case after/outside
of the `switch` unless the situation specifically calls for one. We use -Wswitch so changes to
the enum will provide a good warning to update any `switch`.
* Prototypes should name their argument variables to make them easier to understand, except if
such a name is redundant because the type carries enough information to make it obvious. In
other words, if you have a prototype like `foo(const std::string &message)` you should name
`message` because std::string is common and *what* the string is for is otherwise opaque.
OTOH, if you have `foo(const options &, const std::string &message)` one should skip the name
for `options &` as it just adds redundant text to the prototype.

53
charybdis/README.md Normal file
View file

@ -0,0 +1,53 @@
# Charybdis Server
Charybdis is the executable running `libircd`. This application provides an
interface for the server administrator to start, stop, configure and locally
communicate with the daemon. It sets up an `asio::io_service` which is passed
to `libircd`, then it sets up signal handling, and then it runs the ios event
loop until commanded to exit.
This program executes in the foreground. It does not "daemonize" anymore with a
`fork()` etc. You are free to use your shell to execute or move the program
to the background, or simply use a `tmux` or `screen`. Charybdis will output
the libircd log to stdout and stderr by default.
### Signals
Charybdis handles certain POSIX signals and their behavior is documented
below. Signals are only handled here in the Charybdis executable; libircd
itself does not use any signal logic (that we know about).
* Signal handling is accomplished through `boost::asio`'s mechanism which
installs a handler to intercept the signal's delivery and posts it to the
event loop for execution at the next event slice. This is how signal safety
is achieved. Furthermore, according to boost docs, when signals are used
this way they can be compatible with windows environments.
##### SIGINT
A `ctrl-c` to Charybdis will bring up the command line console interface. It
will not halt the daemon. Log messages will be suppressed while the console
is waiting for input, but service is still continuing in the background.
##### SIGTSTP
A `ctrl-z` or "Terminal SToP" to Charybdis will bring up the command line
console like with `SIGINT` except that the entire daemon will pause while
waiting for console input. When paused, a second `SIGTSTP` will exhibit default
behavior so your shell can offer its functionality here.
##### SIGHUP
A "HangUP" to Charybdis is only relevant to the command line console, and
signals it to close like an `EOF`. The legacy functionality for reloading
server configuration et al is moved to `SIGUSR1`.
##### SIGQUIT
A `ctrl-\` to Charybdis will cleanly shut down the server. It will not generate
a coredump.
##### SIGUSR1
This signal commands the server to reload and refresh various aspects of its
configuration and running state.

61
include/ircd/README.md Normal file
View file

@ -0,0 +1,61 @@
# IRCd Library
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 introduce the actual application features (or the "business logic") of
the server. These additional modules are found in the `modules/` directory;
see the section for `Developing a module` for more information. This library
can be embedded by developers creating their own server or those who simply
want to use the routines it provides; see the section for `Using libircd`.
### Using libircd
`libircd` can be embedded in your application. This 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.
The prototypical embedding of `libircd` is `charybdis` found in the `charybdis/`
directory.
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 other words, only one IRC daemon can exist within a process's address
space at any time. This is actually a profitable design decision for making
IRCd easier to understand for contributors. The original version of this library
was created at the dawn of the era of dynamic shared objects and began as an
abstraction of code from the server executable. This was done so that additional
feature modules could be created while all sharing the same maps of routines.
The library is based around the `boost::asio::io_service` event loop. It is
nominally single threaded and serializes operations on a single asio strand.
In other words, most code is executed on the thread where you call `ios.run()`;
this is referred to as the "main thread." If ios.run() is called on multiple
threads no concurrency will occur. IRCd occasionally uses global and static
variables; the expectation is that these will not be contended outside of the
main thread. The library may spawn additional threads, mostly from 3rd party
libraries and only under daemonization. We don't like this, and try to prevent
it, but it may happen under certain circumstances. These are all dealt with
internally and shouldn't affect the users of the library.
### Developing a module
libircd facilitates the development of dynamic shared modules which implement
specific application logic used in the server.
### Hacking on libircd
#### Style
##### Misc
* When using a `switch` over an `enum` type, put what would be the `default` case after/outside
of the `switch` unless the situation specifically calls for one. We use -Wswitch so changes to
the enum will provide a good warning to update any `switch`.
* Prototypes should name their argument variables to make them easier to understand, except if
such a name is redundant because the type carries enough information to make it obvious. In
other words, if you have a prototype like `foo(const std::string &message)` you should name
`message` because std::string is common and *what* the string is for is otherwise opaque.
OTOH, if you have `foo(const options &, const std::string &message)` one should skip the name
for `options &` as it just adds redundant text to the prototype.

View file

@ -20,6 +20,7 @@
#pragma once #pragma once
#define HAVE_IRCD_COLOR_H #define HAVE_IRCD_COLOR_H
/// Legacy mIRC color swatch
namespace ircd::color namespace ircd::color
{ {
enum mode enum mode

View file

@ -25,23 +25,23 @@
#pragma once #pragma once
#define HAVE_IRCD_CTX_H #define HAVE_IRCD_CTX_H
// /// Userspace Contexts: cooperative threading from stackful coroutines.
// This is the public interface to the userspace context system. No 3rd party ///
// symbols are included from here. This file is included automatically in stdinc.h /// This is the public interface to the userspace context system. No 3rd party
// and you do not have to include it manually. /// symbols are included from here. This file is included automatically in stdinc.h
// /// and you do not have to include it manually.
// There are two primary objects at work in the context system: ///
// /// There are two primary objects at work in the context system:
// `struct context` <ircd/ctx/context.h> ///
// Public interface emulating std::thread; included automatically from here. /// `struct context` <ircd/ctx/context.h>
// To spawn and manipulate contexts, deal with this object. /// Public interface emulating std::thread; included automatically from here.
// /// To spawn and manipulate contexts, deal with this object.
// `struct ctx` (ircd/ctx.cc) ///
// Internal implementation of the context. This is not included here. /// `struct ctx` (ircd/ctx.cc)
// Several low-level functions are exposed for library creators. This file is usually /// Internal implementation of the context. This is not included here.
// included when boost/asio.hpp is also included and calls are actually made into boost. /// Several low-level functions are exposed for library creators. This file is usually
// /// included when boost/asio.hpp is also included and calls are actually made into boost.
///
namespace ircd::ctx namespace ircd::ctx
{ {
using std::chrono::steady_clock; using std::chrono::steady_clock;

View file

@ -46,12 +46,12 @@ namespace ircd::ctx
/// the constructor; the execution will then be posted to the io_service /// the constructor; the execution will then be posted to the io_service
/// event queue instead. `DISPATCH` is an alternative, see boost::asio docs. /// event queue instead. `DISPATCH` is an alternative, see boost::asio docs.
/// ///
/// Unlike std::thread, when this object goes out of scope the context is /// When this object goes out of scope the context is interrupted and joined
/// interrupted and joined if it has not been already; the current context /// if it has not been already; the current context will wait for this to
/// will wait for this to complete. If the child context does not cooperate /// complete. If the child context does not cooperate the destructor will hang.
/// the destructor will hang. To prevent this behavior `detach()` the ctx /// To prevent this behavior `detach()` the ctx from this handler object; the
/// from this handler object; the detached context will free its own resources /// detached context will free its own resources when finished executing.
/// when finished executing. This is bad practice except in certain cases. /// This is bad practice except in certain cases.
/// ///
/// To wait for the child context to finish use `join()`. Calling `interrupt()` /// To wait for the child context to finish use `join()`. Calling `interrupt()`
/// will cause an `interrupted` exception to be thrown down the child's stack /// will cause an `interrupted` exception to be thrown down the child's stack

View file

@ -41,7 +41,7 @@ class ircd::ctx::dock
void notify(ctx &) noexcept; void notify(ctx &) noexcept;
public: public:
auto size() const { return q.size(); } size_t size() const;
template<class time_point, class predicate> bool wait_until(time_point&& tp, predicate&& pred); template<class time_point, class predicate> bool wait_until(time_point&& tp, predicate&& pred);
template<class time_point> cv_status wait_until(time_point&& tp); template<class time_point> cv_status wait_until(time_point&& tp);
@ -72,6 +72,10 @@ noexcept
assert(q.empty()); assert(q.empty());
} }
/// Wake up the next context waiting on the dock
///
/// Unlike notify_one(), the next context in the queue is repositioned in the
/// back before being woken up for better fairness.
inline void inline void
ircd::ctx::dock::notify() ircd::ctx::dock::notify()
noexcept noexcept
@ -85,6 +89,7 @@ noexcept
notify(*c); notify(*c);
} }
/// Wake up the next context waiting on the dock
inline void inline void
ircd::ctx::dock::notify_one() ircd::ctx::dock::notify_one()
noexcept noexcept
@ -95,14 +100,15 @@ noexcept
notify(*q.front()); notify(*q.front());
} }
/// Wake up all contexts waiting on the dock.
///
/// We copy the queue and post all notifications without requesting direct context
/// switches. This ensures everyone gets notified in a single transaction without
/// any interleaving during this process.
inline void inline void
ircd::ctx::dock::notify_all() ircd::ctx::dock::notify_all()
noexcept noexcept
{ {
// We copy the queue and post all notifications without requesting direct context switches.
// This ensures everyone gets notified in a single transaction without any interleaving
// during this process.
const auto copy(q); const auto copy(q);
for(const auto &c : copy) for(const auto &c : copy)
ircd::ctx::notify(*c); ircd::ctx::notify(*c);
@ -148,7 +154,7 @@ template<class duration,
class predicate> class predicate>
bool bool
ircd::ctx::dock::wait_for(const duration &dur, ircd::ctx::dock::wait_for(const duration &dur,
predicate&& pred) predicate&& pred)
{ {
static const duration zero(0); static const duration zero(0);
@ -173,9 +179,12 @@ template<class time_point>
ircd::ctx::cv_status ircd::ctx::cv_status
ircd::ctx::dock::wait_until(time_point&& tp) ircd::ctx::dock::wait_until(time_point&& tp)
{ {
const scope remove(std::bind(&dock::remove_self, this)); const scope remove
q.emplace_back(&cur()); {
std::bind(&dock::remove_self, this)
};
q.emplace_back(&cur());
return ircd::ctx::wait_until<std::nothrow_t>(tp)? cv_status::timeout: return ircd::ctx::wait_until<std::nothrow_t>(tp)? cv_status::timeout:
cv_status::no_timeout; cv_status::no_timeout;
} }
@ -184,15 +193,22 @@ template<class time_point,
class predicate> class predicate>
bool bool
ircd::ctx::dock::wait_until(time_point&& tp, ircd::ctx::dock::wait_until(time_point&& tp,
predicate&& pred) predicate&& pred)
{ {
if(pred()) if(pred())
return true; return true;
const scope remove(std::bind(&dock::remove_self, this)); const scope remove
{
std::bind(&dock::remove_self, this)
};
q.emplace_back(&cur()); do q.emplace_back(&cur()); do
{ {
const bool expired(ircd::ctx::wait_until<std::nothrow_t>(tp)); const bool expired
{
ircd::ctx::wait_until<std::nothrow_t>(tp)
};
if(pred()) if(pred())
return true; return true;
@ -203,6 +219,14 @@ ircd::ctx::dock::wait_until(time_point&& tp,
while(1); while(1);
} }
/// The number of contexts waiting in the queue.
inline size_t
ircd::ctx::dock::size()
const
{
return q.size();
}
inline void inline void
ircd::ctx::dock::notify(ctx &ctx) ircd::ctx::dock::notify(ctx &ctx)
noexcept noexcept

View file

@ -23,10 +23,7 @@
#pragma once #pragma once
#define HAVE_IRCD_DB_H #define HAVE_IRCD_DB_H
// IRCd Database /// Database: an object store from the primitives of `cell`, `column`, and `row`.
//
// Please see db/README.md for documentation.
//
namespace ircd::db namespace ircd::db
{ {
struct init; struct init;
@ -62,10 +59,11 @@ namespace ircd::db
extern struct log::log log; extern struct log::log log;
} }
// /// Forward declarations for rocksdb because we do not include it here.
// These are forward declarations to objects we may carry a pointer to. ///
// Users of ircd::db should not have to deal directly with these types. /// These are forward declarations to objects we may carry a pointer to.
// /// Users of ircd::db should not have to deal directly with these types.
///
namespace rocksdb namespace rocksdb
{ {
struct DB; struct DB;

View file

@ -1,8 +1,6 @@
## IRCd Database ## IRCd Database
IRCd's database is presented here primarily as a persistent Object store. The database is an object store built from the primitives of `cell`, `column`, and `row`.
In other words, the structure presented by the database can be represented
with JSON. This is built from the primitives of `column`, `row` and `cell`.
#### Columns #### Columns
While a simple key-value store could naively store a JSON document as a textual While a simple key-value store could naively store a JSON document as a textual

View file

@ -31,25 +31,25 @@ namespace ircd
struct exception; struct exception;
} }
/** The root exception type. /// The root exception type.
* ///
* All exceptions in the project inherit from this type. /// All exceptions in the project inherit from this type.
* To catch any exception from a project developer's code: /// To catch any exception from a project developer's code:
* catch(const ircd::exception &) {} /// catch(const ircd::exception &) {}
* ///
* Remember: not all exceptions are from project developer's code, /// Remember: not all exceptions are from project developer's code,
* such as std::out_of_range. In most contexts if you have to deal with this /// such as std::out_of_range. In most contexts if you have to deal with this
* someone else was lazy, which is bad. To be sure and catch any exception: /// someone else was lazy, which is bad. To be sure and catch any exception:
* catch(const std::exception &) {} /// catch(const std::exception &) {}
* ///
* Remember: not all exceptions have to inherit from std::exception, but /// Remember: not all exceptions have to inherit from std::exception, but
* those are rogue exceptions. We do not allow this. To be sure nothing /// those are rogue exceptions. We do not allow this. To be sure nothing
* can possibly get through, add to the bottom: /// can possibly get through, add to the bottom:
* catch(...) {} /// catch(...) {}
* ///
* Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert' /// Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert'
* for exceptions, and the rogue can be found-out in testing. /// for exceptions, and the rogue can be found-out in testing.
*/ ///
struct ircd::exception struct ircd::exception
:std::exception :std::exception
{ {
@ -73,38 +73,37 @@ struct ircd::exception
} }
}; };
/** Exception generator convenience macro /// Exception generator convenience macro
* ///
* If you want to create your own exception type, you have found the right /// If you want to create your own exception type, you have found the right
* place! This macro allows creating an exception in the the hierarchy. /// place! This macro allows creating an exception in the the hierarchy.
* ///
* To create an exception, invoke this macro in your header. Examples: /// To create an exception, invoke this macro in your header. Examples:
* ///
* IRCD_EXCEPTION(ircd::exception, my_exception) /// IRCD_EXCEPTION(ircd::exception, my_exception)
* IRCD_EXCEPTION(my_exception, my_specific_exception) /// IRCD_EXCEPTION(my_exception, my_specific_exception)
* ///
* Then your catch sequence can look like the following: /// Then your catch sequence can look like the following:
* ///
* catch(const my_specific_exception &e) /// catch(const my_specific_exception &e)
* { /// {
* log("something specifically bad happened: %s", e.what()); /// log("something specifically bad happened: %s", e.what());
* } /// }
* catch(const my_exception &e) /// catch(const my_exception &e)
* { /// {
* log("something generically bad happened: %s", e.what()); /// log("something generically bad happened: %s", e.what());
* } /// }
* catch(const ircd::exception &e) /// catch(const ircd::exception &e)
* { /// {
* log("unrelated bad happened: %s", e.what()); /// log("unrelated bad happened: %s", e.what());
* } /// }
* catch(const std::exception &e) /// catch(const std::exception &e)
* { /// {
* log("unhandled bad happened: %s", e.what()); /// log("unhandled bad happened: %s", e.what());
* } /// }
* ///
* Remember: the order of the catch blocks is important. /// Remember: the order of the catch blocks is important.
*/ ///
#define IRCD_EXCEPTION(parent, name) \ #define IRCD_EXCEPTION(parent, name) \
struct name \ struct name \
:parent \ :parent \
@ -122,6 +121,7 @@ struct name \
} \ } \
}; \ }; \
/// Hides the name of the exception when generating a string
#define IRCD_EXCEPTION_HIDENAME(parent, name) \ #define IRCD_EXCEPTION_HIDENAME(parent, name) \
struct name \ struct name \
:parent \ :parent \
@ -139,12 +139,12 @@ struct name \
} \ } \
}; };
/** Root error exception type. Inherit from this. /// Root error exception type. Inherit from this.
* List your own exception somewhere else (unless you're overhauling libircd). /// List your own exception somewhere else (unless you're overhauling libircd).
* example, in your namespace: /// example, in your namespace:
* ///
* IRCD_EXCEPTION(ircd::error, error) /// IRCD_EXCEPTION(ircd::error, error)
*/ ///
namespace ircd namespace ircd
{ {
IRCD_EXCEPTION(exception, error) // throw ircd::error("something bad") IRCD_EXCEPTION(exception, error) // throw ircd::error("something bad")

View file

@ -22,6 +22,7 @@
#pragma once #pragma once
#define HAVE_IRCD_FMT_H #define HAVE_IRCD_FMT_H
/// Typesafe format strings from formal grammars & standard RTTI
namespace ircd::fmt namespace ircd::fmt
{ {
IRCD_EXCEPTION(ircd::error, error); IRCD_EXCEPTION(ircd::error, error);

View file

@ -36,6 +36,7 @@
* AUTOMODPATH = directory for autoloaded modules * AUTOMODPATH = directory for autoloaded modules
*/ */
/// Tools for working with the local filesystem.
namespace ircd::fs namespace ircd::fs
{ {
IRCD_EXCEPTION(ircd::error, error) IRCD_EXCEPTION(ircd::error, error)

View file

@ -25,6 +25,7 @@
#pragma once #pragma once
#define HAVE_IRCD_HTTP_H #define HAVE_IRCD_HTTP_H
/// HyperText TransPort: formal grammars & tools
namespace ircd::http namespace ircd::http
{ {
enum code :int; enum code :int;

View file

@ -23,6 +23,7 @@
#pragma once #pragma once
#define HAVE_IRCD_INFO_H #define HAVE_IRCD_INFO_H
/// Information & metadata about the library.
namespace ircd::info namespace ircd::info
{ {
struct line; struct line;

View file

@ -31,6 +31,9 @@
#include "stdinc.h" #include "stdinc.h"
#endif #endif
/// \brief Internet Relay Chat daemon. This is the principal namespace for IRCd.
///
///
namespace ircd namespace ircd
{ {
extern bool debugmode; ///< Toggled by command line to indicate debug behavior extern bool debugmode; ///< Toggled by command line to indicate debug behavior

View file

@ -29,33 +29,33 @@
#pragma once #pragma once
#define HAVE_IRCD_JS_H #define HAVE_IRCD_JS_H
namespace ircd { /// JavaScript Embedded Machine
namespace js { namespace ircd::js
// Root exception for this subsystem. The exception hierarchy integrating
// with JS itself inherits from this and is defined in TODO: _________
IRCD_EXCEPTION(ircd::error, error)
// Specific logging facility for this subsystem with snomask
extern struct log::log log;
// Fetch version information
enum class ver
{ {
IMPLEMENTATION, // Root exception for this subsystem. The exception hierarchy integrating
}; // with JS itself inherits from this and is defined in TODO: _________
const char *version(const ver &ver); IRCD_EXCEPTION(ircd::error, error)
// Specific logging facility for this subsystem with snomask
extern struct log::log log;
// Fetch version information
enum class ver
{
IMPLEMENTATION,
};
const char *version(const ver &ver);
struct init;
}
// Initialize the subsystem (singleton held by IRCd main context only) // Initialize the subsystem (singleton held by IRCd main context only)
struct init struct ircd::js::init
{ {
init(); init();
~init() noexcept; ~init() noexcept;
}; };
} // namespace js
} // namespace ircd
#ifndef RB_ENABLE_JS #ifndef RB_ENABLE_JS
// //
// Stub definitions for when JS isn't enabled because the definition // Stub definitions for when JS isn't enabled because the definition

View file

@ -22,6 +22,7 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_H #define HAVE_IRCD_JSON_H
/// JavaScript Object Notation: formal grammars & tools
/// ///
/// The IRCd JSON subsystem is meant to be a fast, safe, and extremely /// The IRCd JSON subsystem is meant to be a fast, safe, and extremely
/// lightweight interface. We have taken a somewhat non-traditional approach /// lightweight interface. We have taken a somewhat non-traditional approach

View file

@ -22,15 +22,17 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_ARRAY_H #define HAVE_IRCD_JSON_ARRAY_H
// ircd::json::array is the rank1 analog to ircd::json::object. It accepts /// Lightweight interface to a JSON array string.
// queries with numerical indexing. The same parsing approach is used in ///
// ircd::json::object and that is important to note here: iterating this array /// This is the rank1 analog to ircd::json::object. It accepts queries with
// by incrementing your own numerical index and making calls into this object /// numerical indexing. The same parsing approach is used in ircd::json::object
// is NOT efficient. Simply put, do not do something like /// and that is important to note here: iterating this array by incrementing
// `for(int x=0; x<array.count(); x++) array.at(x)` as that will parse the /// your own numerical index and making calls into this object is NOT efficient.
// array from the beginning on every single iteration. Instead, use the /// Simply put, do not do something like
// provided iterator object. /// `for(int x=0; x<array.count(); x++) array.at(x)` as that will parse the
// /// array from the beginning on every single iteration. Instead, use the
/// provided iterator object.
///
struct ircd::json::array struct ircd::json::array
:string_view :string_view
{ {

View file

@ -22,15 +22,6 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_MEMBER_H #define HAVE_IRCD_JSON_MEMBER_H
// json::member is a pair of values. The key value (member.first) should always
// be a STRING type. We don't use string_view directly in member.first because
// json::value can take ownership of a string or use a literal depending on
// the circumstance and it's more consistent this way.
//
// json::member, like json::value, is a runtime construct though still very
// lightweight and useful for non-deterministic composition to and extraction
// from JSON strings.
//
namespace ircd::json namespace ircd::json
{ {
struct member; struct member;
@ -46,6 +37,13 @@ namespace ircd::json
string_view stringify(mutable_buffer &, const std::vector<json::object> &); string_view stringify(mutable_buffer &, const std::vector<json::object> &);
} }
/// A pair of json::value representing an object member.
///
/// The key value (member.first) should always be a STRING type. We don't use
/// string_view directly in member.first because json::value can take ownership
/// of a string or use a literal depending on the circumstance and it's more
/// consistent this way.
///
struct ircd::json::member struct ircd::json::member
:std::pair<value, value> :std::pair<value, value>
{ {

View file

@ -27,42 +27,43 @@ namespace ircd::json
struct object; struct object;
} }
// ircd::json::object is an extremely lightweight device for making /// Lightweight interface to a JSON object string.
// queries into a string of JSON. This is a read-only device. It is merely ///
// functionality built on top of a string_view which is just a pair of /// This makes queries into a string of JSON. This is a read-only device.
// const char* pointers to the borders of the JSON object. /// It is merely functionality built on top of a string_view which is just a
// /// pair of const char* pointers to the borders of the JSON object.
// This class computes over strings of JSON by parsing it on-the-fly ///
// via forward iteration. The const_iterator is fundamental. All other member /// This class computes over strings of JSON by parsing it on-the-fly
// functions are built from this forward iteration and have worst-case linear /// via forward iteration. The const_iterator is fundamental. All other member
// complexity *every time you invoke them*. This is not necessarily a bad /// functions are built from this forward iteration and have worst-case linear
// thing in the appropriate use case. Our parser is pretty efficient; this /// complexity *every time you invoke them*. This is not necessarily a bad
// device conducts zero copies, zero allocations and zero indexing; instead /// thing in the appropriate use case. Our parser is pretty efficient; this
// the parser provides string_views to members during the iteration. /// device conducts zero copies, zero allocations and zero indexing; instead
// /// the parser provides string_views to members during the iteration.
// The returned values are character ranges (string_view's) which themselves ///
// are type agnostic to their contents. The type of a value is determined at /// The returned values are character ranges (string_view's) which themselves
// the user's discretion by querying the content of the string_view using a /// are type agnostic to their contents. The type of a value is determined at
// util function like json::type() etc. In other words, a value carries type /// the user's discretion by querying the content of the string_view using a
// data from its own original content. This means the user is responsible for /// util function like json::type() etc. In other words, a value carries type
// removing prefix and suffix characters like '{' or '"' after determining the /// data from its own original content. This means the user is responsible for
// type if they want a truly pure value string. Our zero-copy string_view utils /// removing prefix and suffix characters like '{' or '"' after determining the
// make this to a simple ballet of pointers. /// type if they want a truly pure value string. Our zero-copy string_view utils
// /// make this to a simple ballet of pointers.
// Other devices for dealing with strings of JSON are available: if an index ///
// should be populated (ircd::json::index), or if a certain set of keys /// Other devices for dealing with strings of JSON are available: if an index
// should be found and extracted with a single pass (ircd::json::extract). /// should be populated (ircd::json::index), or if a certain set of keys
// /// should be found and extracted with a single pass (ircd::json::extract).
// Some serialization/write functions are actually provided here, these ///
// are to *rewrite* JSON into our desired output form. /// Some serialization/write functions are actually provided here, these
// /// are to *rewrite* JSON into our desired output form.
// Recursive traversal cannot be achieved via a single key string value; so ///
// any string_view argument for a key will not be recursive. In other words, /// Recursive traversal cannot be achieved via a single key string value; so
// due to the fact that a JS identifier can have almost any character we have /// any string_view argument for a key will not be recursive. In other words,
// to use a different *type* like a vector of strings; in our common case we /// due to the fact that a JS identifier can have almost any character we have
// use an initializer_list typedef'ed as `path` and those overloads will be /// to use a different *type* like a vector of strings; in our common case we
// recursive. /// use an initializer_list typedef'ed as `path` and those overloads will be
// /// recursive.
///
struct ircd::json::object struct ircd::json::object
:string_view :string_view
{ {

View file

@ -22,17 +22,14 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_PROPERTY_H #define HAVE_IRCD_JSON_PROPERTY_H
///////////////////////////////////////////////////////////////////////////////
//
// tuple property template. Specify a list of these in the tuple template to
// form the members of the object.
//
namespace ircd::json namespace ircd::json
{ {
template<const char *const &name, class value_type> struct property; template<const char *const &name, class value_type> struct property;
} }
/// The property template specifies a key/value member of a json::tuple
///
///
template<const char *const &name, template<const char *const &name,
class T> class T>
struct ircd::json::property struct ircd::json::property

View file

@ -22,40 +22,37 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_TUPLE_H #define HAVE_IRCD_JSON_TUPLE_H
//
// Here we represent a JSON object with a named tuple, allowing the programmer
// to create a structure specifying all of the potentially valid members of the
// object. Access to a specific member is O(1) just like a native `struct`
// rather than a linear or logn lookup into a map. The size of the tuple is
// extremely minimal: only the size of the values it stores. This is because
// the member keys and type data are all static or dealt with at compile time.
//
// The member structure for the tuple is called `property` because json::member
// is already used to pair together runtime oriented json::values. This system
// only decays into runtime members and values when compile-time logic cannot
// be achieved.
//
// Create and use a tuple to efficiently extract members from a json::object.
// The tuple will populate its own members during a single-pass iteration of
// the JSON input. If the JSON does not contain a member specified in the
// tuple, the value will be default initialized. If the JSON contains a member
// not specified in the tuple, it is ignored. If you need to know all of the
// members specified in the JSON dynamically, use a json::index or iterate
// manually.
//
namespace ircd { namespace ircd {
namespace json { namespace json {
/////////////////////////////////////////////////////////////////////////////// /// All tuple templates inherit from this non-template type for tagging.
//
// ircd::json::tuple template. Create your own struct inheriting this
// class template with the members.
//
struct tuple_base struct tuple_base
{ {
// EBO tag // EBO tag
}; };
/// A compile-time construct to describe a JSON object's members and types.
///
/// Here we represent a JSON object with a named tuple, allowing the programmer
/// to create a structure specifying all of the potentially valid members of the
/// object. Thus at runtime, the tuple only carries around its values like a
/// `struct`. Unlike a `struct`, the tuple is abstractly iterable and we have
/// implemented logic operating on all JSON tuples regardless of their makeup
/// without any effort from a developer creating a new tuple.
///
/// The member structure for the tuple is called `property` because json::member
/// is already used to pair together runtime oriented json::values. This system
/// only decays into runtime members and values when compile-time logic cannot
/// be achieved.
///
/// Create and use a tuple to efficiently extract members from a json::object.
/// The tuple will populate its own members during a single-pass iteration of
/// the JSON input. If the JSON does not contain a member specified in the
/// tuple, the value will be default initialized. If the JSON contains a member
/// not specified in the tuple, it is ignored. If you need to know all of the
/// members specified in the JSON dynamically, use a json::index or iterate
/// manually.
///
template<class... T> template<class... T>
struct tuple struct tuple
:std::tuple<T...> :std::tuple<T...>

View file

@ -22,20 +22,22 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_VALUE_H #define HAVE_IRCD_JSON_VALUE_H
// The ircd::json::value is used if we have to keep non-deterministic runtime /// A primitive of the ircd::json system representing a value at runtime.
// state of values apropos a JSON object. In other words, value is runtime- ///
// typed rather than the json::tuple which is compile-time typed. The cost of /// This holds state for values apropos a JSON object.
// using this value structure is in the switching based on the type enum it ///
// stores as well as a branch in the destructor to deallocate owned resources. /// Value's data can either be in the form of a JSON string or it can be
// This is still very lightweight. The structure itself is the same size as /// native machine state. The serial flag indicates the former.
// a string_view (two pointers). It is also not template-based, allowing us ///
// to keep logic in the definition files and out of the headers. Nevertheless, /// Value is capable of allocation and ownership of its internal data if
// this class should not be abused over an alternative compile-time solution. /// necessary with move semantics, but copying may be not implemented in
// /// exceptional cases.
// Value cannot be copied because it can own resources, and recursively. The ///
// resource ownership is necessary in cases like nested initializer_lists /// Value can can hold any of the JSON types in either of these states.
// and other such complex compositions. /// This is accomplished with runtime switching and branching but this is
// /// still lightweight. The structure is just the size of two pointers, the
/// same as a string_view.
///
struct ircd::json::value struct ircd::json::value
{ {
union // xxx std::variant union // xxx std::variant

View file

@ -22,15 +22,6 @@
#pragma once #pragma once
#define HAVE_IRCD_LIFE_GUARD_H #define HAVE_IRCD_LIFE_GUARD_H
//
// life_guard is a convenience which takes advantage of
// std::enable_shared_from_this<T>. The life_guard glorifies the constructor
// of an std::shared_ptr<T> by accepting std::weak_ptr<T> and T& itself all
// with proper semantics. Once construction is successful, the user holds it
// for the duration of the scope ensuring T& survives context interleaving
// without being destructed.
//
namespace ircd namespace ircd
{ {
// Tests if type inherits from std::enable_shared_from_this<> // Tests if type inherits from std::enable_shared_from_this<>
@ -59,19 +50,25 @@ namespace ircd
template<class T> struct life_guard; template<class T> struct life_guard;
} }
/* Use the life_guard to keep an object alive within a function running in a context. /// life_guard is a convenience which takes advantage of std::enable_shared_from_this<T>.
* /// The life_guard glorifies the constructor of an std::shared_ptr<T> by accepting
* Example: /// std::weak_ptr<T> and T& itself all with proper semantics. Once construction is
* /// successful, the user holds it for the duration of the scope ensuring T& survives
void foo(client &c) /// context interleaving without being destructed.
{ ///
const life_guard<client> lg(c); /// Use the life_guard to keep an object alive within a function running in a context.
///
c.call(); // This call was always safe with or w/o life_guard. /// Example:
ctx::wait(); // The context has now yielded and another context might destroy client &c ///
c.call(); // The context continues and this would have made a call on a dead c. /// void foo(client &c)
} /// {
*/ /// const life_guard<client> lg(c);
///
/// c.call(); // This call was always safe with or w/o life_guard.
/// ctx::wait(); // The context has now yielded and another context might destroy client &c
/// c.call(); // The context continues and this would have made a call on a dead c.
/// }
///
template<class T> template<class T>
struct ircd::life_guard struct ircd::life_guard
:std::shared_ptr<T> :std::shared_ptr<T>

View file

@ -44,6 +44,7 @@ namespace ircd
const char *smalldate(const time_t &); const char *smalldate(const time_t &);
} }
/// Logging system
namespace ircd::log namespace ircd::log
{ {
enum facility :int; enum facility :int;

View file

@ -22,6 +22,7 @@
#pragma once #pragma once
#define HAVE_IRCD_MAPI_H #define HAVE_IRCD_MAPI_H
/// Module API: Interface for module developers.
namespace ircd::mapi namespace ircd::mapi
{ {
struct header; struct header;

View file

@ -22,6 +22,7 @@
#pragma once #pragma once
#define HAVE_IRCD_MODS_H #define HAVE_IRCD_MODS_H
/// Modules system
namespace ircd::mods namespace ircd::mods
{ {
IRCD_EXCEPTION(ircd::error, error) IRCD_EXCEPTION(ircd::error, error)

View file

@ -23,12 +23,7 @@
#pragma once #pragma once
#define HAVE_IRCD_UTIL_RANDOM_H #define HAVE_IRCD_UTIL_RANDOM_H
namespace ircd::rand /// Some character set dictionaries
{
extern std::random_device device;
extern std::mt19937_64 mt;
}
namespace ircd::rand::dict namespace ircd::rand::dict
{ {
extern const std::string alnum; extern const std::string alnum;
@ -38,8 +33,12 @@ namespace ircd::rand::dict
extern const std::string numeric; extern const std::string numeric;
} }
/// Tools for randomization
namespace ircd::rand namespace ircd::rand
{ {
extern std::random_device device;
extern std::mt19937_64 mt;
uint64_t integer(); uint64_t integer();
uint64_t integer(const uint64_t &min, const uint64_t &max); uint64_t integer(const uint64_t &min, const uint64_t &max);

View file

@ -142,8 +142,20 @@ namespace std
// Forward declarations from third party namespaces not included here // Forward declarations from third party namespaces not included here
// //
// Boost is not exposed unless explicitly included by a definition file. This /// Forward declarations for boost because it is not included here.
// is a major improvement of project compile time. ///
/// libircd does not include third party headers along with its own headers
/// for the public interface, only standard library headers. boost is only
/// included in specific definition files where we use its functionality.
/// This is a major improvement in project compile time.
namespace boost
{
}
/// Forward declarations for boost::asio because it is not included here.
///
/// Boost headers are not exposed to our users unless explicitly included by a
/// definition file.
namespace boost::asio namespace boost::asio
{ {
struct io_service; // Allow a reference to an ios to be passed to ircd struct io_service; // Allow a reference to an ios to be passed to ircd

View file

@ -36,13 +36,12 @@ namespace ircd
bool operator!(const string_view &); bool operator!(const string_view &);
} }
// /// Customized std::string_view (experimental TS / C++17)
// Customized std::string_view (experimental TS / C++17) ///
// /// This class adds iterator-based (char*, char*) construction to std::string_view which otherwise
// This class adds iterator-based (char*, char*) construction to std::string_view which otherwise /// takes traditional (char*, size_t) arguments. This allows boost::spirit grammars to create
// takes traditional (char*, size_t) arguments. This allows boost::spirit grammars to create /// string_view's using the raw[] directive achieving zero-copy/zero-allocation parsing.
// string_view's using the raw[] directive achieving zero-copy/zero-allocation parsing. ///
//
struct ircd::string_view struct ircd::string_view
:std::string_view :std::string_view
{ {

View file

@ -26,6 +26,11 @@
#pragma once #pragma once
#define HAVE_IRCD_UTIL_H #define HAVE_IRCD_UTIL_H
/// Tools for developers
namespace ircd::util
{
}
namespace ircd { namespace ircd {
inline namespace util { inline namespace util {

3
ircd/README.md Normal file
View file

@ -0,0 +1,3 @@
# IRCd Library Definitions
This directory contains definitions and linkage for `libircd`

View file

@ -429,24 +429,21 @@ ircd::async_recv_next(std::shared_ptr<client> client)
async_recv_next(std::move(client), milliseconds(-1)); async_recv_next(std::move(client), milliseconds(-1));
} }
///
/// This function is the basis for the client's request loop. We still use
/// an asynchronous pattern until there is activity on the socket (a request)
/// in which case the switch to synchronous mode is made by jumping into an
/// ircd::context drawn from the request pool. When the request is finished,
/// the client exits back into asynchronous mode until the next request is
/// received and rinse and repeat.
// //
// This function is the basis for the client's request loop. We still use /// This sequence exists to avoid any possible c10k-style limitation imposed by
// an asynchronous pattern until there is activity on the socket (a request) /// dedicating a context and its stack space to the lifetime of a connection.
// in which case we switch to synchronous mode by jumping into an ircd::context /// This is similar to the thread-per-request pattern before async was in vogue.
// drawn from the request pool. When the request is finished, we exit back
// into asynchronous mode until the next request is received and rinse and repeat.
//
// This sequence exists to avoid any possible c10k-style limitation imposed by
// dedicating a context and its stack space to the lifetime of a connection.
// This is similar to the thread-per-request pattern before async was in vogue.
// Except now with userspace threads, a context switch has a cost on the order
// of a function call, not nearly that of a system thread. So after enduring
// several years of non-blocking stackless callback asynchronous web-scale hell,
// we have now made it out alive on the other side. Enjoy.
//
// Pay close attention to the comments to know exactly where you are and what
// you can do at any given point in this sequence.
// //
/// Developers: Pay close attention to the comments to know exactly where you
/// are and what you can do at any given point in this sequence.
///
void void
ircd::async_recv_next(std::shared_ptr<client> client, ircd::async_recv_next(std::shared_ptr<client> client,
const milliseconds &timeout) const milliseconds &timeout)

View file

@ -933,6 +933,11 @@ const
return json::string(begin, end); return json::string(begin, end);
} }
///////////////////////////////////////////////////////////////////////////////
//
// json/value.h
//
ircd::json::value::~value() ircd::json::value::~value()
noexcept noexcept
{ {

View file

@ -193,29 +193,31 @@ ircd::socket::cancel()
sd.cancel(); sd.cancel();
} }
// /// Asynchronous callback when the socket is ready
// Overload for operator() without a timeout. see: operator() ///
// /// Overload for operator() without a timeout. see: operator()
///
void void
ircd::socket::operator()(handler h) ircd::socket::operator()(handler h)
{ {
operator()(milliseconds(-1), std::move(h)); operator()(milliseconds(-1), std::move(h));
} }
// /// Asynchronous callback when the socket is ready
// This function calls back the handler when the socket has received ///
// something and is ready to be read from. /// This function calls back the handler when the socket has received
// /// something and is ready to be read from.
// The purpose here is to allow waiting for data from the socket without ///
// blocking any context and using any stack space whatsoever, i.e full /// The purpose here is to allow waiting for data from the socket without
// asynchronous mode. /// blocking any context and using any stack space whatsoever, i.e full
// /// asynchronous mode.
// boost::asio has no direct way to accomplish this, so we use a little ///
// trick to read a single byte with MSG_PEEK as our indication. This is /// boost::asio has no direct way to accomplish this because the buffer size
// done directly on the socket and not through the SSL cipher, but we /// must be positive so we use a little trick to read a single byte with
// don't want this byte anyway. This isn't such a great trick, because /// MSG_PEEK as our indication. This is done directly on the socket and
// it may result in an extra syscall; so there's room for improvement here. /// not through the SSL cipher, but we don't want this byte anyway. This
// /// isn't such a great trick.
///
void void
ircd::socket::operator()(const milliseconds &timeout, ircd::socket::operator()(const milliseconds &timeout,
handler callback) handler callback)

View file

@ -57,7 +57,7 @@ struct body
}; };
// Generate !accounts:host which is the MXID of the room where the members // Generate !accounts:host which is the MXID of the room where the members
// are the actual account registrations themselves for this homeserver. // are the actual account registrations for this homeserver.
const m::id::room::buf accounts_room_id const m::id::room::buf accounts_room_id
{ {
"accounts", home_server "accounts", home_server
@ -74,6 +74,7 @@ post_login_password(client &client,
const resource::request &request, const resource::request &request,
const resource::request::body<body> &body) const resource::request::body<body> &body)
{ {
// Build a canonical MXID from a the user field
const m::id::user::buf user_id const m::id::user::buf user_id
{ {
unquote(at<name::user>(body)), home_server unquote(at<name::user>(body)), home_server