Zero  0.1.0
block_alloc.h
Go to the documentation of this file.
1 #ifndef __BLOCK_ALLOC_H
2 #define __BLOCK_ALLOC_H
3 
5 #include "dynarray.h"
6 #include "mem_block.h"
7 //#include "tatas.h"
8 
9 // for placement new support, which users need
10 #include <new>
11 #include "w.h"
12 #include <cstdlib>
13 #include <deque>
14 
15 /* Forward decls so we can do proper friend declarations later
16  */
17 template<class T, size_t MaxBytes> class block_alloc;
18 
19 template<class T, size_t MaxBytes> inline void* operator new(size_t nbytes, block_alloc<T, MaxBytes>& alloc);
20 
21 template<class T, size_t MaxBytes> inline void operator delete(void* ptr, block_alloc<T, MaxBytes>& alloc);
22 
23 // a basic block_pool backed by a dynarray
24 struct dynpool : public memory_block::block_pool {
25  typedef memory_block::block mblock;
26 
27  pthread_mutex_t _lock;
28 
29  //tatas_lock _lock;
30  dynarray _arr;
31 
32  std::deque<mblock*> _free_list;
33 
34  size_t _chip_size;
35 
36  size_t _chip_count;
37 
38  size_t _log2_block_size;
39 
40  size_t _arr_end;
41 
42  dynpool(size_t chip_size, size_t chip_count,
43  size_t log2_block_size, size_t max_bytes);
44 
45  virtual ~dynpool();
46 
47  virtual bool validate_pointer(void* ptr);
48 
49 protected:
50 
51  size_t _size() const;
52 
53  mblock* _at(size_t i);
54 
55  virtual
56  mblock* _acquire_block();
57 
58  virtual void _release_block(mblock* b);
59 };
60 
101 template<class T, class Pool=dynpool, size_t MaxBytes = 0>
102 struct block_pool {
103  typedef memory_block::meta_block_size<sizeof(T)> BlockSize;
104 
105  // use functions because dbx can't see enums very well
106  static size_t block_size() {
107  return BlockSize::BYTES;
108  }
109 
110  static size_t chip_count() {
111  return BlockSize::COUNT;
112  }
113 
114  static size_t chip_size() {
115  return sizeof(T);
116  }
117 
118  // gets old typing this over and over...
119 #define TEMPLATE_ARGS chip_size(), chip_count(), block_size()
120 
121  static Pool* get_static_pool() {
122  static Pool p(chip_size(), chip_count(), BlockSize::LOG2,
123  MaxBytes ? MaxBytes : 1024 * 1024 * 1024);
124  return &p;
125  }
126 
127  block_pool() :
128  _blist(get_static_pool(), TEMPLATE_ARGS) {}
129 
130  /* Acquire one object from the pool.
131  */
132  void* acquire() {
133  return _blist.acquire(TEMPLATE_ARGS);
134  }
135 
136  /* Verify that we own the object then find its block and report it
137  as released. If \e destruct is \e true then call the object's
138  desctructor also.
139  */
140  static void release(void* ptr) {
141  w_assert0(get_static_pool()->validate_pointer(ptr));
142  memory_block::block::release_chip(ptr, TEMPLATE_ARGS);
143  }
144 
145 private:
146  memory_block::block_list _blist;
147 };
148 
149 template<class T, size_t MaxBytes = 0>
150 struct block_alloc {
151  typedef block_pool<T, dynpool, MaxBytes> Pool;
152 
153  static void destroy_object(T* ptr) {
154  if (ptr == nullptr) {
155  return;
156  }
157 
158  ptr->~T();
159  Pool::release(ptr); // static method
160  // that releases the ptr into a static pool
161  // (member of block_pool) (of type dynpool)
162  }
163 
164 private:
165  Pool _pool;
166 
167  // let operator new/delete access alloc()
168  friend void* operator new<>(size_t, block_alloc<T, MaxBytes>&);
169 
170  friend void operator delete<>(void*, block_alloc<T, MaxBytes>&);
171 };
172 
173 template<class T, size_t MaxBytes>
174 inline
175 void* operator new(size_t nbytes, block_alloc<T, MaxBytes>& alloc) {
176  (void)nbytes; // keep gcc happy
177  w_assert1(nbytes == sizeof(T));
178  return alloc._pool.acquire();
179 }
180 
181 /* No, this isn't a way to do "placement delete" (if only the language
182  allowed that symmetry)... this operator is only called -- by the
183  compiler -- if T's constructor throws
184  */
185 template<class T, size_t MaxBytes>
186 inline
187 void operator delete(void* ptr, block_alloc<T, MaxBytes>& /*alloc*/) {
188  if (ptr == nullptr) {
189  return;
190  }
191  block_alloc<T, MaxBytes>::Pool::release(ptr);
192  w_assert2(0); // let a debug version catch this.
193 }
194 
195 inline
196 size_t dynpool::_size() const {
197  return _arr_end >> _log2_block_size;
198 }
199 
200 inline
201 dynpool::mblock* dynpool::_at(size_t i) {
202  size_t offset = i << _log2_block_size;
203  union {
204  char* c;
205  mblock* b;
206  } u = {_arr + offset};
207  return u.b;
208 }
209 
210 #undef TEMPLATE_ARGS
211 
212 // prototype for the object cache TFactory...
213 template<class T>
214 struct object_cache_default_factory {
215  // these first three are required... the template args are optional
216  static T*
217  construct(void* ptr) {
218  return new(ptr) T;
219  }
220 
221  static void
222  reset(T* t) { /* do nothing */ }
223 
224  static T*
225  init(T* t) { /* do nothing */ return t;
226  }
227 };
228 
229 template<class T>
230 struct object_cache_initializing_factory {
231  // these first three are required... the template args are optional
232  static T*
233  construct(void* ptr) {
234  return new(ptr) T;
235  }
236 
237  static void
238  reset(T* t) {
239  t->reset();
240  }
241 
242  static T*
243  init(T* t) {
244  t->init();
245  return t;
246  }
247 
248  // matched by object_cache::acquire below, but with the extra first T* arg...
249  template<class Arg1>
250  static T* init(T* t, Arg1 arg1) {
251  t->init(arg1);
252  return t;
253  }
254 
255  template<class Arg1, class Arg2>
256  static T* init(T* t, Arg1 arg1, Arg2 arg2) {
257  t->init(arg1, arg2);
258  return t;
259  }
260 
261  template<class Arg1, class Arg2, class Arg3>
262  static T* init(T* t, Arg1 arg1, Arg2 arg2, Arg3 arg3) {
263  t->init(arg1, arg2, arg3);
264  return t;
265  }
266 };
267 
268 template<class T, class TFactory=object_cache_default_factory<T>, size_t MaxBytes = 0>
269 struct object_cache {
270 
271  // for convenience... make sure to extend the object_cache_default_factory to match!!!
272  T* acquire() {
273  return TFactory::init(_acquire());
274  }
275 
276  template<class Arg1>
277  T* acquire(Arg1 arg1) {
278  return TFactory::init(_acquire(), arg1);
279  }
280 
281  template<class Arg1, class Arg2>
282  T* acquire(Arg1 arg1, Arg2 arg2) {
283  return TFactory::init(_acquire(), arg1, arg2);
284  }
285 
286  template<class Arg1, class Arg2, class Arg3>
287  T* acquire(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
288  return TFactory::init(_acquire(), arg1, arg2, arg3);
289  }
290 
291  T* _acquire() {
292  // constructed when its block was allocated...
293  union {
294  void* v;
295  T* t;
296  } u = {_pool.acquire()};
297  return u.t;
298  }
299 
300  static void release(T* obj) {
301  TFactory::reset(obj);
302  Pool::release(obj);
303  }
304 
305 private:
306 
307  struct cache_pool : public dynpool {
308 
309  // just a pass-thru...
310  cache_pool(size_t cs, size_t cc, size_t l2bs, size_t mb) :
311  dynpool(cs, cc, l2bs, mb) {}
312 
313  virtual void _release_block(mblock* b);
314 
315  virtual mblock* _acquire_block();
316 
317  virtual ~cache_pool();
318  };
319 
320  typedef block_pool<T, cache_pool, MaxBytes> Pool;
321 
322  Pool _pool;
323 };
324 
325 template<class T, class TF, size_t M>
326 inline
327 void object_cache<T, TF, M>::cache_pool::_release_block(mblock* b) {
328  union {
329  cache_pool* cp;
330  memory_block::block_list* bl;
331  } u = {this};
332  b->_owner = u.bl;
333  dynpool::_release_block(b);
334 }
335 
336 /* Intercept untagged (= newly-allocated) blocks in order to
337  construct the objects they contain.
338 */
339 template<class T, class TF, size_t M>
340 inline
341 dynpool::mblock* object_cache<T, TF, M>::cache_pool::_acquire_block() {
342  dynpool::mblock* b = dynpool::_acquire_block();
343  void* me = this;
344  if (me != b->_owner) {
345  // new block -- initialize its objects
346  for (size_t j = 0; j < Pool::chip_count(); j++) {
347  TF::construct(b->_get(j, Pool::chip_size()));
348  }
349  b->_owner = 0;
350  }
351  return b;
352 }
353 
354 /* Destruct all cached objects before going down
355  */
356 template<class T, class TF, size_t M>
357 inline
358 object_cache<T, TF, M>::cache_pool::~cache_pool() {
359  size_t size = _size();
360  for (size_t i = 0; i < size; i++) {
361  mblock* b = _at(i);
362  for (size_t j = 0; j < Pool::chip_count(); j++) {
363  union {
364  char* c;
365  T* t;
366  } u = {b->_get(j, Pool::chip_size())};
367  u.t->~T();
368  }
369  }
370 }
371 
372 /* A pool for holding blobs of bytes whose size is fixed but
373  determined at runtime. This class must only be instantiated once
374  per Tag.
375  */
376 struct blob_pool {
377  typedef dynpool Pool;
378 
379  typedef memory_block::block_list BlockList;
380 
381  struct helper;
382 
383  blob_pool(size_t size);
384 
385  size_t nbytes() const {
386  return _chip_size;
387  }
388 
389  void* acquire();
390 
391  // really release, but for backward compat w/ ats...
392  void destroy(void* ptr);
393 
394 private:
395  size_t _chip_size;
396 
397  size_t _block_size;
398 
399  size_t _chip_count;
400 
401  Pool _pool;
402 
403  // no copying allowed
405 
406  void operator=(blob_pool&);
407 };
408 
409 inline
410 void* operator new(size_t nbytes, blob_pool& alloc) {
411  w_assert1(nbytes <= alloc.nbytes());
412  return alloc.acquire();
413 }
414 
415 /* No, this isn't a way to do "placement delete" (if only the language
416  allowed that symmetry)... this operator is only called -- by the
417  compiler -- if T's constructor throws
418  */
419 inline
420 void operator delete(void* ptr, blob_pool& alloc) {
421  alloc.destroy(ptr);
422 }
423 
424 
425 
427 #endif // __BLOCK_ALLOC_H
Simple pool class.
Definition: stl_pool.h:40
#define w_assert1(x)
Level 1 should not add significant extra time.
Definition: w_base.h:198
#define w_assert2(x)
Level 2 adds some time.
Definition: w_base.h:206
#define w_assert0(x)
Default assert/debug level is 0.
Definition: w_base.h:175
#define T
Definition: w_okvl_inl.h:45