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:
parent
2d494d8924
commit
a4d186b6a1
4 changed files with 331 additions and 128 deletions
|
@ -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
|
||||
|
||||
*/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -44,6 +44,7 @@ libircd_la_SOURCES = \
|
|||
ctx.cc \
|
||||
client.cc \
|
||||
vm.cc \
|
||||
hook.cc \
|
||||
fmt.cc
|
||||
|
||||
#authproc.cc \
|
||||
|
|
192
ircd/hook.cc
192
ircd/hook.cc
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue