Honeycomb  0.1
Component-Model Framework
Pool.h
Go to the documentation of this file.
1 // Honeycomb, Copyright (C) 2015 NewGamePlus Inc. Distributed under the Boost Software License v1.0.
2 #pragma once
3 
4 #include "Honey/Misc/MtMap.h"
6 
7 namespace honey
8 {
9 
10 namespace lockfree { template<class> class FreeList; }
11 
14 
16 
30 {
31  template<class> friend class lockfree::FreeList;
32 public:
39  MemPool(const vector<tuple<szt,szt>>& buckets, const Id& id = idnull, szt align = alignof(double));
40 
42  void* alloc(szt size, uint8 align = 1, const char* srcFile = nullptr, int srcLine = 0);
44  void free(void* ptr_);
45 
47  szt allocBytes() const;
49  szt usedBytes() const;
51  szt freeBytes() const { return allocBytes() - usedBytes(); }
52 
53  #ifdef DEBUG
54  void validate() const;
57  String printStats() const;
59  String printUsed() const;
60  #else
61  void validate() const {}
62  String printStats() const { return ""; }
63  String printUsed() const { return ""; }
64  #endif
65 
66  const Id& id() const { return _id; }
67 
68 private:
70  class Bucket
71  {
72  public:
74 
75  struct Handle
76  {
77  //divide max swappable into 2 parts, index and tag
78  typedef mt::uintBySize<sizeof(atomic::SwapMaxType)/2>::type Int;
79 
80  Handle() : index(-1) {}
81  Handle(nullptr_t) : Handle() {}
82  Handle(uint8 chunk, Int block) : index((block << 8) | chunk) {}
83 
84  bool operator==(Handle rhs) const { return index == rhs.index; }
85  bool operator!=(Handle rhs) const { return !operator==(rhs); }
86  explicit operator bool() const { return index != Int(-1); }
87 
88  uint8 chunk() const { return index & 0xFF; }
89  Int block() const { return index >> 8; }
90 
92  };
93 
96  {
97  TaggedHandle() : tag(0) {}
98  TaggedHandle(Handle handle, Int tag) : Handle(handle), tag(tag) {}
99 
100  TaggedHandle& operator=(Handle rhs) { Handle::operator=(rhs); return *this; }
101 
102  bool operator==(TaggedHandle rhs) const { return Handle::operator==(rhs) && tag == rhs.tag; }
103  bool operator!=(TaggedHandle rhs) const { return !operator==(rhs); }
104 
105  const Handle& handle() const { return *this; }
106  Handle& handle() { return *this; }
107  Int nextTag() const { return tag+1; }
108 
110  };
111 
113  struct BlockHeader
114  {
115  struct Debug
116  {
117  static const int sigFree = 0xB10CF8EE;
118  static const int sigUsed = 0x05EDB10C;
119 
121  int srcLine;
122  const char* srcFile;
124  int sig;
125  };
126 
128  uint8& bucket() { return *(reinterpret_cast<uint8*>(this) + sizeof(BlockHeader) - 1); }
129 
130  #ifdef DEBUG
131  void validate(int sig) { assert(debug.sig == sig, "Error: invalid block signature. Block overwritten by overflow or in unexpected state (eg. freed twice)."); }
133  #else
134  void validate(int) {}
135  #endif
136 
137  #ifdef DEBUG
139  #endif
144  };
145 
146  static BlockHeader* blockHeader(uint8* data) { return reinterpret_cast<BlockHeader*>(data - sizeof(BlockHeader)); }
147  static uint8* blockData(BlockHeader* header) { return reinterpret_cast<uint8*>(header) + sizeof(BlockHeader); }
148 
149  Bucket(MemPool& pool, szt blockSize, szt blockCount) :
150  _pool(pool),
151  _bucketIndex(-1),
152  _blockSize(blockSize),
153  _blockCountInit(blockCount),
154  _blockCount(0),
155  _chunkCount(0),
156  _chunkSizeTotal(0),
157  _freeHead(TaggedHandle()),
158  _freeCount(0),
159  _usedCount(0),
160  _usedSize(0)
161  {}
162 
163  ~Bucket()
164  {
165  //Delete all expansion chunks. The first chunk is the initial pool allocation, we don't own it.
166  for (auto i : range(1, _chunkCount.load())) delete_(_chunks[i].data());
167  }
168 
170  void initChunk(uint8* chunk, szt chunkSize, szt blockCount);
172  void* alloc(szt size, uint8 align, const char* srcFile, int srcLine);
174  void reserve(szt capacity);
176  void expand();
178  void free(BlockHeader* header);
179 
180  szt blockOffsetMax() const { return _pool._blockAlign-1; }
181  szt blockStride() const { return (szt)(intptr_t)alignCeil((void*)(_blockSize + sizeof(BlockHeader)), _pool._blockAlign); }
182 
184  BlockHeader* deref(Handle handle) const
185  {
186  assert(handle && handle.chunk() < _chunkCount);
187  auto& chunk = _chunks[handle.chunk()];
188  assert(blockStride()*handle.block() < chunk.size());
189  uint8* blockData = alignCeil(chunk.data() + sizeof(BlockHeader), _pool._blockAlign);
190  return blockHeader(blockData + blockStride()*handle.block());
191  }
192 
193  MemPool& _pool;
194  uint8 _bucketIndex;
195  const szt _blockSize;
196  const szt _blockCountInit;
197  szt _blockCount;
198  array<Buffer<uint8>, numeral<uint64>().sizeBits> _chunks;
199  Atomic<uint8> _chunkCount;
200  Atomic<szt> _chunkSizeTotal;
201  Atomic<TaggedHandle> _freeHead;
202  Atomic<szt> _freeCount;
203  TaggedHandle _usedHead;
204  Atomic<szt> _usedCount;
205  szt _usedSize;
206  SpinLock _lock;
207  };
208 
210  class Heap
211  {
212  public:
214  struct BlockHeader
215  {
216  struct Debug
217  {
218  int srcLine;
219  const char* srcFile;
221  int sig;
222  };
223 
224  static const uint8 heapTag = -1;
225 
227  uint8& tag() { return *(reinterpret_cast<uint8*>(this) + sizeof(BlockHeader) - 1); }
228 
229  #ifdef DEBUG
230  void validate(int sig) { assert(debug.sig == sig, "Error: invalid block signature. Block overwritten by overflow or in unexpected state (eg. freed twice)."); }
232  #else
233  void validate(int) {}
234  #endif
235 
236  #ifdef DEBUG
238  #endif
243  };
244 
245  static BlockHeader* blockHeader(uint8* data) { return reinterpret_cast<BlockHeader*>(data - sizeof(BlockHeader)); }
246  static uint8* blockData(BlockHeader* header) { return reinterpret_cast<uint8*>(header) + sizeof(BlockHeader); }
247 
248  Heap(MemPool& pool) :
249  _pool(pool),
250  _allocTotal(0),
251  _usedHead(nullptr),
252  _usedCount(0)
253  {}
254 
256  void* alloc(szt size, uint8 align, const char* srcFile, int srcLine);
258  void free(BlockHeader* header);
259 
260  MemPool& _pool;
261  Atomic<szt> _allocTotal;
262  BlockHeader* _usedHead;
263  Atomic<szt> _usedCount;
264  SpinLock _lock;
265  };
266 
267  #ifdef DEBUG
268  void lock() const;
269  void unlock() const;
270  #endif
271 
272  Id _id;
273  const szt _blockAlign;
274  szt _blockSizeMax;
275  vector<UniquePtr<Bucket>> _buckets;
276  std::map<szt, Bucket*> _bucketMap;
277  UniquePtr<uint8> _bucketChunk;
278  UniquePtr<Heap> _heap;
279 };
280 
282 
287 template<template<class> class Subclass, class T>
288 class MemPoolAllocator : public Allocator<Subclass, T>
289 {
291 public:
292  using typename Super::pointer;
293  using typename Super::size_type;
294 
295  pointer allocate(size_type n, const void* = 0) { return static_cast<pointer>(this->subc().pool().alloc(sizeof(T)*n)); }
296  pointer allocate(size_type n, const char* srcFile, int srcLine, const void* = 0)
297  { return static_cast<pointer>(this->subc().pool().alloc(sizeof(T)*n, 1, srcFile, srcLine)); }
298  void deallocate(pointer p, size_type) { this->subc().pool().free(p); }
299 };
300 
302 
303 }
pointer allocate(size_type n, const char *srcFile, int srcLine, const void *=0)
Definition: Pool.h:296
void validate() const
Ensure that all used/free blocks are valid (check signatures)
Definition: Pool.cpp:308
uint8 & tag()
Get tag from reserved area.
Definition: Pool.h:227
Enables blocks to be referenced via index rather than pointer so that they can include a tag while st...
Definition: Pool.h:75
Holds block handle and tag to prevent lock-free ABA issues.
Definition: Pool.h:95
int srcLine
Definition: Pool.h:218
static const uint8 heapTag
Definition: Pool.h:224
T * alignCeil(T *p, szt bytes)
Align a pointer to the next byte boundary bytes. Does nothing if p is already on boundary. Alignment must be a power of two.
Definition: Allocator.h:43
Int index
Definition: Pool.h:91
Debug debug
Definition: Pool.h:237
bool operator==(TaggedHandle rhs) const
Definition: Pool.h:102
Bucket block header.
Definition: Pool.h:113
const Handle & handle() const
Definition: Pool.h:105
uint8 chunk() const
Definition: Pool.h:88
void free(void *ptr_)
Free a memory block allocated from the pool.
Definition: Pool.cpp:261
String printStats() const
Print statistics about pool.
Definition: Pool.cpp:326
uint8 & bucket()
Get bucket from reserved area.
Definition: Pool.h:128
const char * srcFile
Definition: Pool.h:219
MemPool(const vector< tuple< szt, szt >> &buckets, const Id &id=idnull, szt align=alignof(double))
Definition: Pool.cpp:209
Inherit to declare that class is not copyable.
Definition: Meta.h:286
void delete_(T *&p)
Destruct object, free memory and set pointer to null.
Definition: Allocator.h:75
Handle(nullptr_t)
Definition: Pool.h:81
Lock-free freelist, allocates re-usable objects and provides automatic storage expansion for concurre...
Definition: Pool.h:10
void validate(int sig)
Assert that block signature is valid and matches expected value sig
Definition: Pool.h:231
void * alloc(szt size, uint8 align=1, const char *srcFile=nullptr, int srcLine=0)
Allocate a size bytes block of memory at byte boundary align. alignment must be a power of two...
Definition: Pool.cpp:249
T * pointer
Definition: Allocator.h:104
std::enable_if< mt::isIterator< Iter1 >::value, Range_< Iter1, Iter2 > >::type range(Iter1 &&first, Iter2 &&last)
Range from iterators [first, last)
Definition: Range.h:116
Int block() const
Definition: Pool.h:89
Subclass< T > & subc()
Definition: Allocator.h:123
static const int sigFree
Block Free.
Definition: Pool.h:117
TaggedHandle(Handle handle, Int tag)
Definition: Pool.h:98
uint8 reserved
Last byte is reserved to differentiate block header types.
Definition: Pool.h:143
szt allocBytes() const
Calc total bytes allocated by pool.
Definition: Pool.cpp:278
unsigned char uint8
Definition: Core.h:12
szt size_type
Definition: Allocator.h:108
Handle & handle()
Definition: Pool.h:106
std::allocator compatible allocator
Definition: Allocator.h:100
static const int sigUsed
Used Block.
Definition: Pool.h:118
#define assert(...)
Forwards to assert_#args. See assert_1(), assert_2().
Definition: Debug.h:24
Debug debug
Definition: Pool.h:138
BlockHeader * next
Definition: Pool.h:239
void deallocate(pointer p, size_type)
Definition: Pool.h:298
#define idnull
Null id.
Definition: Id.h:124
uint8 offset
Offset from original block position due to alignment (can change each allocation) ...
Definition: Pool.h:241
int sig
Signature sentinel to verify block state.
Definition: Pool.h:124
MemPool allocator.
Definition: Pool.h:288
void validate(int sig)
Assert that block signature is valid and matches expected value sig
Definition: Pool.h:132
Handle prev
Definition: Pool.h:123
Unicode UTF-16 string class, wrapper around std::u16string.
Definition: String.h:23
Handle()
Definition: Pool.h:80
size_t szt
Size type, shorthand for size_t.
Definition: Core.h:90
uint8 offset
Offset from original block position due to alignment (can change each allocation) ...
Definition: Pool.h:142
szt size
Definition: Pool.h:240
Memory pool.
Definition: Pool.h:29
Handle next
Definition: Pool.h:141
szt usedBytes() const
Calc total bytes used in pool.
Definition: Pool.cpp:286
bool operator!=(TaggedHandle rhs) const
Definition: Pool.h:103
bool operator!=(Handle rhs) const
Definition: Pool.h:85
String printUsed() const
Print all used blocks.
Definition: Pool.cpp:391
int size(const StdContainer &cont)
Safely get the size of a std container as a signed integer.
Definition: StdUtil.h:19
Handle handle
Definition: Pool.h:140
Int nextTag() const
Definition: Pool.h:107
const char * srcFile
Definition: Pool.h:122
uint8 reserved
Last byte is reserved to differentiate block header types.
Definition: Pool.h:242
TaggedHandle()
Definition: Pool.h:97
Int tag
Definition: Pool.h:109
int sig
Signature sentinel to verify block state.
Definition: Pool.h:221
pointer allocate(size_type n, const void *=0)
Definition: Pool.h:295
Holds a name string and its hashed value for fast comparison ops. See String Identifier.
Definition: Id.h:25
Handle(uint8 chunk, Int block)
Definition: Pool.h:82
Heap block header.
Definition: Pool.h:214
BlockHeader * prev
Definition: Pool.h:220
platform::SwapMaxType SwapMaxType
Largest atomically-swappable type.
Definition: Atomic.h:105
Global Honeycomb namespace.
const Id & id() const
Definition: Pool.h:66
mt::uintBySize< sizeof(atomic::SwapMaxType)/2 >::type Int
Definition: Pool.h:78
bool operator==(Handle rhs) const
Definition: Pool.h:84
TaggedHandle & operator=(Handle rhs)
Definition: Pool.h:100
szt freeBytes() const
Calc total bytes free in pool.
Definition: Pool.h:51