Pyrogenesis  trunk
unique_range.h
Go to the documentation of this file.
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).
76 {
77 public:
78  typedef void* pointer;
79  typedef void element_type;
80 
82  {
83  Clear();
84  }
85 
86  UniqueRange(pointer p, size_t size, IdxDeleter deleter)
87  {
88  Set(p, size, deleter);
89  }
90 
92  {
93  Pilfer(rvalue);
94  }
95 
97  {
98  UniqueRange& lvalue = rvalue;
99  if(this != &lvalue)
100  {
101  Delete();
102  Pilfer(lvalue);
103  }
104  return *this;
105  }
106 
108  {
109  Delete();
110  }
111 
112  pointer get() const
113  {
114  return pointer(address_ & ~(allocationAlignment-1));
115  }
116 
118  {
120  }
121 
122  size_t size() const
123  {
124  return size_;
125  }
126 
127  // side effect: subsequent get_deleter will return idxDeleterNone
128  pointer release() // relinquish ownership
129  {
130  pointer ret = get();
131  Clear();
132  return ret;
133  }
134 
135  void reset()
136  {
137  Delete();
138  Clear();
139  }
140 
141  void reset(pointer p, size_t size, IdxDeleter deleter)
142  {
143  Delete();
144  Set(p, size, deleter);
145  }
146 
147  void swap(UniqueRange& rhs)
148  {
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&);
157 
158 private:
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 
172  void Clear()
173  {
174  Set(0, 0, idxDeleterNone);
175  }
176 
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 
186  void Delete()
187  {
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 
198 static inline void swap(UniqueRange& p1, UniqueRange& p2)
199 {
200  p1.swap(p2);
201 }
202 
203 static inline void swap(UniqueRange&& p1, UniqueRange& p2)
204 {
205  p2.swap(p1);
206 }
207 
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 
218 
219 
220 #endif // #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE
#define PROT_WRITE
Definition: wmman.h:33
pointer release()
Definition: unique_range.h:128
static const size_t pageSize
Definition: alignment.h:83
void Set(pointer p, size_t size, IdxDeleter deleter)
Definition: unique_range.h:159
intptr_t IdxDeleter
Definition: unique_range.h:43
UniqueRange()
Definition: unique_range.h:81
LIB_API UniqueRange AllocateAligned(size_t size, size_t alignment)
Definition: unique_range.cpp:79
Definition: unique_range.h:75
void Clear()
Definition: unique_range.h:172
static void swap(UniqueRange &p1, UniqueRange &p2)
Definition: unique_range.h:198
uintptr_t address_
Definition: unique_range.h:192
void Delete()
Definition: unique_range.h:186
Definition: unique_range.h:196
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:315
LIB_API void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter *idxDeleter)
register a deleter, returning its index within the table.
Definition: unique_range.cpp:50
UniqueRange(UniqueRange &&rvalue)
Definition: unique_range.h:91
void(* UniqueRangeDeleter)(void *pointer, size_t size)
Definition: unique_range.h:48
LIB_API UniqueRange AllocateVM(size_t size, vm::PageType pageSize=vm::kDefault, int prot=PROT_READ|PROT_WRITE)
Definition: unique_range.cpp:95
UniqueRange(pointer p, size_t size, IdxDeleter deleter)
Definition: unique_range.h:86
~UniqueRange()
Definition: unique_range.h:107
IdxDeleter get_deleter() const
Definition: unique_range.h:117
Definition: vm.h:46
#define NOTHROW_DECLARE
indicate a function will not throw any synchronous exceptions, thus hopefully generating smaller and ...
Definition: code_annotation.h:85
PageType
Definition: vm.h:42
UniqueRange & operator=(UniqueRange &&rvalue)
Definition: unique_range.h:96
#define PROT_READ
Definition: wmman.h:32
void swap(UniqueRange &rhs)
Definition: unique_range.h:147
size_t size_
Definition: unique_range.h:193
static const IdxDeleter idxDeleterNone
Definition: unique_range.h:46
void Pilfer(UniqueRange &victim)
Definition: unique_range.h:177
void reset(pointer p, size_t size, IdxDeleter deleter)
Definition: unique_range.h:141
void element_type
Definition: unique_range.h:79
void * pointer
Definition: unique_range.h:78
size_t size() const
Definition: unique_range.h:122
LIB_API NOTHROW_DECLARE void CallUniqueRangeDeleter(void *pointer, size_t size, IdxDeleter idxDeleter)
Definition: unique_range.cpp:70
static const size_t allocationAlignment
Definition: alignment.h:91
void reset()
Definition: unique_range.h:135