0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-26 02:18:53 +02: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
### 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.
* Generate doxygen using `/usr/bin/doxygen tools/doxygen.conf` the target
directory is doc/html. Browse to doc/html/index.html

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
#define HAVE_IRCD_COLOR_H
/// Legacy mIRC color swatch
namespace ircd::color
{
enum mode

View file

@ -25,23 +25,23 @@
#pragma once
#define HAVE_IRCD_CTX_H
//
// 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
// and you do not have to include it manually.
//
// 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.
// To spawn and manipulate contexts, deal with this object.
//
// `struct ctx` (ircd/ctx.cc)
// Internal implementation of the context. This is not included here.
// 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.
//
/// 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
/// and you do not have to include it manually.
///
/// 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.
/// To spawn and manipulate contexts, deal with this object.
///
/// `struct ctx` (ircd/ctx.cc)
/// Internal implementation of the context. This is not included here.
/// 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
{
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
/// event queue instead. `DISPATCH` is an alternative, see boost::asio docs.
///
/// Unlike std::thread, when this object goes out of scope the context is
/// interrupted and joined if it has not been already; the current context
/// will wait for this to complete. If the child context does not cooperate
/// the destructor will hang. To prevent this behavior `detach()` the ctx
/// from this handler object; the detached context will free its own resources
/// when finished executing. This is bad practice except in certain cases.
/// When this object goes out of scope the context is interrupted and joined
/// if it has not been already; the current context will wait for this to
/// complete. If the child context does not cooperate the destructor will hang.
/// To prevent this behavior `detach()` the ctx from this handler object; the
/// detached context will free its own resources when finished executing.
/// This is bad practice except in certain cases.
///
/// 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

View file

@ -41,7 +41,7 @@ class ircd::ctx::dock
void notify(ctx &) noexcept;
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> cv_status wait_until(time_point&& tp);
@ -72,6 +72,10 @@ noexcept
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
ircd::ctx::dock::notify()
noexcept
@ -85,6 +89,7 @@ noexcept
notify(*c);
}
/// Wake up the next context waiting on the dock
inline void
ircd::ctx::dock::notify_one()
noexcept
@ -95,14 +100,15 @@ noexcept
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
ircd::ctx::dock::notify_all()
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);
for(const auto &c : copy)
ircd::ctx::notify(*c);
@ -148,7 +154,7 @@ template<class duration,
class predicate>
bool
ircd::ctx::dock::wait_for(const duration &dur,
predicate&& pred)
predicate&& pred)
{
static const duration zero(0);
@ -173,9 +179,12 @@ template<class time_point>
ircd::ctx::cv_status
ircd::ctx::dock::wait_until(time_point&& tp)
{
const scope remove(std::bind(&dock::remove_self, this));
q.emplace_back(&cur());
const scope remove
{
std::bind(&dock::remove_self, this)
};
q.emplace_back(&cur());
return ircd::ctx::wait_until<std::nothrow_t>(tp)? cv_status::timeout:
cv_status::no_timeout;
}
@ -184,15 +193,22 @@ template<class time_point,
class predicate>
bool
ircd::ctx::dock::wait_until(time_point&& tp,
predicate&& pred)
predicate&& pred)
{
if(pred())
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
{
const bool expired(ircd::ctx::wait_until<std::nothrow_t>(tp));
const bool expired
{
ircd::ctx::wait_until<std::nothrow_t>(tp)
};
if(pred())
return true;
@ -203,6 +219,14 @@ ircd::ctx::dock::wait_until(time_point&& tp,
while(1);
}
/// The number of contexts waiting in the queue.
inline size_t
ircd::ctx::dock::size()
const
{
return q.size();
}
inline void
ircd::ctx::dock::notify(ctx &ctx)
noexcept

View file

@ -23,10 +23,7 @@
#pragma once
#define HAVE_IRCD_DB_H
// IRCd Database
//
// Please see db/README.md for documentation.
//
/// Database: an object store from the primitives of `cell`, `column`, and `row`.
namespace ircd::db
{
struct init;
@ -62,10 +59,11 @@ namespace ircd::db
extern struct log::log log;
}
//
// 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.
//
/// 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.
///
namespace rocksdb
{
struct DB;

View file

@ -1,8 +1,6 @@
## IRCd Database
IRCd's database is presented here primarily as a persistent Object store.
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`.
The database is an object store built from the primitives of `cell`, `column`, and `row`.
#### Columns
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;
}
/** The root exception type.
*
* All exceptions in the project inherit from this type.
* To catch any exception from a project developer's code:
* catch(const ircd::exception &) {}
*
* 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
* someone else was lazy, which is bad. To be sure and catch any exception:
* catch(const std::exception &) {}
*
* Remember: not all exceptions have to inherit from std::exception, but
* those are rogue exceptions. We do not allow this. To be sure nothing
* can possibly get through, add to the bottom:
* catch(...) {}
*
* Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert'
* for exceptions, and the rogue can be found-out in testing.
*/
/// The root exception type.
///
/// All exceptions in the project inherit from this type.
/// To catch any exception from a project developer's code:
/// catch(const ircd::exception &) {}
///
/// 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
/// someone else was lazy, which is bad. To be sure and catch any exception:
/// catch(const std::exception &) {}
///
/// Remember: not all exceptions have to inherit from std::exception, but
/// those are rogue exceptions. We do not allow this. To be sure nothing
/// can possibly get through, add to the bottom:
/// catch(...) {}
///
/// Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert'
/// for exceptions, and the rogue can be found-out in testing.
///
struct ircd::exception
:std::exception
{
@ -73,38 +73,37 @@ struct ircd::exception
}
};
/** Exception generator convenience macro
*
* 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.
*
* To create an exception, invoke this macro in your header. Examples:
*
* IRCD_EXCEPTION(ircd::exception, my_exception)
* IRCD_EXCEPTION(my_exception, my_specific_exception)
*
* Then your catch sequence can look like the following:
*
* catch(const my_specific_exception &e)
* {
* log("something specifically bad happened: %s", e.what());
* }
* catch(const my_exception &e)
* {
* log("something generically bad happened: %s", e.what());
* }
* catch(const ircd::exception &e)
* {
* log("unrelated bad happened: %s", e.what());
* }
* catch(const std::exception &e)
* {
* log("unhandled bad happened: %s", e.what());
* }
*
* Remember: the order of the catch blocks is important.
*/
/// Exception generator convenience macro
///
/// 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.
///
/// To create an exception, invoke this macro in your header. Examples:
///
/// IRCD_EXCEPTION(ircd::exception, my_exception)
/// IRCD_EXCEPTION(my_exception, my_specific_exception)
///
/// Then your catch sequence can look like the following:
///
/// catch(const my_specific_exception &e)
/// {
/// log("something specifically bad happened: %s", e.what());
/// }
/// catch(const my_exception &e)
/// {
/// log("something generically bad happened: %s", e.what());
/// }
/// catch(const ircd::exception &e)
/// {
/// log("unrelated bad happened: %s", e.what());
/// }
/// catch(const std::exception &e)
/// {
/// log("unhandled bad happened: %s", e.what());
/// }
///
/// Remember: the order of the catch blocks is important.
///
#define IRCD_EXCEPTION(parent, name) \
struct name \
:parent \
@ -122,6 +121,7 @@ struct name \
} \
}; \
/// Hides the name of the exception when generating a string
#define IRCD_EXCEPTION_HIDENAME(parent, name) \
struct name \
:parent \
@ -139,12 +139,12 @@ struct name \
} \
};
/** Root error exception type. Inherit from this.
* List your own exception somewhere else (unless you're overhauling libircd).
* example, in your namespace:
*
* IRCD_EXCEPTION(ircd::error, error)
*/
/// Root error exception type. Inherit from this.
/// List your own exception somewhere else (unless you're overhauling libircd).
/// example, in your namespace:
///
/// IRCD_EXCEPTION(ircd::error, error)
///
namespace ircd
{
IRCD_EXCEPTION(exception, error) // throw ircd::error("something bad")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,33 +29,33 @@
#pragma once
#define HAVE_IRCD_JS_H
namespace ircd {
namespace 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
/// JavaScript Embedded Machine
namespace ircd::js
{
IMPLEMENTATION,
};
const char *version(const ver &ver);
// 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,
};
const char *version(const ver &ver);
struct init;
}
// Initialize the subsystem (singleton held by IRCd main context only)
struct init
struct ircd::js::init
{
init();
~init() noexcept;
};
} // namespace js
} // namespace ircd
#ifndef RB_ENABLE_JS
//
// Stub definitions for when JS isn't enabled because the definition

View file

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

View file

@ -22,15 +22,17 @@
#pragma once
#define HAVE_IRCD_JSON_ARRAY_H
// ircd::json::array is the rank1 analog to ircd::json::object. It accepts
// queries with numerical indexing. The same parsing approach is used in
// ircd::json::object and that is important to note here: iterating this array
// by incrementing your own numerical index and making calls into this object
// is NOT efficient. Simply put, do not do something like
// `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.
//
/// Lightweight interface to a JSON array string.
///
/// This is the rank1 analog to ircd::json::object. It accepts queries with
/// numerical indexing. The same parsing approach is used in ircd::json::object
/// and that is important to note here: iterating this array by incrementing
/// your own numerical index and making calls into this object is NOT efficient.
/// Simply put, do not do something like
/// `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
:string_view
{

View file

@ -22,15 +22,6 @@
#pragma once
#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
{
struct member;
@ -46,6 +37,13 @@ namespace ircd::json
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
:std::pair<value, value>
{

View file

@ -27,42 +27,43 @@ namespace ircd::json
struct object;
}
// ircd::json::object is an extremely lightweight device for making
// 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
// 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
// functions are built from this forward iteration and have worst-case linear
// complexity *every time you invoke them*. This is not necessarily a bad
// thing in the appropriate use case. Our parser is pretty efficient; this
// 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 user's discretion by querying the content of the string_view using a
// util function like json::type() etc. In other words, a value carries type
// data from its own original content. This means the user is responsible for
// removing prefix and suffix characters like '{' or '"' after determining the
// 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
// 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.
//
// 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,
// due to the fact that a JS identifier can have almost any character we have
// to use a different *type* like a vector of strings; in our common case we
// use an initializer_list typedef'ed as `path` and those overloads will be
// recursive.
//
/// Lightweight interface to a JSON object string.
///
/// This makes 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 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
/// functions are built from this forward iteration and have worst-case linear
/// complexity *every time you invoke them*. This is not necessarily a bad
/// thing in the appropriate use case. Our parser is pretty efficient; this
/// 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 user's discretion by querying the content of the string_view using a
/// util function like json::type() etc. In other words, a value carries type
/// data from its own original content. This means the user is responsible for
/// removing prefix and suffix characters like '{' or '"' after determining the
/// 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
/// 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.
///
/// 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,
/// due to the fact that a JS identifier can have almost any character we have
/// to use a different *type* like a vector of strings; in our common case we
/// use an initializer_list typedef'ed as `path` and those overloads will be
/// recursive.
///
struct ircd::json::object
:string_view
{

View file

@ -22,17 +22,14 @@
#pragma once
#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
{
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,
class T>
struct ircd::json::property

View file

@ -22,40 +22,37 @@
#pragma once
#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 json {
///////////////////////////////////////////////////////////////////////////////
//
// ircd::json::tuple template. Create your own struct inheriting this
// class template with the members.
//
/// All tuple templates inherit from this non-template type for tagging.
struct tuple_base
{
// 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>
struct tuple
:std::tuple<T...>

View file

@ -22,20 +22,22 @@
#pragma once
#define HAVE_IRCD_JSON_VALUE_H
// The ircd::json::value is used if we have to keep non-deterministic 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
// 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.
// This is still very lightweight. The structure itself is the same size as
// 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,
// this class should not be abused over an alternative compile-time solution.
//
// Value cannot be copied because it can own resources, and recursively. The
// resource ownership is necessary in cases like nested initializer_lists
// and other such complex compositions.
//
/// A primitive of the ircd::json system representing a value at runtime.
///
/// This holds state for values apropos a JSON object.
///
/// Value's data can either be in the form of a JSON string or it can be
/// native machine state. The serial flag indicates the former.
///
/// Value is capable of allocation and ownership of its internal data if
/// necessary with move semantics, but copying may be not implemented in
/// exceptional cases.
///
/// Value can can hold any of the JSON types in either of these states.
/// 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
{
union // xxx std::variant

View file

@ -22,15 +22,6 @@
#pragma once
#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
{
// Tests if type inherits from std::enable_shared_from_this<>
@ -59,19 +50,25 @@ namespace ircd
template<class T> struct life_guard;
}
/* Use the life_guard to keep an object alive within a function running in a context.
*
* Example:
*
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.
}
*/
/// 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.
///
/// Use the life_guard to keep an object alive within a function running in a context.
///
/// Example:
///
/// 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>
struct ircd::life_guard
:std::shared_ptr<T>

View file

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

View file

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

View file

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

View file

@ -23,12 +23,7 @@
#pragma once
#define HAVE_IRCD_UTIL_RANDOM_H
namespace ircd::rand
{
extern std::random_device device;
extern std::mt19937_64 mt;
}
/// Some character set dictionaries
namespace ircd::rand::dict
{
extern const std::string alnum;
@ -38,8 +33,12 @@ namespace ircd::rand::dict
extern const std::string numeric;
}
/// Tools for randomization
namespace ircd::rand
{
extern std::random_device device;
extern std::mt19937_64 mt;
uint64_t integer();
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
//
// Boost is not exposed unless explicitly included by a definition file. This
// is a major improvement of project compile time.
/// Forward declarations for boost because it is not included here.
///
/// 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
{
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 &);
}
//
// Customized std::string_view (experimental TS / C++17)
//
// 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
// string_view's using the raw[] directive achieving zero-copy/zero-allocation parsing.
//
/// Customized std::string_view (experimental TS / C++17)
///
/// 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
/// string_view's using the raw[] directive achieving zero-copy/zero-allocation parsing.
///
struct ircd::string_view
:std::string_view
{

View file

@ -26,6 +26,11 @@
#pragma once
#define HAVE_IRCD_UTIL_H
/// Tools for developers
namespace ircd::util
{
}
namespace ircd {
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));
}
///
/// 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
// an asynchronous pattern until there is activity on the socket (a request)
// in which case we switch to synchronous mode by jumping into an ircd::context
// 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.
/// 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.
//
/// 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
ircd::async_recv_next(std::shared_ptr<client> client,
const milliseconds &timeout)

View file

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

View file

@ -193,29 +193,31 @@ ircd::socket::cancel()
sd.cancel();
}
//
// Overload for operator() without a timeout. see: operator()
//
/// Asynchronous callback when the socket is ready
///
/// Overload for operator() without a timeout. see: operator()
///
void
ircd::socket::operator()(handler h)
{
operator()(milliseconds(-1), std::move(h));
}
//
// 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
// 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
// done directly on the socket and not through the SSL cipher, but we
// don't want this byte anyway. This isn't such a great trick, because
// it may result in an extra syscall; so there's room for improvement here.
//
/// 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.
///
/// 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
/// asynchronous mode.
///
/// boost::asio has no direct way to accomplish this because the buffer size
/// must be positive so we use a little trick to read a single byte with
/// MSG_PEEK as our indication. This is done directly on the socket and
/// not through the SSL cipher, but we don't want this byte anyway. This
/// isn't such a great trick.
///
void
ircd::socket::operator()(const milliseconds &timeout,
handler callback)

View file

@ -57,7 +57,7 @@ struct body
};
// 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
{
"accounts", home_server
@ -74,6 +74,7 @@ post_login_password(client &client,
const resource::request &request,
const resource::request::body<body> &body)
{
// Build a canonical MXID from a the user field
const m::id::user::buf user_id
{
unquote(at<name::user>(body)), home_server