summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/managed/Managed.hpp28
-rw-r--r--src/core/managed/ManagedContainer.hpp13
-rw-r--r--src/core/managed/Manager.cpp161
-rw-r--r--src/core/managed/Manager.hpp87
4 files changed, 274 insertions, 15 deletions
diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp
index 04c2f84..eec2fc5 100644
--- a/src/core/managed/Managed.hpp
+++ b/src/core/managed/Managed.hpp
@@ -64,7 +64,7 @@ public:
/**
* Virtual destuctor which may be overwritten by child classes.
*/
- virtual ~Managed(){};
+ virtual ~Managed(){ mgr.unmanage(this); };
/**
* Returns a reference ot the manager instance which owns this managed
@@ -112,6 +112,14 @@ public:
}
return res;
}
+
+ void storeData(const std::string &key, Handle<Managed> h);
+
+ bool hasDataKey(const std::string &key);
+
+ Rooted<Managed> readData(const std::string &key);
+
+ bool deleteData(const std::string &key);
};
/**
@@ -498,6 +506,24 @@ public:
Managed *getOwner() const { return owner; }
};
+
+inline void Managed::storeData(const std::string &key, Handle<Managed> h) {
+ mgr.storeData(this, key, h.get());
+}
+
+inline bool Managed::hasDataKey(const std::string &key)
+{
+ return mgr.readData(this, key) != nullptr;
+}
+
+inline Rooted<Managed> Managed::readData(const std::string &key) {
+ return mgr.readData(this, key);
+}
+
+inline bool Managed::deleteData(const std::string &key) {
+ return mgr.deleteData(this, key);
+}
+
}
#endif /* _OUSIA_MANAGED_HPP_ */
diff --git a/src/core/managed/ManagedContainer.hpp b/src/core/managed/ManagedContainer.hpp
index 9ff75d5..7b18bcd 100644
--- a/src/core/managed/ManagedContainer.hpp
+++ b/src/core/managed/ManagedContainer.hpp
@@ -31,8 +31,8 @@
namespace ousia {
/**
- * Template class which can be used to collect "Owned" refrences to a certain
- * type of managed object. Do not use this class directly, use ManagedMap or
+ * Template class which can be used to collect refrences to a certain type of
+ * managed objects. Do not use this class directly, use ManagedMap or
* ManagedVector instead. This class only provides functionality which is common
* to list and map containers (iterators and state).
*
@@ -40,7 +40,7 @@ namespace ousia {
* @param Collection should be a STL container of Owned<T>
*/
template <class T, class Collection>
-class ManagedContainer {
+class ManagedContainer : Managed {
public:
using collection_type = Collection;
using value_type = typename collection_type::value_type;
@@ -81,7 +81,12 @@ public:
* @param owner is the managed object which owns the collection and all
* handles to other managed objects stored within.
*/
- ManagedContainer(Handle<Managed> owner) : owner(owner){};
+ ManagedContainer(Handle<Managed> owner) : Managed(owner->getManager), owner(owner){};
+
+ /**
+ * Destructor of the ManagedContainer class.
+ */
+ virtual ~ManagedContainer() {};
/* State functions */
size_type size() const noexcept { return c.size(); }
diff --git a/src/core/managed/Manager.cpp b/src/core/managed/Manager.cpp
index 7b562a8..411c675 100644
--- a/src/core/managed/Manager.cpp
+++ b/src/core/managed/Manager.cpp
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <cassert>
+
#include "Managed.hpp"
#include "Manager.hpp"
@@ -48,7 +50,7 @@ public:
~ScopedIncrement() { i--; }
};
-/* Class ObjectDescriptor */
+/* Class Manager::ObjectDescriptor */
bool Manager::ObjectDescriptor::hasInRef() const
{
@@ -127,6 +129,8 @@ Manager::~Manager()
}
}
+/* Class Manager: Garbage collection */
+
Manager::ObjectDescriptor *Manager::getDescriptor(Managed *o)
{
if (o) {
@@ -145,6 +149,11 @@ void Manager::manage(Managed *o)
void Manager::addRef(Managed *tar, Managed *src)
{
+ // Make sure the source and target manager are the same
+ if (src) {
+ assert(&tar->getManager() == &src->getManager());
+ }
+
// Fetch the Managed descriptors for the two objects
ObjectDescriptor *dTar = getDescriptor(tar);
ObjectDescriptor *dSrc = getDescriptor(src);
@@ -165,6 +174,11 @@ void Manager::addRef(Managed *tar, Managed *src)
void Manager::deleteRef(Managed *tar, Managed *src, bool all)
{
+ // Make sure the source and target manager are the same
+ if (src) {
+ assert(&tar->getManager() == &src->getManager());
+ }
+
// Fetch the Managed descriptors for the two objects
ObjectDescriptor *dTar = getDescriptor(tar);
ObjectDescriptor *dSrc = getDescriptor(src);
@@ -177,9 +191,8 @@ void Manager::deleteRef(Managed *tar, Managed *src, bool all)
// Decrement the input degree of the input Managed
if (dTar && dTar->decrDegree(RefDir::IN, src, all)) {
// If the Managed has a zero in degree, it can be safely deleted,
- // otherwise
- // if it has no root reference, add it to the "marked" set which is
- // subject to tracing garbage collection
+ // otherwise if it has no root reference, add it to the "marked" set
+ // which is subject to tracing garbage collection
if (!dTar->hasInRef()) {
deleteObject(tar, dTar);
} else if (dTar->rootRefCount == 0) {
@@ -205,15 +218,17 @@ void Manager::deleteObject(Managed *o, ObjectDescriptor *descr)
}
// Increment the recursion depth counter. The "deleteRef" function called
- // below
- // may descend further into this function and the actual deletion should be
- // done in a single step.
+ // below may descend further into this function and the actual deletion
+ // should be done in a single step.
{
ScopedIncrement incr{deletionRecursionDepth};
// Add the Managed to the "deleted" set
deleted.insert(o);
+ // Remove the data store entry
+ store.erase(o);
+
// Remove all output references of this Managed
while (!descr->refOut.empty()) {
deleteRef(descr->refOut.begin()->first, o, true);
@@ -329,5 +344,137 @@ void Manager::sweep()
purgeDeleted();
}
}
+
+/* Class Manager: Attached data */
+
+void Manager::storeData(Managed *ref, const std::string &key, Managed *data)
+{
+ // Add the new reference from the reference object to the data object
+ addRef(data, ref);
+
+ // Make sure a data map for the given reference object exists
+ auto &map = store.emplace(ref, std::map<std::string, Managed *>{}).first->second;
+
+ // Insert the given data for the key, decrement the references if
+ auto it = map.find(key);
+ if (it == map.end()) {
+ map.insert(it, std::make_pair(key, data));
+ } else {
+ // Do nothing if the same data is stored
+ if (it->second == data) {
+ return;
+ }
+
+ // Delete the reference from "ref" to the previously stored element.
+ deleteRef(it->second, ref);
+
+ // Insert a new reference and add the element to the map
+ map.insert(map.erase(it), std::make_pair(key, data));
+ }
+}
+
+Managed *Manager::readData(Managed *ref, const std::string &key) const
+{
+ // Try to find the reference element in the store
+ auto storeIt = store.find(ref);
+ if (storeIt != store.end()) {
+ // Try to find the key in the map for the element
+ auto &map = storeIt->second;
+ auto mapIt = map.find(key);
+ if (mapIt != map.end()) {
+ return mapIt->second;
+ }
+ }
+ return nullptr;
+}
+
+std::map<std::string, Managed *> Manager::readData(Managed *ref) const
+{
+ // Try to find the map for the given reference element and return it
+ auto storeIt = store.find(ref);
+ if (storeIt != store.end()) {
+ return storeIt->second;
+ }
+ return std::map<std::string, Managed *>{};
+}
+
+bool Manager::deleteData(Managed *ref, const std::string &key)
+{
+ // Find the reference element in the store
+ auto storeIt = store.find(ref);
+ if (storeIt != store.end()) {
+ // Delete the key from the data map
+ auto &map = storeIt->second;
+ auto mapIt = map.find(key);
+ if (mapIt != map.end()) {
+ // Delete the reference from "ref" to the previously stored element
+ deleteRef(mapIt->second, ref);
+
+ // Remove the element from the list
+ map.erase(mapIt);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Class Manager: Tagged Memory */
+
+void Manager::tagMemoryRegion(void *tag, void *pStart, void *pEnd)
+{
+ // Convert start and end to integers
+ uintptr_t s1 = reinterpret_cast<uintptr_t>(pStart);
+ uintptr_t e1 = reinterpret_cast<uintptr_t>(pEnd);
+
+#ifndef NDEBUG
+ // Make sure the same memory region is not tagged multiple times
+ const auto itStart = tags.lower_bound(s1);
+ const auto itEnd = tags.upper_bound(e1);
+ for (auto it = itStart; it != itEnd; it++) {
+ uintptr_t s2 = it->first;
+ uintptr_t e2 = it->second.first;
+ assert(!((s2 >= s1 && s2 < e1) || (e2 >= s1 && e2 < e1)));
+ }
+#endif
+
+ // Insert the new region
+ tags.emplace(s1, std::make_pair(e1, tag));
+}
+
+void Manager::untagMemoryRegion(void *pStart, void *pEnd)
+{
+ // Convert start and end to integers
+ uintptr_t s1 = reinterpret_cast<uintptr_t>(pStart);
+ uintptr_t e1 = reinterpret_cast<uintptr_t>(pEnd);
+
+ auto itStart = tags.lower_bound(s1);
+ auto itEnd = tags.upper_bound(e1);
+ for (auto it = itStart; it != itEnd;) {
+ // Copy the data of the element
+ uintptr_t s2 = it->first;
+ uintptr_t e2 = it->second.first;
+ void* tag = it->second.second;
+
+ // Remove the element from the map as we need to modify it
+ it = tags.erase(it);
+ if (s1 <= s2 && e1 >= e2) {
+ // Remove completely covered elements
+ continue;
+ }
+ if (s1 > s2) {
+ // Insert s1-s2 section
+ it = tags.emplace(s2, std::make_pair(s1, tag)).first;
+ }
+ if (e1 < e2) {
+ // Insert e1-e2 section
+ it = tags.emplace(e1, std::make_pair(e2, tag)).first;
+ }
+
+ // Go to the next element
+ it++;
+ }
+}
+
}
diff --git a/src/core/managed/Manager.hpp b/src/core/managed/Manager.hpp
index 95d08e1..5b08cf4 100644
--- a/src/core/managed/Manager.hpp
+++ b/src/core/managed/Manager.hpp
@@ -27,8 +27,9 @@
#ifndef _OUSIA_MANAGER_HPP_
#define _OUSIA_MANAGER_HPP_
-#include <cassert>
+#include <cstdint>
#include <map>
+#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -39,6 +40,12 @@ namespace ousia {
// Forward declaration
class Managed;
+/**
+ * The Manager class implements tracing garbage collection. Garbage Collection
+ * is implemented as a simple directed reference graph with connected component
+ * detection. Garbage collection is performed whenever the number of objects
+ * marked as "probably unreachable" surpasses a certain threshold.
+ */
class Manager {
public:
/**
@@ -126,7 +133,6 @@ private:
*/
static constexpr size_t SWEEP_THRESHOLD = 128;
-protected:
/**
* Threshold that defines the minimum number of entries in the "marked"
* set until "sweep" is called.
@@ -150,6 +156,16 @@ protected:
std::unordered_set<Managed *> deleted;
/**
+ * Map storing the data attached to managed objects.
+ */
+ std::unordered_map<Managed *, std::map<std::string, Managed *>> store;
+
+ /**
+ * Map for storing the tagged memory regions.
+ */
+ std::map<uintptr_t, std::pair<uintptr_t, void*>> tags;
+
+ /**
* Recursion depth while performing deletion. This variable is needed
* because the deletion of an object may cause further objects to be
* deleted. Yet the actual deletion should only be performed at the
@@ -207,7 +223,8 @@ public:
/**
* Registers an object for being managed by the Manager. The Manager now has
* the sole responsibility for freeing the managed object. Under no
- * circumstances free the object manually, this will result in double frees.
+ * circumstances free the object manually as long as other Managed objects
+ * still hold references to it.
*
* @param o is the object which is registered for being used with the
* Manager.
@@ -243,6 +260,70 @@ public:
* Performs garbage collection.
*/
void sweep();
+
+ /**
+ * Registers some arbitrary data (in form of a Managed object) for the
+ * given reference Managed object under a certain (string) key. Overrides
+ * references to existing data for that key.
+ *
+ * @param ref is the Managed object for which the data should be stored.
+ * @param key is the key under which the data should be stored.
+ * @param data is a reference to Managed object containing the data that
+ * should be stored.
+ */
+ void storeData(Managed *ref, const std::string &key, Managed *data);
+
+ /**
+ * Returns the arbitrary data stored for the given reference managed object.
+ *
+ * @param ref is the Managed object for which the data should be stored.
+ * @param key is the key for which the data should be retrieved.
+ * @return a reference to the associated data with the given key.
+ */
+ Managed *readData(Managed *ref, const std::string &key) const;
+
+ /**
+ * Returns a const reference to a map containing all keys and the associated
+ * data objects.
+ *
+ * @param ref is the Managed object for which the data should be stored.
+ * @return a reference to the internal map from keys to managed objects.
+ */
+ std::map<std::string, Managed *> readData(Managed *ref) const;
+
+ /**
+ * Deletes the data stored for the given object with the given key.
+ *
+ * @param ref is the Managed object for which the data should be stored.
+ * @param key is the key for which the data should be retrieved.
+ * @return true if data for this key was deleted, false otherwise.
+ */
+ bool deleteData(Managed *ref, const std::string &key);
+
+ /**
+ * Stores a tag for the given memory region. May not overlap with another
+ * memory region.
+ *
+ * @param tag is user defined data that should be stored.
+ * @param pStart marks the beginning of the memory region (inclusive),
+ * @param pEnd is the end of the memory region (not inclusive).
+ */
+ void tagMemoryRegion(void *tag, void *pStart, void *pEnd);
+
+ /**
+ * Removes the tag from the given memory region. May be a part of a
+ * previously tagged region.
+ */
+ void untagMemoryRegion(void *pStart, void *pEnd);
+
+ /**
+ * Returns the tag for the given pointer or nullptr if no tag is set.
+ *
+ * @param p is the pointer for which the tag should be queried.
+ * @return the associated tag or nullptr if p points at a memory region for
+ * which no tag is set.
+ */
+ void* memoryRegionTag(void *p);
};
}