1 /* Copyright (C) 2015 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE
24 #define INCLUDED_ALLOCATORS_UNIQUE_RANGE
25
26 #include "lib/lib_api.h"
27 #include "lib/alignment.h" // allocationAlignment
28 #include "lib/sysdep/vm.h"
29
30 // we usually don't hold multiple references to allocations, so unique_ptr
31 // can be used instead of the more complex (ICC generated incorrect code on
32 // 2 occasions) and expensive shared_ptr.
33 // a custom deleter is required because allocators such as ReserveAddressSpace need to
34 // pass the size to their deleter. we want to mix pointers from various allocators, but
35 // unique_ptr's deleter is fixed at compile-time, so it would need to be general enough
36 // to handle all allocators.
37 // storing the size and a function pointer would be one such solution, with the added
38 // bonus of no longer requiring a complete type at the invocation of ~unique_ptr.
39 // however, this inflates the pointer size to 3 words. if only a few allocator types
40 // are needed, we can replace the function pointer with an index stashed into the
41 // lower bits of the pointer (safe because all allocations' addresses are multiples
42 // of allocationAlignment).
43 typedef intptr_t IdxDeleter;
44
45 // no-op deleter (use when returning part of an existing allocation)
46 static const IdxDeleter idxDeleterNone = 0;
47
48 typedef void (*UniqueRangeDeleter)(void* pointer, size_t size);
49
50 /**
51 * register a deleter, returning its index within the table.
52 *
53 * @param deleter function pointer. must be uniquely associated with
54 * the idxDeleter storage location.
55 * @param idxDeleter location where to store the next available index.
56 * if it is already non-zero, skip the call to this function to
57 * avoid overhead.
58 *
59 * thread-safe. idxDeleter is used for mutual exclusion between
60 * multiple callers for the same deleter. concurrent registration of
61 * different deleters is also safe due to atomic increments.
62 *
63 * halts the program if more than allocationAlignment deleters are
64 * to be registered.
65 **/
66 LIB_API void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleter);
67
68 LIB_API NOTHROW_DECLARE void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter);
69
70
71 // unfortunately, unique_ptr allows constructing without a custom deleter. to ensure callers can
72 // rely upon pointers being associated with a size, we introduce a `UniqueRange' replacement.
73 // its interface is identical to unique_ptr except for the constructors, the addition of
74 // size() and the removal of operator bool (which avoids implicit casts to int).
75 class UniqueRange
76 {
77 public:
78 typedef void* pointer;
79 typedef void element_type;
80
UniqueRange()81 UniqueRange()
82 {
83 Clear();
84 }
85
UniqueRange(pointer p,size_t size,IdxDeleter deleter)86 UniqueRange(pointer p, size_t size, IdxDeleter deleter)
87 {
88 Set(p, size, deleter);
89 }
90
UniqueRange(UniqueRange && rvalue)91 UniqueRange(UniqueRange&& rvalue)
92 {
93 Pilfer(rvalue);
94 }
95
96 UniqueRange& operator=(UniqueRange&& rvalue)
97 {
98 UniqueRange& lvalue = rvalue;
99 if(this != &lvalue)
100 {
101 Delete();
102 Pilfer(lvalue);
103 }
104 return *this;
105 }
106
~UniqueRange()107 ~UniqueRange()
108 {
109 Delete();
110 }
111
get()112 pointer get() const
113 {
114 return pointer(address_ & ~(allocationAlignment-1));
115 }
116
get_deleter()117 IdxDeleter get_deleter() const
118 {
119 return IdxDeleter(address_ % allocationAlignment);
120 }
121
size()122 size_t size() const
123 {
124 return size_;
125 }
126
127 // side effect: subsequent get_deleter will return idxDeleterNone
release()128 pointer release() // relinquish ownership
129 {
130 pointer ret = get();
131 Clear();
132 return ret;
133 }
134
reset()135 void reset()
136 {
137 Delete();
138 Clear();
139 }
140
reset(pointer p,size_t size,IdxDeleter deleter)141 void reset(pointer p, size_t size, IdxDeleter deleter)
142 {
143 Delete();
144 Set(p, size, deleter);
145 }
146
swap(UniqueRange & rhs)147 void swap(UniqueRange& rhs)
148 {
149 std::swap(address_, rhs.address_);
150 std::swap(size_, rhs.size_);
151 }
152
153 // don't define construction and assignment from lvalue,
154 // but the declarations must be accessible
155 UniqueRange(const UniqueRange&);
156 UniqueRange& operator=(const UniqueRange&);
157
158 private:
Set(pointer p,size_t size,IdxDeleter deleter)159 void Set(pointer p, size_t size, IdxDeleter deleter)
160 {
161 ASSERT((uintptr_t(p) % allocationAlignment) == 0);
162 ASSERT(size_t(deleter) < allocationAlignment);
163
164 address_ = uintptr_t(p) | deleter;
165 size_ = size;
166
167 ASSERT(get() == p);
168 ASSERT(get_deleter() == deleter);
169 ASSERT(this->size() == size);
170 }
171
Clear()172 void Clear()
173 {
174 Set(0, 0, idxDeleterNone);
175 }
176
Pilfer(UniqueRange & victim)177 void Pilfer(UniqueRange& victim)
178 {
179 const size_t size = victim.size();
180 const IdxDeleter idxDeleter = victim.get_deleter();
181 pointer p = victim.release();
182 Set(p, size, idxDeleter);
183 victim.Clear();
184 }
185
Delete()186 void Delete()
187 {
188 CallUniqueRangeDeleter(get(), size(), get_deleter());
189 }
190
191 // (IdxDeleter is stored in the lower bits of address since size might not even be a multiple of 4.)
192 uintptr_t address_;
193 size_t size_;
194 };
195
196 namespace std {
197
swap(UniqueRange & p1,UniqueRange & p2)198 static inline void swap(UniqueRange& p1, UniqueRange& p2)
199 {
200 p1.swap(p2);
201 }
202
swap(UniqueRange && p1,UniqueRange & p2)203 static inline void swap(UniqueRange&& p1, UniqueRange& p2)
204 {
205 p2.swap(p1);
206 }
207
swap(UniqueRange & p1,UniqueRange && p2)208 static inline void swap(UniqueRange& p1, UniqueRange&& p2)
209 {
210 p1.swap(p2);
211 }
212
213 }
214
215 LIB_API UniqueRange AllocateAligned(size_t size, size_t alignment);
216
217 LIB_API UniqueRange AllocateVM(size_t size, vm::PageType pageSize = vm::kDefault, int prot = PROT_READ|PROT_WRITE);
218
219
220 #endif // #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE
221