runtime 1.0.0
Checked CEL programs, owned values, and reusable execution stacks
Loading...
Searching...
No Matches
retirement.hpp
1// SPDX-License-Identifier: BSL-1.0
2#ifndef CEL_RUNTIME_DETAIL_RETIREMENT_HPP
3#define CEL_RUNTIME_DETAIL_RETIREMENT_HPP
4#include <atomic>
5#include <cassert>
6#include <cstddef>
7#include <type_traits>
8#include <utility>
9
10namespace cel::detail {
11static_assert(std::atomic<std::size_t>::is_always_lock_free,
12 "runtime ownership requires lock-free reference counts");
13class retirement_queue;
14
17struct retirement_node {
18 std::atomic<std::size_t> owners{1};
19 retirement_node* next = nullptr;
20 void (*dispose)(retirement_node&, retirement_queue&) noexcept;
21
23 explicit retirement_node(void (*operation)(retirement_node&, retirement_queue&) noexcept) :
24 dispose(operation) {}
25};
26
30class retirement_queue {
31 retirement_node* _pending = nullptr;
32
33public:
35 retirement_queue() noexcept = default;
37 retirement_queue(const retirement_queue&) = delete;
39 retirement_queue& operator=(const retirement_queue&) = delete;
42 ~retirement_queue() {
43 while (_pending) {
44 auto* current = _pending;
45 _pending = current->next;
46 current->next = nullptr;
47 current->dispose(*current, *this);
48 }
49 }
52 void release(retirement_node* node) noexcept {
53 // [knowledge] Identify last ownership at decrement, not with use_count plus reset:
54 // another handle may release concurrently between a snapshot and the decrement.
55 if (node && node->owners.fetch_sub(1, std::memory_order_acq_rel) == 1) {
56 node->next = _pending;
57 _pending = node;
58 }
59 }
60};
61
65template <class T>
66class retirement_ptr {
67 retirement_node* _node = nullptr;
68
69public:
71 retirement_ptr() noexcept = default;
74 explicit retirement_ptr(std::remove_const_t<T>* node) noexcept : _node(node) {}
76 retirement_ptr(const retirement_ptr& other) noexcept : _node(other._node) {
77 if (_node) _node->owners.fetch_add(1, std::memory_order_relaxed);
78 }
80 retirement_ptr(retirement_ptr&& other) noexcept : _node(std::exchange(other._node, nullptr)) {}
83 ~retirement_ptr() {
84 retirement_queue queue;
85 retire(queue);
86 }
89 retirement_ptr& operator=(const retirement_ptr& other) noexcept {
90 retirement_ptr next(other);
91 swap(next);
92 return *this;
93 }
96 retirement_ptr& operator=(retirement_ptr&& other) noexcept {
97 if (this != &other) {
98 retirement_ptr next(std::move(other));
99 swap(next);
100 }
101 return *this;
102 }
104 void swap(retirement_ptr& other) noexcept { std::swap(_node, other._node); }
106 void retire(retirement_queue& queue) noexcept { queue.release(std::exchange(_node, nullptr)); }
108 explicit operator bool() const noexcept { return _node != nullptr; }
111 T* operator->() const noexcept {
112 assert(_node);
113 return static_cast<T*>(_node);
114 }
115};
116} // namespace cel::detail
117#endif