0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-01-13 08:23:56 +01:00

ircd: Refactor hook system.

This commit is contained in:
Jason Volk 2016-09-22 16:59:22 -07:00
parent 2d494d8924
commit a4d186b6a1
4 changed files with 331 additions and 128 deletions

View file

@ -1,11 +1,268 @@
/*
* Copyright (C) 2004-2005 Lee Hardy <lee -at- leeh.co.uk>
* Copyright (C) 2004-2005 ircd-ratbox development team
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#define HAVE_IRCD_HOOK_H
namespace ircd {
namespace hook {
using ctx::future;
// Function returns an empty future for serialization; valid for asynchronous
template<class state> using closure = std::function<future<> (state)>;
// Gives name of event required to happen.first (before), and required to happen.second (after)
using relationship = std::pair<std::string, std::string>;
// Returns true if A happens before B (for sorting)
bool happens_before(const std::string &a_name, const relationship &a_happens,
const std::string &b_name, const relationship &b_happens);
template<class state = void>
struct phase
{
std::string name;
relationship happens;
closure<state> function;
phase(const std::string &name, const relationship &, closure<state>&&);
phase(const std::string &name, closure<state>&&);
phase() = default;
};
template<class state>
phase<state>::phase(const std::string &name,
closure<state>&& function)
:phase{name, {}, std::forward<closure<state>>(function)}
{
}
template<class state>
phase<state>::phase(const std::string &name,
const relationship &happens,
closure<state>&& function)
:name{name}
,happens{happens}
,function{std::forward<closure<state>>(function)}
{
}
template<class state>
struct execution
{
std::map<std::string, future<>> pending; // phase.name => future
std::multimap<std::string, std::string> fences; // happens.second = > phase name
void fences_wait(const std::string &name);
void pending_wait(const std::string &name);
void pending_wait();
};
template<class state>
void
execution<state>::pending_wait()
{
for(const auto &pit : pending)
{
const auto &future(pit.second);
future.wait();
}
}
template<class state>
void
execution<state>::pending_wait(const std::string &name)
{
const auto pit(pending.find(name));
if(pit == end(pending))
return;
const scope erase([this, &pit]
{
pending.erase(pit);
});
const auto &future(pit->second);
future.wait();
}
template<class state>
void
execution<state>::fences_wait(const std::string &name)
{
const auto ppit(fences.equal_range(name));
const scope erase([this, &ppit]
{
fences.erase(ppit.first, ppit.second);
});
for(auto pit(ppit.first); pit != ppit.second; ++pit)
{
const auto &name(pit->second);
pending_wait(name);
}
}
template<class state = void>
struct sequence
{
std::list<phase<state>> space;
void sort();
public:
template<class... phase> void add(phase&&...);
void del(const std::string &name);
void operator()(state);
};
template<class state>
void
sequence<state>::operator()(state s)
{
execution<state> e;
for(const auto &phase : space)
{
e.fences_wait(phase.name);
e.pending_wait(phase.happens.first);
auto future(phase.function(s));
if(!future.valid())
continue;
if(!phase.happens.second.empty())
e.fences.emplace(phase.happens.second, phase.name);
e.pending.emplace(phase.name, std::move(future));
}
e.pending_wait();
}
template<class state>
void
sequence<state>::del(const std::string &name)
{
space.remove_if([&name]
(const auto &phase)
{
return phase.name == name;
});
}
template<class state>
template<class... phase>
void
sequence<state>::add(phase&&... p)
{
space.emplace_back(std::forward<phase>(p)...);
sort();
}
template<class state>
void
sequence<state>::sort()
{
space.sort([]
(const auto &a, const auto &b)
{
return happens_before(a.name, a.happens, b.name, b.happens);
});
}
template<class state = void>
class lambda
{
sequence<state> *s;
std::string name;
public:
lambda(sequence<state> &, const std::string &name, const relationship &, closure<state>);
lambda(sequence<state> &, const std::string &name, closure<state>);
~lambda() noexcept;
};
template<class state>
lambda<state>::lambda(sequence<state> &s,
const std::string &name,
const relationship &relationship,
closure<state> closure)
:s{&s}
,name{name}
{
s.add(name, relationship, std::move(closure));
}
template<class state>
lambda<state>::lambda(sequence<state> &s,
const std::string &name,
closure<state> closure)
:lambda{s, name, {}, std::move(closure)}
{
}
template<class state>
lambda<state>::~lambda()
noexcept
{
s->del(name);
}
template<class state = void>
class function
:lambda<state>
{
virtual future<> operator()(state) const = 0;
public:
function(sequence<state> &, const std::string &name, const relationship & = {});
virtual ~function() noexcept = default;
};
template<class state>
function<state>::function(sequence<state> &s,
const std::string &name,
const relationship &relationship)
:lambda<state>{s, name, relationship, [this]
(state&& st)
{
return this->operator()(std::forward<state>(st));
}}
{
}
} // namespace hook
} // namespace ircd
/*
#ifdef __cplusplus
namespace ircd {
@ -109,9 +366,9 @@ typedef struct
typedef struct
{
client::client *local_link; /* local client originating this, or NULL */
client::client *target; /* dying client */
client::client *from; /* causing client (could be &me or target) */
client::client *local_link; // local client originating this, or NULL
client::client *target; // dying client
client::client *from; // causing client (could be &me or target)
const char *comment;
} hook_data_client_exit;
@ -155,3 +412,5 @@ typedef struct
} // namespace ircd
#endif // __cplusplus
*/

View file

@ -96,6 +96,7 @@ namespace ircd
#include "ctx_future.h"
#include "ctx_async.h"
#include "ctx_pool.h"
#include "hook.h"
#include "line.h"
#include "tape.h"

View file

@ -44,6 +44,7 @@ libircd_la_SOURCES = \
ctx.cc \
client.cc \
vm.cc \
hook.cc \
fmt.cc
#authproc.cc \

View file

@ -2,13 +2,17 @@
* ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd).
* hook.c - code for dealing with the hook system
*
* ~~~~~~~~
* This code is basically a slow leaking array. Events are simply just a
* position in this array. When hooks are added, events will be created if
* they dont exist - this means modules with hooks can be loaded in any
* order, and events are preserved through module reloads.
* ~~~~~~~~~ left the comment but the code is gone. --jzk
*
* Copyright (C) 2004-2005 Lee Hardy <lee -at- leeh.co.uk>
* Copyright (C) 2004-2005 ircd-ratbox development team
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -36,6 +40,68 @@
*/
namespace ircd {
namespace hook {
} // namespace hook
} // namespace ircd
using namespace ircd;
bool
hook::happens_before(const std::string &a_name,
const relationship &a_happens,
const std::string &b_name,
const relationship &b_happens)
{
// Explicit request by b that a happens before
if(b_happens.first == a_name)
return true;
// Explicit request by a that b happens before
if(a_happens.first == b_name)
return false;
// Explicit request by b that a happens after
if(b_happens.second == a_name)
return false;
// Explicit request by a that b happens after
if(a_happens.second == b_name)
return true;
// a happens before everything and b has at least something before it
if(a_happens.first.empty() && !b_happens.first.empty())
return true;
// b happens before everything and a has at least something before it
if(b_happens.first.empty() && !a_happens.first.empty())
return false;
// a happens after everything and b has at least something after it
if(a_happens.second.empty() && !b_happens.second.empty())
return false;
// b happens after everything and a has at least something after it
if(b_happens.second.empty() && !a_happens.second.empty())
return true;
//TODO: ???
// both have the same before requirement
if(a_happens.first == b_happens.first)
return a_happens.second < b_happens.second;
//TODO: ???
// both have the same after requirement
if(a_happens.second == b_happens.second)
return a_happens.first < b_happens.first;
//TODO: ???
return false;
}
/*
hook *hooks;
@ -87,128 +153,4 @@ init_hook(void)
h_rehash = register_hook("rehash");
}
/* grow_hooktable()
* Enlarges the hook table by HOOK_INCREMENT
*/
static void
grow_hooktable(void)
{
hook *newhooks;
newhooks = (hook *)rb_malloc(sizeof(hook) * (max_hooks + HOOK_INCREMENT));
memcpy(newhooks, hooks, sizeof(hook) * num_hooks);
rb_free(hooks);
hooks = newhooks;
max_hooks += HOOK_INCREMENT;
}
/* find_freehookslot()
* Finds the next free slot in the hook table, given by an entry with
* h->name being NULL.
*/
static int
find_freehookslot(void)
{
int i;
if((num_hooks + 1) > max_hooks)
grow_hooktable();
for(i = 0; i < max_hooks; i++)
{
if(!hooks[i].name)
return i;
}
/* shouldnt ever get here */
return(max_hooks - 1);
}
/* find_hook()
* Finds an event in the hook table.
*/
static int
find_hook(const char *name)
{
int i;
for(i = 0; i < max_hooks; i++)
{
if(!hooks[i].name)
continue;
if(!irccmp(hooks[i].name, name))
return i;
}
return -1;
}
/* register_hook()
* Finds an events position in the hook table, creating it if it doesnt
* exist.
*/
int
register_hook(const char *name)
{
int i;
if((i = find_hook(name)) < 0)
{
i = find_freehookslot();
hooks[i].name = rb_strdup(name);
num_hooks++;
}
return i;
}
/* add_hook()
* Adds a hook to an event in the hook table, creating event first if
* needed.
*/
void
add_hook(const char *name, hookfn fn)
{
int i;
i = register_hook(name);
rb_dlinkAddAlloc(reinterpret_cast<void *>(fn), &hooks[i].hooks);
}
/* remove_hook()
* Removes a hook from an event in the hook table.
*/
void
remove_hook(const char *name, hookfn fn)
{
int i;
if((i = find_hook(name)) < 0)
return;
rb_dlinkFindDestroy(reinterpret_cast<void *>(fn), &hooks[i].hooks);
}
/* call_hook()
* Calls functions from a given event in the hook table.
*/
void
call_hook(int id, void *arg)
{
hookfn fn;
rb_dlink_node *ptr;
/* The ID we were passed is the position in the hook table of this
* hook
*/
RB_DLINK_FOREACH(ptr, hooks[id].hooks.head)
{
fn = (hookfn)ptr->data;
fn(arg);
}
}
} // namespace ircd
*/