1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#include "System/Object.h"
#include "System/ContainerUtil.h"
#include "System/creg/STL_Map.h"
#include "System/Log/ILog.h"
#include "System/Platform/CrashHandler.h"
CR_BIND(CObject, )
CR_REG_METADATA(CObject, (
CR_MEMBER(sync_id),
CR_MEMBER(detached),
CR_MEMBER(listening),
CR_MEMBER(listeners),
CR_MEMBER(listenersDepTbl),
CR_MEMBER(listeningDepTbl)
))
std::atomic<std::int64_t> CObject::cur_sync_id(0);
static bool VectorInsertSorted(std::vector<CObject*>& v, CObject* o)
{
return (spring::VectorInsertUniqueSorted(v, o, [](const CObject* a, const CObject* b) { return (a->GetSyncID() < b->GetSyncID()); }));
}
static bool VectorEraseSorted(std::vector<CObject*>& v, CObject* o)
{
return (spring::VectorEraseUniqueSorted(v, o, [](const CObject* a, const CObject* b) { return (a->GetSyncID() < b->GetSyncID()); }));
}
CObject::CObject() : detached(false), listeners(), listening()
{
// Note1: this static var is shared between all different types of classes synced & unsynced (CUnit, CFeature, CProjectile, ...)
// Still it doesn't break syncness even when synced objects have different sync_ids between clients as long as the sync_id is
// creation time dependent and monotonously increasing, so the _order_ remains between clients.
// Use atomic fetch-and-add, so threads don't read half written data nor write old (= smaller) numbers
sync_id = ++cur_sync_id;
assert((sync_id + 1) > sync_id); // check for overflow
}
CObject::~CObject()
{
assert(!detached);
detached = true;
for (const auto& p: listenersDepTbl) {
assert(p.first >= DEPENDENCE_ATTACKER && p.first < DEPENDENCE_COUNT);
assert(p.second < listeners.size());
for (CObject* obj: listeners[p.second]) {
obj->DependentDied(this);
const auto jt = obj->listeningDepTbl.find(p.first);
if (jt == obj->listeningDepTbl.end())
continue;
VectorEraseSorted(obj->listening[ jt->second ], this);
}
}
for (const auto& p: listeningDepTbl) {
assert(p.first >= DEPENDENCE_ATTACKER && p.first < DEPENDENCE_COUNT);
assert(p.second < listening.size());
for (CObject* obj: listening[p.second]) {
const auto jt = obj->listenersDepTbl.find(p.first);
if (jt == obj->listenersDepTbl.end())
continue;
VectorEraseSorted(obj->listeners[ jt->second ], this);
}
}
}
// NOTE:
// we can be listening to a single object from several different places
// objects are responsible for not adding the same dependence more than
// once, and preferably try to delete the dependence ASAP in order not
// to waste memory
void CObject::AddDeathDependence(CObject* obj, DependenceType dep)
{
assert(!detached && !obj->detached);
// check this explicitly
if (detached || obj->detached)
return;
VectorInsertSorted(const_cast<TSyncSafeSet&>( GetListening(dep)), obj);
VectorInsertSorted(const_cast<TSyncSafeSet&>(obj->GetListeners(dep)), this);
}
void CObject::DeleteDeathDependence(CObject* obj, DependenceType dep)
{
assert(!detached);
if (detached || obj->detached)
return;
const auto it = listeningDepTbl.find(dep);
const auto jt = obj->listenersDepTbl.find(dep);
if (it != listeningDepTbl.end()) VectorEraseSorted( listening[ it->second ], obj);
if (jt != obj->listenersDepTbl.end()) VectorEraseSorted(obj->listeners[ jt->second ], this);
}
|