sandbox
Loading...
Searching...
No Matches
copy_on_write.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_SIGNAL_COPY_ON_WRITE_HPP_
3#define LIBSBX_SIGNAL_COPY_ON_WRITE_HPP_
4
5#include <utility>
6#include <atomic>
7
8namespace sbx::signals {
9
10template<typename Type>
12
13 struct payload {
14
15 template<typename... Args>
16 explicit payload(Args&&... args)
17 : count{1},
18 value{std::forward<Args>(args)...} { }
19
20 ~payload() = default;
21
22 std::atomic<std::size_t> count;
23 Type value;
24
25 }; // struct payload
26
27 template<typename Other>
28 friend auto swap(copy_on_write<Other>& lhs, copy_on_write<Other>& rhs) noexcept -> void;
29
30public:
31
32 using value_type = Type;
33
35 : _payload{new payload{}} { }
36
37 template<typename Other>
38 requires (!std::is_same_v<std::decay_t<Other>, copy_on_write>)
39 explicit copy_on_write(Other&& other)
40 : _payload{new payload{std::forward<Other>(other)}} { }
41
42 copy_on_write(const copy_on_write& other) noexcept
43 : _payload{other._payload} {
44 if (_payload) {
45 _payload->count.fetch_add(1, std::memory_order_relaxed);
46 }
47 }
48
49 copy_on_write(copy_on_write&& other) noexcept
50 : _payload{std::exchange(other._payload, nullptr)} {}
51
53 _release();
54 }
55
56 auto operator=(const copy_on_write& other) noexcept -> copy_on_write& {
57 if (this != &other) {
58 _release();
59 _payload = other._payload;
60
61 if (_payload) {
62 _payload->count.fetch_add(1, std::memory_order_relaxed);
63 }
64 }
65
66 return *this;
67 }
68
69 auto operator=(copy_on_write&& other) noexcept -> copy_on_write& {
70 if (this != &other) {
71 _release();
72 _payload = std::exchange(other._payload, nullptr);
73 }
74
75 return *this;
76 }
77
78 auto read() const noexcept -> const value_type& {
79 return _payload->value;
80 }
81
82 auto write() -> value_type& {
83 if (!_is_unique()) {
84 *this = copy_on_write{read()};
85 }
86
87 return _payload->value;
88 }
89
90private:
91
92 auto _release() -> void {
93 if (_payload && _payload->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
94 delete _payload;
95 }
96
97 _payload = nullptr;
98 }
99
100 auto _is_unique() const -> bool {
101 return _payload->count == 1;
102 }
103
104 payload* _payload;
105
106}; // class copy_on_write
107
108template<typename Type>
109inline auto swap(copy_on_write<Type>& lhs, copy_on_write<Type>& rhs) noexcept -> void {
110 using std::swap;
111 swap(lhs._payload, rhs._payload);
112}
113
114template<typename Type>
115auto cow_read(const Type& value) -> const Type& {
116 return value;
117}
118
119template<typename Type>
120auto cow_read(copy_on_write<Type>& value) -> const Type& {
121 return value.read();
122}
123
124template<typename Type>
125auto cow_write(Type& value) -> Type& {
126 return value;
127}
128
129template<typename Type>
130auto cow_write(copy_on_write<Type>& value) -> Type& {
131 return value.write();
132}
133
134} // namespace sbx::signals
135
136#endif // LIBSBX_SIGNAL_COPY_ON_WRITE_HPP_
Definition: copy_on_write.hpp:11