Roc Toolkit internal modules
Roc Toolkit: real-time audio streaming
pool.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Roc authors
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  */
8 
9 //! @file roc_core/pool.h
10 //! @brief Pool.
11 
12 #ifndef ROC_CORE_POOL_H_
13 #define ROC_CORE_POOL_H_
14 
15 #include "roc_core/alignment.h"
16 #include "roc_core/iallocator.h"
17 #include "roc_core/list.h"
18 #include "roc_core/log.h"
19 #include "roc_core/mutex.h"
20 #include "roc_core/noncopyable.h"
21 #include "roc_core/panic.h"
22 #include "roc_core/stddefs.h"
23 
24 namespace roc {
25 namespace core {
26 
27 //! Pool.
28 //!
29 //! @tparam T defines object type.
30 //!
31 //! Allocates chunks from given allocator containing a fixed number of fixed
32 //! sized objects. Maintains a list of free objects.
33 //!
34 //! The memory is always maximum aligned. Thread-safe.
35 template <class T> class Pool : public NonCopyable<> {
36 public:
37  //! Initialization.
38  //!
39  //! @b Parameters
40  //! - @p allocator is used to allocate chunks
41  //! - @p object_size defines object size in bytes
42  //! - @p poison enables memory poisoning for debugging
43  Pool(IAllocator& allocator, size_t object_size, bool poison)
44  : allocator_(allocator)
45  , used_elems_(0)
46  , elem_size_(max_align(std::max(sizeof(Elem), object_size)))
47  , chunk_hdr_size_(max_align(sizeof(Chunk)))
48  , chunk_n_elems_(1)
49  , poison_(poison) {
50  roc_log(LogDebug, "pool: initializing: object_size=%lu poison=%d",
51  (unsigned long)elem_size_, (int)poison);
52  }
53 
54  ~Pool() {
55  deallocate_all_();
56  }
57 
58  //! Allocate new object.
59  //! @returns
60  //! pointer to a maximum aligned uninitialized memory for a new object
61  //! or NULL if memory can't be allocated.
62  void* allocate() {
63  Elem* elem = get_elem_();
64  if (elem == NULL) {
65  return NULL;
66  }
67 
68  elem->~Elem();
69 
70  void* memory = elem;
71 
72  if (poison_) {
73  memset(memory, PoisonAllocated, elem_size_);
74  } else {
75  memset(memory, 0, elem_size_);
76  }
77 
78  return memory;
79  }
80 
81  //! Free previously allocated memory.
82  void deallocate(void* memory) {
83  if (memory == NULL) {
84  roc_panic("pool: deallocating null pointer");
85  }
86 
87  if (poison_) {
88  memset(memory, PoisonDeallocated, elem_size_);
89  }
90 
91  Elem* elem = new (memory) Elem;
92  put_elem_(elem);
93  }
94 
95  //! Destroy object and deallocate its memory.
96  void destroy(T& object) {
97  object.~T();
98  deallocate(&object);
99  }
100 
101 private:
102  enum { PoisonAllocated = 0x7a, PoisonDeallocated = 0x7d };
103 
104  struct Chunk : ListNode {};
105  struct Elem : ListNode {};
106 
107  Elem* get_elem_() {
108  Mutex::Lock lock(mutex_);
109 
110  if (free_elems_.size() == 0) {
111  allocate_chunk_();
112  }
113 
114  Elem* elem = free_elems_.front();
115  if (elem != NULL) {
116  free_elems_.remove(*elem);
117  used_elems_++;
118  }
119 
120  return elem;
121  }
122 
123  void put_elem_(Elem* elem) {
124  Mutex::Lock lock(mutex_);
125 
126  if (used_elems_ == 0) {
127  roc_panic("pool: unpaired deallocation");
128  }
129 
130  used_elems_--;
131  free_elems_.push_front(*elem);
132  }
133 
134  void allocate_chunk_() {
135  void* memory = allocator_.allocate(chunk_offset_(chunk_n_elems_));
136  if (memory == NULL) {
137  return;
138  }
139 
140  Chunk* chunk = new (memory) Chunk;
141  chunks_.push_back(*chunk);
142 
143  for (size_t n = 0; n < chunk_n_elems_; n++) {
144  Elem* elem = new ((char*)chunk + chunk_offset_(n)) Elem;
145  free_elems_.push_back(*elem);
146  }
147 
148  chunk_n_elems_ *= 2;
149  }
150 
151  void deallocate_all_() {
152  if (used_elems_ != 0) {
153  roc_panic("pool: detected leak: used=%lu free=%lu",
154  (unsigned long)used_elems_, (unsigned long)free_elems_.size());
155  }
156 
157  while (Elem* elem = free_elems_.front()) {
158  free_elems_.remove(*elem);
159  }
160 
161  while (Chunk* chunk = chunks_.front()) {
162  chunks_.remove(*chunk);
163  allocator_.deallocate(chunk);
164  }
165  }
166 
167  size_t chunk_offset_(size_t n) const {
168  return chunk_hdr_size_ + n * elem_size_;
169  }
170 
171  Mutex mutex_;
172 
173  IAllocator& allocator_;
174 
175  List<Chunk, NoOwnership> chunks_;
176  List<Elem, NoOwnership> free_elems_;
177  size_t used_elems_;
178 
179  const size_t elem_size_;
180  const size_t chunk_hdr_size_;
181  size_t chunk_n_elems_;
182 
183  const bool poison_;
184 };
185 
186 } // namespace core
187 } // namespace roc
188 
189 //! Placement new for core::Pool<T>.
190 //! @note
191 //! nothrow forces compiler to check for NULL return value before calling ctor.
192 template <class T>
193 inline void* operator new(size_t size, roc::core::Pool<T>& pool) ROC_ATTR_NOTHROW {
194  roc_panic_if(size != sizeof(T));
195  return pool.allocate();
196 }
197 
198 //! Placement delete for core::Pool<T>.
199 //! @note
200 //! Compiler calls this if ctor throws in a placement new expression.
201 template <class T>
202 inline void operator delete(void* ptr, roc::core::Pool<T>& pool)ROC_ATTR_NOTHROW {
203  pool.deallocate(ptr);
204 }
205 
206 #endif // ROC_CORE_POOL_H_
Memory allocator interface.
Definition: iallocator.h:23
#define roc_panic_if(x)
Panic if condition is true.
Definition: panic.h:26
void destroy(T &object)
Destroy object and deallocate its memory.
Definition: pool.h:96
Base class for list element.
Definition: list_node.h:26
Root namespace.
Intrusive doubly-linked list.
Definition: list.h:31
Alignment.
Mutex.
Definition: mutex.h:27
Commonly used types and functions.
size_t max_align(size_t sz)
Adjust the given size to be maximum aligned.
Definition: alignment.h:27
Intrusive doubly-linked list.
Pool(IAllocator &allocator, size_t object_size, bool poison)
Initialization.
Definition: pool.h:43
#define roc_panic(...)
Print error message and terminate program gracefully.
Definition: panic.h:42
void deallocate(void *memory)
Free previously allocated memory.
Definition: pool.h:82
Base class for non-copyable objects.
Definition: noncopyable.h:23
#define ROC_ATTR_NOTHROW
Function never throws.
Definition: attributes.h:16
Mutex.
#define roc_log(...)
Print message to log.
Definition: log.h:25
RAII mutex lock.
Definition: scoped_lock.h:21
void * allocate()
Allocate new object.
Definition: pool.h:62
Debug message.
Definition: log.h:35
Panic function.
Pool.
Definition: pool.h:35
Non-copyable object.
Logging.
Memory allocator interface.