0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-29 08:13:46 +02:00

ircd:Ⓜ️ Move vm eval definitions into module.

This commit is contained in:
Jason Volk 2018-05-06 21:15:25 -07:00
parent a04a3e840e
commit 456f8e7ac1
6 changed files with 477 additions and 319 deletions

View file

@ -94,7 +94,6 @@ struct ircd::m::init
self::init _self;
keys::init _keys;
dbs::init _dbs;
vm::init _vm;
std::unique_ptr<modules> modules;
std::unique_ptr<listeners> listeners;

View file

@ -15,7 +15,6 @@
///
namespace ircd::m::vm
{
struct init;
struct error; // custom exception
struct opts;
struct copts;
@ -24,9 +23,8 @@ namespace ircd::m::vm
enum fault :uint;
using fault_t = std::underlying_type<fault>::type;
extern struct log::log log;
extern uint64_t current_sequence;
extern ctx::shared_view<accepted> accept;
extern uint64_t current_sequence;
extern const opts default_opts;
extern const copts default_copts;
@ -35,12 +33,6 @@ namespace ircd::m::vm
uint64_t retired_sequence();
}
struct ircd::m::vm::init
{
init();
~init() noexcept;
};
/// Event Evaluation Device
///
/// This object conducts the evaluation of an event or a tape of multiple
@ -238,7 +230,7 @@ struct ircd::m::vm::accepted
ctx::ctx *context;
const vm::opts *opts;
const event::conforms *report;
std::string strung;
shared_buffer<mutable_buffer> strung;
accepted(const m::event &event,
const vm::opts *const &opts,

View file

@ -112,7 +112,6 @@ libircd_la_SOURCES = \
m/request.cc \
m/v1.cc \
m/m.cc \
m/vm.cc \
ircd.cc \
###

View file

@ -108,11 +108,13 @@ try
return;
}
const string_view prefixes[]
static const string_view prefixes[]
{
"s_", "m_", "client_", "key_", "federation_", "media_"
};
m::modules.emplace("vm"s, "vm"s);
for(const auto &name : mods::available())
if(startswith_any(name, std::begin(prefixes), std::end(prefixes)))
m::modules.emplace(name, name);
@ -374,6 +376,293 @@ noexcept
{
}
///////////////////////////////////////////////////////////////////////////////
//
// m/vm.h
//
decltype(ircd::m::vm::accept)
ircd::m::vm::accept
{};
decltype(ircd::m::vm::current_sequence)
ircd::m::vm::current_sequence
{};
decltype(ircd::m::vm::default_opts)
ircd::m::vm::default_opts
{};
decltype(ircd::m::vm::default_copts)
ircd::m::vm::default_copts
{};
/// Instance list linkage for all of the evaluations.
template<>
decltype(ircd::util::instance_list<ircd::m::vm::eval>::list)
ircd::util::instance_list<ircd::m::vm::eval>::list
{};
decltype(ircd::m::vm::eval::id_ctr)
ircd::m::vm::eval::id_ctr
{};
//
// Eval
//
// Processes any event from any place from any time and does whatever is
// necessary to validate, reject, learn from new information, ignore old
// information and advance the state of IRCd as best as possible.
//
// eval::eval
//
ircd::m::vm::eval::eval(const room &room,
json::iov &event,
const json::iov &content)
:eval{}
{
operator()(room, event, content);
}
ircd::m::vm::eval::eval(json::iov &event,
const json::iov &content,
const vm::copts &opts)
:eval{opts}
{
operator()(event, content);
}
ircd::m::vm::eval::eval(const event &event,
const vm::opts &opts)
:eval{opts}
{
operator()(event);
}
ircd::m::vm::eval::eval(const vm::copts &opts)
:opts{&opts}
,copts{&opts}
{
}
ircd::m::vm::eval::eval(const vm::opts &opts)
:opts{&opts}
{
}
ircd::m::vm::eval::~eval()
noexcept
{
}
ircd::m::vm::eval::operator
const event::id::buf &()
const
{
return event_id;
}
///
/// Figure 1:
/// in . <-- injection
/// ===:::::::==//
/// | ||||||| // <-- these functions
/// | \\|// //|
/// | ||| // | | acceleration
/// | |||// | |
/// | |||/ | |
/// | ||| | V
/// | !!! |
/// | * | <----- nozzle
/// | ///|||\\\ |
/// |/|/|/|\|\|\| <---- propagation cone
/// _/|/|/|/|\|\|\|\_
/// out
///
/// Inject a new event in a room originating from this server.
///
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const room &room,
json::iov &event,
const json::iov &contents)
{
using prototype = fault (eval &, const m::room &, json::iov &, const json::iov &);
static import<prototype> function
{
"vm", "eval__commit_room"
};
// This eval entry point is only used for commits. We try to find the
// commit opts the user supplied directly to this eval or with the room.
if(!copts)
copts = room.opts;
if(!copts)
copts = &vm::default_copts;
// Note that the regular opts is unconditionally overridden because the
// user should have provided copts instead.
this->opts = copts;
// Set a member pointer to the json::iov currently being composed. This
// allows other parallel evals to have deep access to exactly what this
// eval is attempting to do.
issue = &event;
room_id = room.room_id;
const unwind deissue{[this]
{
room_id = {};
issue = nullptr;
}};
return function(*this, room, event, contents);
}
/// Inject a new event originating from this server.
///
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(json::iov &event,
const json::iov &contents)
{
using prototype = fault (eval &, json::iov &, const json::iov &);
static import<prototype> function
{
"vm", "eval__commit"
};
// This eval entry point is only used for commits. If the user did not
// supply commit opts we supply the default ones here.
if(!copts)
copts = &vm::default_copts;
// Note that the regular opts is unconditionally overridden because the
// user should have provided copts instead.
this->opts = copts;
// Set a member pointer to the json::iov currently being composed. This
// allows other parallel evals to have deep access to exactly what this
// eval is attempting to do.
assert(!room_id || issue == &event);
issue = &event;
const unwind deissue{[this]
{
// issue is untouched when room_id is set; that indicates it was set
// and will be unset by another eval function (i.e above).
if(!room_id)
issue = nullptr;
}};
return function(*this, event, contents);
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const event &event)
{
using prototype = fault (eval &, const m::event &);
static import<prototype> function
{
"vm", "eval__event"
};
// Set a member pointer to the event currently being evaluated. This
// allows other parallel evals to have deep access to exactly what this
// eval is working on. The pointer must be nulled on the way out.
this->event_= &event;
const unwind null_event{[this]
{
this->event_ = nullptr;
}};
return function(*this, event);
}
const uint64_t &
ircd::m::vm::sequence(const eval &eval)
{
return eval.sequence;
}
uint64_t
ircd::m::vm::retired_sequence()
{
event::id::buf event_id;
return retired_sequence(event_id);
}
uint64_t
ircd::m::vm::retired_sequence(event::id::buf &event_id)
{
static constexpr auto column_idx
{
json::indexof<event, "event_id"_>()
};
auto &column
{
dbs::event_column.at(column_idx)
};
const auto it
{
column.rbegin()
};
if(!it)
{
// If this iterator is invalid the events db should
// be completely fresh.
assert(db::sequence(*dbs::events) == 0);
return 0;
}
const auto &ret
{
byte_view<uint64_t>(it->first)
};
event_id = it->second;
return ret;
}
ircd::string_view
ircd::m::vm::reflect(const enum fault &code)
{
switch(code)
{
case fault::ACCEPT: return "ACCEPT";
case fault::EXISTS: return "EXISTS";
case fault::INVALID: return "INVALID";
case fault::DEBUGSTEP: return "DEBUGSTEP";
case fault::BREAKPOINT: return "BREAKPOINT";
case fault::GENERAL: return "GENERAL";
case fault::EVENT: return "EVENT";
case fault::STATE: return "STATE";
case fault::INTERRUPT: return "INTERRUPT";
}
return "??????";
}
//
// accepted
//
ircd::m::vm::accepted::accepted(const m::event &event,
const vm::opts *const &opts,
const event::conforms *const &report)
:m::event{event}
,context{ctx::current}
,opts{opts}
,report{report}
{
}
///////////////////////////////////////////////////////////////////////////////
//
// m/keys.h

View file

@ -36,6 +36,23 @@ AM_LDFLAGS += \
# -export-symbols-regex * \
###
###############################################################################
#
# /
#
moduledir = @moduledir@
root_la_SOURCES = root.cc
console_la_SOURCES = console.cc
vm_la_SOURCES = vm.cc
module_LTLIBRARIES = \
root.la \
console.la \
vm.la \
###
###############################################################################
#
# Server modules
@ -76,21 +93,6 @@ m_module_LTLIBRARIES = \
m_room_member.la \
###
###############################################################################
#
# /
#
moduledir = @moduledir@
root_la_SOURCES = root.cc
console_la_SOURCES = console.cc
module_LTLIBRARIES = \
root.la \
console.la \
###
###############################################################################
#
# /_matrix/client/

View file

@ -8,86 +8,45 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
namespace ircd::m::vm
{
extern log::log log;
extern hook::site commit_hook;
extern hook::site eval_hook;
extern hook::site notify_hook;
static void _tmp_effects(const m::event &event); //TODO: X
static void write(eval &);
static fault _eval_edu(eval &, const event &);
static fault _eval_pdu(eval &, const event &);
extern "C" fault eval__event(eval &, const event &);
extern "C" fault eval__commit(eval &, json::iov &, const json::iov &);
extern "C" fault eval__commit_room(eval &, const room &, json::iov &, const json::iov &);
static void init();
static void fini();
}
ircd::mapi::header
IRCD_MODULE
{
"Matrix Virtual Machine",
ircd::m::vm::init, ircd::m::vm::fini
};
decltype(ircd::m::vm::log)
ircd::m::vm::log
{
"vm", 'v'
};
decltype(ircd::m::vm::accept)
ircd::m::vm::accept
{};
decltype(ircd::m::vm::current_sequence)
ircd::m::vm::current_sequence
{};
decltype(ircd::m::vm::default_opts)
ircd::m::vm::default_opts
{};
decltype(ircd::m::vm::default_copts)
ircd::m::vm::default_copts
{};
//
// init
//
ircd::m::vm::init::init()
{
id::event::buf event_id;
current_sequence = retired_sequence(event_id);
log.info("BOOT %s @%lu [%s]",
string_view{m::my_node.node_id},
current_sequence,
current_sequence? string_view{event_id} : "NO EVENTS"_sv);
}
ircd::m::vm::init::~init()
{
id::event::buf event_id;
const auto current_sequence
{
retired_sequence(event_id)
};
log.info("HLT '%s' @%lu [%s]",
string_view{m::my_node.node_id},
current_sequence,
current_sequence? string_view{event_id} : "NO EVENTS"_sv);
}
namespace ircd::m::vm
{
extern hook::site commit_hook;
}
decltype(ircd::m::vm::commit_hook)
ircd::m::vm::commit_hook
{
{ "name", "vm.commit" }
};
//
// Eval
//
// Processes any event from any place from any time and does whatever is
// necessary to validate, reject, learn from new information, ignore old
// information and advance the state of IRCd as best as possible.
namespace ircd::m::vm
{
extern hook::site eval_hook;
extern hook::site notify_hook;
void _tmp_effects(const m::event &event); //TODO: X
void write(eval &);
fault _eval_edu(eval &, const event &);
fault _eval_pdu(eval &, const event &);
}
decltype(ircd::m::vm::eval_hook)
ircd::m::vm::eval_hook
{
@ -100,98 +59,64 @@ ircd::m::vm::notify_hook
{ "name", "vm.notify" }
};
/// Instance list linkage for all of the evaluations.
template<>
decltype(ircd::util::instance_list<ircd::m::vm::eval>::list)
ircd::util::instance_list<ircd::m::vm::eval>::list
{};
decltype(ircd::m::vm::eval::id_ctr)
ircd::m::vm::eval::id_ctr
{};
//
// eval::eval
// init
//
ircd::m::vm::eval::eval(const room &room,
json::iov &event,
const json::iov &content)
:eval{}
void
ircd::m::vm::init()
{
operator()(room, event, content);
}
id::event::buf event_id;
current_sequence = retired_sequence(event_id);
ircd::m::vm::eval::eval(json::iov &event,
const json::iov &content,
const vm::copts &opts)
:eval{opts}
{
operator()(event, content);
}
ircd::m::vm::eval::eval(const event &event,
const vm::opts &opts)
:eval{opts}
{
operator()(event);
}
ircd::m::vm::eval::eval(const vm::copts &opts)
:opts{&opts}
,copts{&opts}
{
}
ircd::m::vm::eval::eval(const vm::opts &opts)
:opts{&opts}
{
}
ircd::m::vm::eval::~eval()
noexcept
{
}
ircd::m::vm::eval::operator
const event::id::buf &()
const
{
return event_id;
}
/// Inject a new event in a room originating from this server.
///
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const room &room,
json::iov &event,
const json::iov &contents)
{
// This eval entry point is only used for commits. We try to find the
// commit opts the user supplied directly to this eval or with the room.
if(!copts)
copts = room.opts;
if(!copts)
copts = &vm::default_copts;
// Note that the regular opts is unconditionally overridden because the
// user should have provided copts instead.
this->opts = copts;
const auto &opts(*this->copts);
// Set a member pointer to the json::iov currently being composed. This
// allows other parallel evals to have deep access to exactly what this
// eval is attempting to do.
this->issue = &event;
this->room_id = room.room_id;
const unwind ueiov{[this]
log::info
{
this->room_id = {};
this->issue = nullptr;
}};
log, "BOOT %s @%lu [%s]",
string_view{m::my_node.node_id},
current_sequence,
current_sequence? string_view{event_id} : "NO EVENTS"_sv
};
}
void
ircd::m::vm::fini()
{
id::event::buf event_id;
const auto current_sequence
{
retired_sequence(event_id)
};
log::info
{
log, "HLT '%s' @%lu [%s]",
string_view{m::my_node.node_id},
current_sequence,
current_sequence? string_view{event_id} : "NO EVENTS"_sv
};
}
//
// eval
//
enum ircd::m::vm::fault
ircd::m::vm::eval__commit_room(eval &eval,
const room &room,
json::iov &event,
const json::iov &contents)
{
assert(eval.issue);
assert(eval.room_id);
assert(eval.copts);
assert(eval.opts);
assert(room.room_id);
const auto &opts
{
*eval.copts
};
const json::iov::push room_id
{
event, { "room_id", room.room_id }
@ -367,55 +292,23 @@ ircd::m::vm::eval::operator()(const room &room,
{ event, { "prev_state", prev_state } },
};
return operator()(event, contents);
return eval(event, contents);
}
/// Inject a new event originating from this server.
///
/// Figure 1:
/// in . <-- injection
/// ===:::::::==//
/// | ||||||| // <-- this function
/// | \\|// //|
/// | ||| // | | acceleration
/// | |||// | |
/// | |||/ | |
/// | ||| | V
/// | !!! |
/// | * | <----- nozzle
/// | ///|||\\\ |
/// |/|/|/|\|\|\| <---- propagation cone
/// _/|/|/|/|\|\|\|\_
/// out
///
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(json::iov &event,
const json::iov &contents)
ircd::m::vm::eval__commit(eval &eval,
json::iov &event,
const json::iov &contents)
{
// This eval entry point is only used for commits. If the user did not
// supply commit opts we supply the default ones here.
if(!copts)
copts = &vm::default_copts;
assert(eval.issue);
assert(eval.copts);
assert(eval.opts);
assert(eval.copts);
// Note that the regular opts is unconditionally overridden because the
// user should have provided copts instead.
assert(copts);
this->opts = copts;
const auto &opts(*copts);
// Set a member pointer to the json::iov currently being composed. This
// allows other parallel evals to have deep access to exactly what this
// eval is up to. This function may have been called from another function
// which already set the pointer, so we are careful to not null it for them
// on the way out.
const bool issue{this->issue};
assert(!issue || this->issue == &event);
this->issue = &event;
const unwind null_issue{[this, &issue]
const auto &opts
{
if(!issue)
this->issue = nullptr;
}};
*eval.copts
};
const json::iov::add_if _origin
{
@ -458,7 +351,7 @@ ircd::m::vm::eval::operator()(json::iov &event,
const string_view event_id
{
opts.event_id?
make_id(event, this->event_id, event_id_hash):
make_id(event, eval.event_id, event_id_hash):
string_view{}
};
@ -502,23 +395,25 @@ ircd::m::vm::eval::operator()(json::iov &event,
event, { "content", content },
};
return operator()(event);
return eval(event);
}
enum ircd::m::vm::fault
ircd::m::vm::eval::operator()(const event &event)
ircd::m::vm::eval__event(eval &eval,
const event &event)
try
{
// Set a member pointer to the event currently being evaluated. This
// allows other parallel evals to have deep access to exactly what this
// eval is working on. The pointer must be nulled on the way out.
this->event_= &event;
const unwind null_event{[this]
{
this->event_ = nullptr;
}};
assert(eval.opts);
assert(eval.event_);
assert(eval.id);
assert(eval.ctx);
if(copts)
const auto &opts
{
*eval.opts
};
if(eval.copts)
{
if(unlikely(!my_host(at<"origin"_>(event))))
throw error
@ -526,7 +421,7 @@ try
fault::GENERAL, "Committing event for origin: %s", at<"origin"_>(event)
};
if(copts->debuglog_precommit)
if(eval.copts->debuglog_precommit)
log.debug("injecting event(mark +%ld) %s",
vm::current_sequence,
pretty_oneline(event));
@ -535,15 +430,14 @@ try
commit_hook(event);
}
assert(opts);
const event::conforms &report
{
opts->conforming && !opts->conformed?
event::conforms{event, opts->non_conform.report}:
opts->report
opts.conforming && !opts.conformed?
event::conforms{event, opts.non_conform.report}:
opts.report
};
if(opts->conforming && !report.clean())
if(opts.conforming && !report.clean())
throw error
{
fault::INVALID, "Non-conforming event: %s", string(report)
@ -553,8 +447,8 @@ try
const fault ret
{
json::get<"event_id"_>(event)?
_eval_pdu(*this, event):
_eval_edu(*this, event)
_eval_pdu(eval, event):
_eval_edu(eval, event)
};
if(ret != fault::ACCEPT)
@ -562,71 +456,90 @@ try
vm::accepted accepted
{
event, opts, &report
event, &opts, &report
};
if(opts->effects)
if(opts.effects)
notify_hook(event);
if(opts->notify)
if(opts.notify)
vm::accept(accepted);
if(opts->effects)
if(opts.effects)
_tmp_effects(event);
if(opts->debuglog_accept)
if(opts.debuglog_accept)
log.debug("%s", pretty_oneline(event));
if(opts->infolog_accept)
if(opts.infolog_accept)
log.info("%s", pretty_oneline(event));
return ret;
}
catch(const error &e)
{
if(opts->errorlog & e.code)
log.error("eval %s: %s %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what(),
e.content);
if(eval.opts->errorlog & e.code)
log::error
{
log, "eval %s: %s %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what(),
e.content
};
if(opts->warnlog & e.code)
log.warning("eval %s: %s %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what(),
e.content);
if(eval.opts->warnlog & e.code)
log::warning
{
log, "eval %s: %s %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what(),
e.content
};
if(opts->nothrows & e.code)
if(eval.opts->nothrows & e.code)
return e.code;
throw;
}
catch(const ctx::interrupted &e)
{
if(opts->errorlog & fault::INTERRUPT)
log.error("eval %s: #NMI: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what());
if(eval.opts->errorlog & fault::INTERRUPT)
log::error
{
log, "eval %s: #NMI: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what()
};
if(eval.opts->warnlog & fault::INTERRUPT)
log::warning
{
log, "eval %s: #NMI: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what()
};
if(opts->warnlog & fault::INTERRUPT)
log.warning("eval %s: #NMI: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what());
throw;
}
catch(const std::exception &e)
{
if(opts->errorlog & fault::GENERAL)
log.error("eval %s: #GP: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what());
if(eval.opts->errorlog & fault::GENERAL)
log::error
{
log, "eval %s: #GP: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what()
};
if(opts->warnlog & fault::GENERAL)
log.warning("eval %s: #GP: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what());
if(eval.opts->warnlog & fault::GENERAL)
log::warning
{
log, "eval %s: #GP: %s",
json::get<"event_id"_>(event)?: json::string{"<edu>"},
e.what()
};
if(opts->nothrows & fault::GENERAL)
if(eval.opts->nothrows & fault::GENERAL)
return fault::GENERAL;
throw error
@ -783,19 +696,16 @@ ircd::m::vm::write(eval &eval)
{
auto &txn(*eval.txn);
if(eval.opts->debuglog_accept)
log.debug("Committing %zu cells in %zu bytes to events database...",
txn.size(),
txn.bytes());
log::debug
{
log, "Committing %zu cells in %zu bytes to events database...",
txn.size(),
txn.bytes()
};
txn();
}
const uint64_t &
ircd::m::vm::sequence(const eval &eval)
{
return eval.sequence;
}
uint64_t
ircd::m::vm::retired_sequence()
{
@ -838,25 +748,6 @@ ircd::m::vm::retired_sequence(event::id::buf &event_id)
return ret;
}
ircd::string_view
ircd::m::vm::reflect(const enum fault &code)
{
switch(code)
{
case fault::ACCEPT: return "ACCEPT";
case fault::EXISTS: return "EXISTS";
case fault::INVALID: return "INVALID";
case fault::DEBUGSTEP: return "DEBUGSTEP";
case fault::BREAKPOINT: return "BREAKPOINT";
case fault::GENERAL: return "GENERAL";
case fault::EVENT: return "EVENT";
case fault::STATE: return "STATE";
case fault::INTERRUPT: return "INTERRUPT";
}
return "??????";
}
//TODO: X
void
ircd::m::vm::_tmp_effects(const m::event &event)
@ -880,17 +771,3 @@ ircd::m::vm::_tmp_effects(const m::event &event)
send(my_room, at<"sender"_>(event), "ircd.room", at<"room_id"_>(event), json::object{});
}
}
//
// accepted
//
ircd::m::vm::accepted::accepted(const m::event &event,
const vm::opts *const &opts,
const event::conforms *const &report)
:m::event{event}
,context{ctx::current}
,opts{opts}
,report{report}
{
}