summaryrefslogtreecommitdiff
path: root/src/serialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/serialize.cpp')
-rw-r--r--src/serialize.cpp355
1 files changed, 262 insertions, 93 deletions
diff --git a/src/serialize.cpp b/src/serialize.cpp
index 765fab0a9..71fc7e8be 100644
--- a/src/serialize.cpp
+++ b/src/serialize.cpp
@@ -1,178 +1,347 @@
/*
+ * Anope IRC Services
*
- * (C) 2003-2017 Anope Team
- * Contact us at team@anope.org
+ * Copyright (C) 2012-2017 Anope Team <team@anope.org>
*
- * Please read COPYING and README for further details.
+ * This file is part of Anope. Anope is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software
+ * Foundation, version 2.
*
- * Based on the original code of Epona by Lara.
- * Based on the original code of Services by Andy Church.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see see <http://www.gnu.org/licenses/>.
*/
#include "services.h"
#include "anope.h"
#include "serialize.h"
#include "modules.h"
-#include "account.h"
-#include "bots.h"
-#include "regchannel.h"
-#include "xline.h"
-#include "access.h"
+#include "event.h"
using namespace Serialize;
-std::vector<Anope::string> Type::TypeOrder;
-std::map<Anope::string, Type *> Serialize::Type::Types;
-std::list<Serializable *> *Serializable::SerializableItems;
+static std::map<Serialize::TypeBase *, std::unordered_map<ID, Object *>> objects;
-void Serialize::RegisterTypes()
-{
- static Type nc("NickCore", NickCore::Unserialize), na("NickAlias", NickAlias::Unserialize), bi("BotInfo", BotInfo::Unserialize),
- ci("ChannelInfo", ChannelInfo::Unserialize), access("ChanAccess", ChanAccess::Unserialize),
- akick("AutoKick", AutoKick::Unserialize), memo("Memo", Memo::Unserialize), xline("XLine", XLine::Unserialize);
-}
+std::vector<FieldBase *> Serialize::serializableFields;
+
+std::multimap<Anope::string, Anope::string> Serialize::child_types;
+
+static ID curid;
-void Serialize::CheckTypes()
+
+Object *Serialize::GetID(Serialize::TypeBase *type, ID id)
{
- for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it)
- {
- Serialize::Type *t = it->second;
- t->Check();
- }
+ auto it = objects[type].find(id);
+ if (it != objects[type].end())
+ return it->second;
+ return nullptr;
}
-Serializable::Serializable(const Anope::string &serialize_type) : last_commit(0), last_commit_time(0), id(0), redis_ignore(0)
+void Serialize::GC()
{
- if (SerializableItems == NULL)
- SerializableItems = new std::list<Serializable *>();
- SerializableItems->push_back(this);
+ for (auto it = objects.begin(); it != objects.end(); ++it)
+ for (auto it2 = it->second.begin(); it2 != it->second.end();)
+ {
+ Object *o = it2->second;
- this->s_type = Type::Find(serialize_type);
+ if (!o->CanGC())
+ {
+ // Wipe internal storage to force refetch
+ o->Wipe();
+ ++it2;
+ continue;
+ }
- this->s_iter = SerializableItems->end();
- --this->s_iter;
+ Anope::Logger.Debug2("garbage collected object {0}", o->id);
- FOREACH_MOD(OnSerializableConstruct, (this));
+ it2 = it->second.erase(it2);
+ delete o;
+ }
}
-Serializable::Serializable(const Serializable &other) : last_commit(0), last_commit_time(0), id(0), redis_ignore(0)
+void Serialize::Unregister(Module *m)
{
- SerializableItems->push_back(this);
- this->s_iter = SerializableItems->end();
- --this->s_iter;
+ for (TypeBase *s : ServiceManager::Get()->FindServices<Serialize::TypeBase *>())
+ if (s->GetOwner() == m)
+ s->Unregister();
- this->s_type = other.s_type;
+ for (FieldBase *field : serializableFields)
+ if (field->GetOwner() == m)
+ field->Unregister();
+}
- FOREACH_MOD(OnSerializableConstruct, (this));
+void Serialize::Object::Wipe()
+{
+ for (Serialize::FieldBase *base : s_type->GetFields())
+ {
+ base->Uncache(this);
+ }
}
-Serializable::~Serializable()
+std::vector<Edge> Object::GetEdges(TypeBase *type)
{
- FOREACH_MOD(OnSerializableDestruct, (this));
+ std::vector<Edge> refs;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGetRefs, this, type, refs);
+ if (result == EVENT_ALLOW)
+ return refs;
- SerializableItems->erase(this->s_iter);
+ if (type == nullptr)
+ {
+ refs.clear();
+ for (const std::pair<TypeBase *, std::vector<Edge>> &p : edges)
+ {
+ const std::vector<Edge> &e = p.second;
+ refs.insert(refs.end(), e.begin(), e.end());
+ }
+ return refs;
+ }
+
+ auto it = edges.find(type);
+ if (it != edges.end())
+ return it->second;
+ return std::vector<Edge>();
}
-Serializable &Serializable::operator=(const Serializable &)
+Object::Object(TypeBase *type)
{
- return *this;
+ ID i;
+ EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableGetId, type, i);
+ if (result != EVENT_ALLOW)
+ {
+ while (GetID(type, ++curid));
+ i = curid;
+ }
+
+ id = i;
+ objects[type][id] = this;
+
+ this->s_type = type;
+
+ type->objects.insert(this);
+
+ Anope::Logger.Debug2("Creating object id #{0} address {1} type {2}", id, static_cast<void *>(this), type->GetName());
+
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableCreate, this);
}
-void Serializable::QueueUpdate()
+Object::Object(TypeBase *type, ID i)
{
- /* Schedule updater */
- FOREACH_MOD(OnSerializableUpdate, (this));
+ this->id = i;
+ objects[type][id] = this;
+
+ this->s_type = type;
- /* Check for modifications now - this can delete this object! */
- FOREACH_MOD(OnSerializeCheck, (this->GetSerializableType()));
+ type->objects.insert(this);
+
+ Anope::Logger.Debug2("Creating object from id #{0} address {1} type {2}", id, static_cast<void *>(this), type->GetName());
}
-bool Serializable::IsCached(Serialize::Data &data)
+Object::~Object()
{
- return this->last_commit == data.Hash();
+ Anope::Logger.Debug2("Destructing object id #{0} address {1} type {2}", id, static_cast<void *>(this), s_type->GetName());
+
+ /* Remove in memory edges */
+ std::map<TypeBase *, std::vector<Edge>> copy = edges;
+ for (const std::pair<TypeBase *, std::vector<Edge>> &p : copy)
+ for (const Edge &edge : p.second)
+ {
+ if (!edge.direction)
+ {
+ Anope::Logger.Debug2("Removing edge from object id #{0} type {1} on field {2}", edge.other->id, edge.other->GetSerializableType()->GetName(), edge.field->serialize_name);
+ edge.other->RemoveEdge(this, edge.field);
+ }
+ else
+ {
+ Anope::Logger.Debug2("Removing edge to object id #{0} type {1} on field {2}", edge.other->id, edge.other->GetSerializableType()->GetName(), edge.field->serialize_name);
+ this->RemoveEdge(edge.other, edge.field);
+ }
+ }
+
+ objects[s_type].erase(id);
+ s_type->objects.erase(this);
}
-void Serializable::UpdateCache(Serialize::Data &data)
+void Object::Delete()
{
- this->last_commit = data.Hash();
+ Anope::Logger.Debug2("Deleting object id #{0} type {1}", id, s_type->GetName());
+
+ /* Delete dependant objects */
+ for (const Edge &edge : GetEdges(nullptr))
+ {
+ Object *other = edge.other;
+ FieldBase *field = edge.field;
+
+ if (edge.direction)
+ continue;
+
+ if (field->depends)
+ {
+ Anope::Logger.Debug2("Deleting dependent object #{0} type {1} due to edge on {2}", other->id, other->GetSerializableType()->GetName(), field->serialize_name);
+ other->Delete();
+ }
+ else
+ {
+ Anope::Logger.Debug2("Unsetting field {0} on object #{1} type {2}", field->serialize_name, other->id, other->GetSerializableType()->GetName());
+ field->UnsetS(other);
+ }
+ }
+
+ EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableDelete, this);
+
+ delete this;
}
-bool Serializable::IsTSCached()
+void Object::AddEdge(Object *other, FieldBase *field)
{
- return this->last_commit_time == Anope::CurTime;
+ // field = the field on 'this' object
+ this->edges[other->GetSerializableType()].emplace_back(other, field, true);
+ // field = the field on the other object
+ other->edges[this->GetSerializableType()].emplace_back(this, field, false);
}
-void Serializable::UpdateTS()
+void Object::RemoveEdge(Object *other, FieldBase *field)
{
- this->last_commit_time = Anope::CurTime;
+ std::vector<Edge> &myedges = this->edges[other->GetSerializableType()];
+ auto it = std::find(myedges.begin(), myedges.end(), Edge(other, field, true));
+ if (it != myedges.end())
+ myedges.erase(it);
+ else
+ Anope::Logger.Debug2("Unable to locate edge for removal on #{0} type {1} -> #{2} type {3}", this->id, s_type->GetName(), other->id, other->GetSerializableType()->GetName());
+
+ if (myedges.empty())
+ this->edges.erase(other->GetSerializableType());
+
+ std::vector<Edge> &theiredges = other->edges[this->GetSerializableType()];
+ it = std::find(theiredges.begin(), theiredges.end(), Edge(this, field, false));
+ if (it != theiredges.end())
+ theiredges.erase(it);
+ else
+ Anope::Logger.Debug2("Unable to locate edge for removal on #{0} type {1} <- #{2} type {3}", this->id, s_type->GetName(), other->id, other->GetSerializableType()->GetName());
+
+ if (theiredges.empty())
+ other->edges.erase(this->GetSerializableType());
}
-const std::list<Serializable *> &Serializable::GetItems()
+TypeBase::TypeBase(Module *o, const Anope::string &n) : Service(o, TypeBase::NAME, n), name(n), owner(o)
{
- return *SerializableItems;
}
-Type::Type(const Anope::string &n, unserialize_func f, Module *o) : name(n), unserialize(f), owner(o), timestamp(0)
+void TypeBase::Unregister()
{
- TypeOrder.push_back(this->name);
- Types[this->name] = this;
+ Anope::Logger.Debug2("Unregistering type {0}", this->GetName());
+
+ // Delete in memory objects
+ std::unordered_map<ID, Object *> objs = ::objects[this];
+ for (auto &pair : objs)
+ delete pair.second;
- FOREACH_MOD(OnSerializeTypeCreate, (this));
+ ::objects.erase(this);
+
+ for (FieldBase *field : serializableFields)
+ {
+ if (field->serialize_type == this->GetName())
+ {
+ field->Unregister();
+ }
+ }
}
-Type::~Type()
+Serialize::FieldBase *TypeBase::GetField(const Anope::string &fname)
{
- /* null the type of existing serializable objects of this type */
- if (Serializable::SerializableItems != NULL)
- for (std::list<Serializable *>::iterator it = Serializable::SerializableItems->begin(); it != Serializable::SerializableItems->end(); ++it)
- {
- Serializable *s = *it;
+ /* is this too slow? */
+ for (FieldBase *fb : ServiceManager::Get()->FindServices<FieldBase *>())
+ if (fb->serialize_type == this->GetName() && fb->serialize_name == fname)
+ return fb;
- if (s->s_type == this)
- s->s_type = NULL;
- }
+ Anope::Logger.Debug2("GetField() for unknown field {0} on {1}", fname, this->GetName());
- std::vector<Anope::string>::iterator it = std::find(TypeOrder.begin(), TypeOrder.end(), this->name);
- if (it != TypeOrder.end())
- TypeOrder.erase(it);
- Types.erase(this->name);
+ return nullptr;
}
-Serializable *Type::Unserialize(Serializable *obj, Serialize::Data &data)
+std::vector<Serialize::FieldBase *> TypeBase::GetFields()
{
- return this->unserialize(obj, data);
+ std::vector<Serialize::FieldBase *> fields;
+
+ for (FieldBase *fb : ServiceManager::Get()->FindServices<FieldBase *>())
+ if (fb->serialize_type == this->GetName())
+ fields.push_back(fb);
+
+ return fields;
}
-void Type::Check()
+TypeBase *TypeBase::Find(const Anope::string &name)
{
- FOREACH_MOD(OnSerializeCheck, (this));
+ return ServiceManager::Get()->FindService<TypeBase *>(name);
}
-time_t Type::GetTimestamp() const
+std::vector<TypeBase *> TypeBase::GetTypes()
{
- return this->timestamp;
+ return ServiceManager::Get()->FindServices<TypeBase *>();
}
-void Type::UpdateTimestamp()
+FieldBase::FieldBase(Module *c, const Anope::string &n, const Anope::string &t, bool d)
+ : Service(c, FieldBase::NAME)
+ , serialize_type(t)
+ , serialize_name(n)
+ , depends(d)
{
- this->timestamp = Anope::CurTime;
+ serializableFields.push_back(this);
}
-Type *Serialize::Type::Find(const Anope::string &name)
+FieldBase::~FieldBase()
{
- std::map<Anope::string, Type *>::iterator it = Types.find(name);
- if (it != Types.end())
- return it->second;
- return NULL;
+ auto it = std::find(serializableFields.begin(), serializableFields.end(), this);
+ if (it != serializableFields.end())
+ serializableFields.erase(it);
}
-const std::vector<Anope::string> &Type::GetTypeOrder()
+void FieldBase::Unregister()
{
- return TypeOrder;
+ Anope::Logger.Debug2("Unregistering field {0} on {1}", serialize_name, serialize_type);
+
+ /* find edges on this field */
+ for (Object *s : Serialize::GetObjects<Object *>(serialize_type))
+ {
+ for (const std::pair<TypeBase *, std::vector<Edge>> &p : s->edges)
+ for (const Edge &edge : p.second)
+ if (edge.direction && edge.field == this)
+ {
+ Anope::Logger.Debug2("Removing edge on #{0} type {1} -> #{2} type {3}", s->id, s->GetSerializableType()->GetName(), edge.other->id, edge.other->GetSerializableType()->GetName());
+ s->RemoveEdge(edge.other, edge.field);
+
+ goto cont;
+ }
+ cont:;
+ }
}
-const std::map<Anope::string, Serialize::Type *>& Type::GetTypes()
+void Serialize::SetParent(const Anope::string &child, const Anope::string &parent)
{
- return Types;
+ child_types.insert(std::make_pair(parent, child));
}
+std::vector<Serialize::TypeBase *> Serialize::GetTypes(const Anope::string &name)
+{
+ std::vector<Serialize::TypeBase *> v;
+
+ Serialize::TypeBase *t = Serialize::TypeBase::Find(name);
+ if (t != nullptr)
+ v.push_back(t);
+ else
+ Anope::Logger.Debug2("GetTypes for unknown type {0}", name);
+
+ auto its = child_types.equal_range(name);
+ for (; its.first != its.second; ++its.first)
+ {
+ t = Serialize::TypeBase::Find(its.first->second);
+ if (t != nullptr)
+ v.push_back(t);
+ }
+
+ return v;
+}