diff options
Diffstat (limited to 'src/serialize.cpp')
-rw-r--r-- | src/serialize.cpp | 355 |
1 files changed, 262 insertions, 93 deletions
diff --git a/src/serialize.cpp b/src/serialize.cpp index 3c54b933d..5315a8469 100644 --- a/src/serialize.cpp +++ b/src/serialize.cpp @@ -1,178 +1,347 @@ /* + * Anope IRC Services * - * (C) 2003-2016 Anope Team - * Contact us at team@anope.org + * Copyright (C) 2012-2016 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; +} |