source: mainline/uspace/lib/cpp/include/internal/memory/shared_payload.hpp@ de00da5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since de00da5 was de00da5, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: fixed enable_if usage, added payload_tag that allows us to properly construct smart pointers from payloads

  • Property mode set to 100644
File size: 7.1 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_INTERNAL_MEMORY_SHARED_PAYLOAD
30#define LIBCPP_INTERNAL_MEMORY_SHARED_PAYLOAD
31
32#include <cinttypes>
33#include <utility>
34
35namespace std
36{
37 template<class>
38 struct default_delete;
39
40 struct allocator_arg_t;
41}
42
43namespace std::aux
44{
45 /**
46 * At the moment we do not have atomics, change this
47 * to std::atomic<long> once we do.
48 */
49 using refcount_t = long;
50
51 /**
52 * This allows us to construct shared_ptr from
53 * a payload pointer in make_shared etc.
54 */
55 struct payload_tag_t
56 { /* DUMMY BODY */ };
57
58 inline constexpr payload_tag_t payload_tag{};
59
60 template<class D, class T>
61 void use_payload_deleter(D* deleter, T* data)
62 {
63 if (deleter)
64 (*deleter)(data);
65 }
66
67 template<class T>
68 class shared_payload_base
69 {
70 public:
71 virtual void destroy() = 0;
72 virtual T* get() const noexcept = 0;
73
74 virtual uint8_t* deleter() const noexcept = 0;
75
76 virtual void increment() noexcept = 0;
77 virtual void increment_weak() noexcept = 0;
78 virtual bool decrement() noexcept = 0;
79 virtual bool decrement_weak() noexcept = 0;
80 virtual refcount_t refs() const noexcept = 0;
81 virtual refcount_t weak_refs() const noexcept = 0;
82 virtual bool expired() const noexcept = 0;
83 virtual shared_payload_base* lock() noexcept = 0;
84
85 virtual ~shared_payload_base() = default;
86 };
87
88 template<class T, class D = default_delete<T>>
89 class shared_payload: public shared_payload_base<T>
90 {
91 public:
92 shared_payload(T* ptr, D deleter = D{})
93 : data_{ptr}, deleter_{deleter},
94 refcount_{1}, weak_refcount_{1}
95 { /* DUMMY BODY */ }
96
97 template<class... Args>
98 shared_payload(Args&&... args)
99 : data_{new T{forward<Args>(args)...}},
100 deleter_{}, refcount_{1}, weak_refcount_{1}
101 { /* DUMMY BODY */ }
102
103 template<class Alloc, class... Args>
104 shared_payload(allocator_arg_t, Alloc alloc, Args&&... args)
105 : data_{alloc.allocate(1)},
106 deleter_{}, refcount_{1}, weak_refcount_{1}
107 {
108 alloc.construct(data_, forward<Args>(args)...);
109 }
110
111 template<class Alloc, class... Args>
112 shared_payload(D deleter, Alloc alloc, Args&&... args)
113 : data_{alloc.allocate(1)},
114 deleter_{deleter}, refcount_{1}, weak_refcount_{1}
115 {
116 alloc.construct(data_, forward<Args>(args)...);
117 }
118
119 void destroy() override
120 {
121 if (refs() == 0)
122 {
123 if (data_)
124 {
125 deleter_(data_);
126 data_ = nullptr;
127 }
128
129 if (weak_refs() == 0)
130 delete this;
131 }
132 }
133
134 T* get() const noexcept override
135 {
136 return data_;
137 }
138
139 uint8_t* deleter() const noexcept override
140 {
141 return (uint8_t*)&deleter_;
142 }
143
144 void increment() noexcept override
145 {
146 __atomic_add_fetch(&refcount_, 1, __ATOMIC_ACQ_REL);
147 }
148
149 void increment_weak() noexcept override
150 {
151 __atomic_add_fetch(&weak_refcount_, 1, __ATOMIC_ACQ_REL);
152 }
153
154 bool decrement() noexcept override
155 {
156 if (__atomic_sub_fetch(&refcount_, 1, __ATOMIC_ACQ_REL) == 0)
157 {
158 /**
159 * First call to destroy() will delete the held object,
160 * so it doesn't matter what the weak_refcount_ is,
161 * but we added one and we need to remove it now.
162 */
163 decrement_weak();
164
165 return true;
166 }
167 else
168 return false;
169 }
170
171 bool decrement_weak() noexcept override
172 {
173 return __atomic_sub_fetch(&weak_refcount_, 1, __ATOMIC_ACQ_REL) == 0 && refs() == 0;
174 }
175
176 refcount_t refs() const noexcept override
177 {
178 return __atomic_load_n(&refcount_, __ATOMIC_RELAXED);
179 }
180
181 refcount_t weak_refs() const noexcept override
182 {
183 return __atomic_load_n(&weak_refcount_, __ATOMIC_RELAXED);
184 }
185
186 bool expired() const noexcept override
187 {
188 return refs() == 0;
189 }
190
191 shared_payload_base<T>* lock() noexcept override
192 {
193 refcount_t rfs = refs();
194 while (rfs != 0L)
195 {
196 if (__atomic_compare_exchange_n(&refcount_, &rfs, rfs + 1,
197 true, __ATOMIC_RELAXED,
198 __ATOMIC_RELAXED))
199 {
200 return this;
201 }
202 }
203
204 return nullptr;
205 }
206
207 private:
208 T* data_;
209 D deleter_;
210
211 /**
212 * We're using a trick where refcount_ > 0
213 * means weak_refcount_ has 1 added to it,
214 * this makes it easier for weak_ptrs that
215 * can't decrement the weak_refcount_ to
216 * zero with shared_ptrs using this object.
217 */
218 refcount_t refcount_;
219 refcount_t weak_refcount_;
220 };
221}
222
223#endif
Note: See TracBrowser for help on using the repository browser.