about summary refs log tree commit diff
path: root/immer/transience/gc_transience_policy.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'immer/transience/gc_transience_policy.hpp')
-rw-r--r--immer/transience/gc_transience_policy.hpp110
1 files changed, 110 insertions, 0 deletions
diff --git a/immer/transience/gc_transience_policy.hpp b/immer/transience/gc_transience_policy.hpp
new file mode 100644
index 000000000000..d3c30efa1cf7
--- /dev/null
+++ b/immer/transience/gc_transience_policy.hpp
@@ -0,0 +1,110 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/heap/tags.hpp>
+
+#include <atomic>
+#include <memory>
+#include <utility>
+
+namespace immer {
+
+/*!
+ * Provides transience ownership tracking when a *tracing garbage
+ * collector* is used instead of reference counting.
+ *
+ * @rst
+ *
+ * .. warning:: Using this policy without an allocation scheme that
+ *    includes automatic tracing garbage collection may cause memory
+ *    leaks.
+ *
+ * @endrst
+ */
+struct gc_transience_policy
+{
+    template <typename HeapPolicy>
+    struct apply
+    {
+        struct type
+        {
+            using heap_ = typename HeapPolicy::type;
+
+            struct edit
+            {
+                void* v;
+                edit() = delete;
+                bool operator==(edit x) const { return v == x.v; }
+                bool operator!=(edit x) const { return v != x.v; }
+            };
+
+            struct owner
+            {
+                void* make_token_()
+                {
+                    return heap_::allocate(1, norefs_tag{});
+                };
+
+                mutable std::atomic<void*> token_;
+
+                operator edit() { return {token_}; }
+
+                owner()
+                    : token_{make_token_()}
+                {}
+                owner(const owner& o)
+                    : token_{make_token_()}
+                {
+                    o.token_ = make_token_();
+                }
+                owner(owner&& o) noexcept
+                    : token_{o.token_.load()}
+                {}
+                owner& operator=(const owner& o)
+                {
+                    o.token_ = make_token_();
+                    token_   = make_token_();
+                    return *this;
+                }
+                owner& operator=(owner&& o) noexcept
+                {
+                    token_ = o.token_.load();
+                    return *this;
+                }
+            };
+
+            struct ownee
+            {
+                edit token_{nullptr};
+
+                ownee& operator=(edit e)
+                {
+                    assert(e != noone);
+                    // This would be a nice safety plug but it sadly
+                    // does not hold during transient concatenation.
+                    // assert(token_ == e || token_ == edit{nullptr});
+                    token_ = e;
+                    return *this;
+                }
+
+                bool can_mutate(edit t) const { return token_ == t; }
+                bool owned() const { return token_ != edit{nullptr}; }
+            };
+
+            static owner noone;
+        };
+    };
+};
+
+template <typename HP>
+typename gc_transience_policy::apply<HP>::type::owner
+    gc_transience_policy::apply<HP>::type::noone = {};
+
+} // namespace immer