BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlma_concurrentfixedpool.h
Go to the documentation of this file.
1/// @file bdlma_concurrentfixedpool.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlma_concurrentfixedpool.h -*-C++-*-
8#ifndef INCLUDED_BDLMA_CONCURRENTFIXEDPOOL
9#define INCLUDED_BDLMA_CONCURRENTFIXEDPOOL
10
11/// @defgroup bdlma_concurrentfixedpool bdlma_concurrentfixedpool
12/// @brief Provide thread-safe pool of limited # of blocks of uniform size.
13/// @addtogroup bdl
14/// @{
15/// @addtogroup bdlma
16/// @{
17/// @addtogroup bdlma_concurrentfixedpool
18/// @{
19///
20/// <h1> Outline </h1>
21/// * <a href="#bdlma_concurrentfixedpool-purpose"> Purpose</a>
22/// * <a href="#bdlma_concurrentfixedpool-classes"> Classes </a>
23/// * <a href="#bdlma_concurrentfixedpool-description"> Description </a>
24/// * <a href="#bdlma_concurrentfixedpool-usage"> Usage </a>
25/// * <a href="#bdlma_concurrentfixedpool-example-1-basic-usage"> Example 1: Basic Usage </a>
26///
27/// # Purpose {#bdlma_concurrentfixedpool-purpose}
28/// Provide thread-safe pool of limited # of blocks of uniform size.
29///
30/// # Classes {#bdlma_concurrentfixedpool-classes}
31///
32/// - bdlma::ConcurrentFixedPool: thread-safe pool of limited number of blocks
33///
34/// @see bdlma_concurrentpool
35///
36/// # Description {#bdlma_concurrentfixedpool-description}
37/// This component implements a *fully thread-safe* memory pool
38/// that allocates and manages a limited number (specified at construction) of
39/// memory blocks of some uniform size (also specified at construction). A
40/// `bdlma::ConcurrentFixedPool` constructed to manage up to `N` blocks also
41/// provides an association between the address of each block and an index in
42/// the range `[ 0 .. N - 1 ]`.
43///
44/// Other than this mapping between block and index, and the associated limit on
45/// the maximum number of blocks that may be simultaneously allocated, this
46/// component's semantics are identical to `bdlma::ConcurrentPool`. In
47/// particular, this component overloads global operator `new` in the same
48/// manner, and the behaviors of `release` and `reserveCapacity` are equivalent
49/// to the corresponding methods in `bdlma::ConcurrentPool`.
50///
51/// Like `bdlma::ConcurrentPool`, this component is intended to be used to
52/// implement *out-of-place* container classes that hold elements of uniform
53/// size.
54///
55/// ## Usage {#bdlma_concurrentfixedpool-usage}
56///
57///
58/// This section illustrates intended use of this component.
59///
60/// ### Example 1: Basic Usage {#bdlma_concurrentfixedpool-example-1-basic-usage}
61///
62///
63/// `bdlma::ConcurrentFixedPool` is intended to implement *out-of-place*
64/// container classes that hold up to a fixed number of elements, all of uniform
65/// size. Suppose we wish to implement a simple thread pool. We want the
66/// equivalent of a `bsl::deque<bsl::function<void(void)> >`. However, to
67/// minimize the time spent performing operations on this deque - which must be
68/// carried out under a lock - we instead store just pointers in the deque, and
69/// manage memory efficiently using `bdlma::ConcurrentFixedPool`.
70/// `bdlma::ConcurrentFixedPool` is fully thread-safe and does not require any
71/// additional synchronization.
72///
73/// The example below is just for the container portion of our simple thread
74/// pool. The implementation of the worker thread, and the requisite
75/// synchronization, are omitted for clarity.
76/// @code
77/// class my_JobQueue {
78///
79/// public:
80/// // PUBLIC TYPES
81/// typedef bsl::function<void(void)> Job;
82///
83/// private:
84/// // DATA
85/// bslmt::Mutex d_lock;
86/// bsl::deque<Job *> d_queue;
87/// bdlma::ConcurrentFixedPool d_pool;
88/// bslma::Allocator *d_allocator_p;
89///
90/// // Not implemented:
91/// my_JobQueue(const my_JobQueue&);
92///
93/// public:
94/// // CREATORS
95/// my_JobQueue(int maxJobs, bslma::Allocator *basicAllocator = 0);
96/// ~my_JobQueue();
97///
98/// // MANIPULATORS
99/// void enqueueJob(const Job& job);
100///
101/// int tryExecuteJob();
102/// };
103///
104/// my_JobQueue::my_JobQueue(int maxJobs, bslma::Allocator *basicAllocator)
105/// : d_queue(basicAllocator)
106/// , d_pool(sizeof(Job), maxJobs, basicAllocator)
107/// , d_allocator_p(bslma::Default::allocator(basicAllocator))
108/// {
109/// }
110///
111/// my_JobQueue::~my_JobQueue()
112/// {
113/// Job *jobPtr;
114/// while (!d_queue.empty()) {
115/// jobPtr = d_queue.front();
116/// jobPtr->~Job();
117/// d_queue.pop_front();
118/// }
119/// }
120///
121/// void my_JobQueue::enqueueJob(const Job& job)
122/// {
123/// Job *jobPtr = new (d_pool) Job(job, d_allocator_p);
124/// d_lock.lock();
125/// d_queue.push_back(jobPtr);
126/// d_lock.unlock();
127/// }
128///
129/// int my_JobQueue::tryExecuteJob()
130/// {
131/// d_lock.lock();
132/// if (d_queue.empty()) {
133/// d_lock.unlock();
134/// return -1; // RETURN
135/// }
136/// Job *jobPtr = d_queue.front();
137/// d_queue.pop_front();
138/// d_lock.unlock();
139/// (*jobPtr)();
140/// d_pool.deleteObject(jobPtr);
141/// return 0;
142/// }
143/// @endcode
144/// Note that in the destructor, there is no need to deallocate the individual
145/// job objects - the destructor of `bdlma::ConcurrentFixedPool` will release
146/// any remaining allocated memory. However, it *is* necessary to invoke the
147/// destructors of all these objects, as the destructor of
148/// `bdlma::ConcurrentFixedPool` will not do so.
149/// @}
150/** @} */
151/** @} */
152
153/** @addtogroup bdl
154 * @{
155 */
156/** @addtogroup bdlma
157 * @{
158 */
159/** @addtogroup bdlma_concurrentfixedpool
160 * @{
161 */
162
163#include <bdlscm_version.h>
164
165#include <bslmt_mutex.h>
166
167#include <bsls_atomic.h>
168
169#include <bdlma_pool.h>
170
171#include <bslma_allocator.h>
172#include <bslma_deleterhelper.h>
173
174#include <bsls_alignmentutil.h>
175#include <bsls_assert.h>
176
177#include <bsl_vector.h>
178#include <bsl_cstdlib.h>
179
180
181namespace bdlma {
182
183 // ===============================
184 // struct ConcurrentFixedPool_Node
185 // ===============================
186
187/// The component-private `struct` provides a header for blocks that are
188/// allocated from `ConcurrentFixedPool` objects.
190
191 // DATA
192 unsigned d_next; // index of next free node when on free list; otherwise,
193 // index of this node itself adjusted with a generation
194 // count
195};
196
197 // =========================
198 // class ConcurrentFixedPool
199 // =========================
200
201/// This class implements a memory pool that allocates and manages up to a
202/// fixed number of memory blocks of some uniform size, with both the limit
203/// on the number of blocks and the block size specified at construction.
204///
205/// This class guarantees thread safety when allocating or releasing memory
206/// (but see the documentation for the `release` method).
207///
208/// See @ref bdlma_concurrentfixedpool
210
211 // PRIVATE TYPES
212 typedef ConcurrentFixedPool_Node Node; // type of memory block "header"
213
214 // DATA
215 bsls::AtomicInt d_freeList; // head of free list
216
217 const unsigned d_sizeMask; // mask corresponding to max size
218 // of pool; rounded up to power of
219 // 2
220
221 bsl::vector<Node *> d_nodes; // holds nodes currently being
222 // pooled; enables index <->
223 // address mapping
224
225 const int d_dataOffset; // offset (in bytes) to memory
226 // block within a 'Node'
227
228 const int d_nodeSize; // size of blocks pooled by
229 // 'd_nodePool'
230
231 bslmt::Mutex d_nodePoolMutex; // mutex for access to 'd_nodePool'
232
233 bdlma::Pool d_nodePool; // underlying memory pool
234
235 int d_numNodes; // number of nodes in 'd_nodes'
236 // that are currently being pooled
237
238 const int d_objectSize; // size of pooled objects as
239 // specified at construction
240
241 int d_backoffLevel; // determines amount of spinning
242 // when under contention
243
244 // NOT IMPLEMENTED
246 ConcurrentFixedPool& operator=(const ConcurrentFixedPool&);
247
248 private:
249 // PRIVATE MANIPULATORS
250
251 /// Allocate a memory block of the `objectSize` specified at
252 /// construction from the underlying pool from which this fixed pool
253 /// obtains memory. Return the address of that block or 0 if this pool
254 /// is exhausted (i.e., `poolSize()` memory blocks have already been
255 /// allocated from this pool).
256 void *allocateNew();
257
258 public:
259 // CREATORS
260
261 /// Create a memory pool that returns memory of the specified
262 /// `objectSize` for each invocation of the `allocate` method.
263 /// Configure this pool to support allocation of up to the specified
264 /// `poolSize` number of memory blocks. The largest supported
265 /// `poolSize` is 33554431. Optionally specify a `basicAllocator` used
266 /// to supply memory. If `basicAllocator` is 0, the currently installed
267 /// default allocator is used. The behavior is undefined unless
268 /// `0 < objectSize`, `0 < poolSize`, and `0x1FFFFFF >= poolSize`.
270 int poolSize,
271 bslma::Allocator *basicAllocator = 0);
272
273 /// Destroy this object and release all associated memory.
275
276 // MANIPULATORS
277
278 /// Allocate a memory block of the `objectSize` specified at
279 /// construction. Return the address of that block or 0 if the pool is
280 /// exhausted (i.e., `poolSize()` memory blocks have already been
281 /// allocated from this pool).
282 void *allocate();
283
284 /// Deallocate the memory block at the specified `address` back to this
285 /// pool for reuse.
286 void deallocate(void *address);
287
288 /// Destroy the specified `object` based on its dynamic type and then
289 /// use this allocator to deallocate its memory footprint. Do nothing
290 /// if `object` is 0. The behavior is undefined unless `object`, when
291 /// cast appropriately to `void *`, was allocated using this allocator
292 /// and has not already been deallocated. Note that
293 /// `dynamic_cast<void *>(object)` is applied if `TYPE` is polymorphic,
294 /// and `static_cast<void *>(object)` is applied otherwise.
295 template<class TYPE>
296 void deleteObject(const TYPE *object);
297
298 /// Destroy the specified `object` based on its static type and then use
299 /// this allocator to deallocate its memory footprint. Do nothing if
300 /// `object` is 0. The behavior is undefined if `object` is a
301 /// base-class pointer to a derived type, was not allocated using this
302 /// allocator, or has already been deallocated.
303 template <class TYPE>
304 void deleteObjectRaw(const TYPE *object);
305
306 /// Release all memory currently allocated through this object. Note
307 /// that this method should only be invoked when it is known that no
308 /// blocks currently allocated through this pool will be used;
309 /// therefore, it is not safe to use this method if any other thread may
310 /// be concurrently allocating memory from this pool. Also note that
311 /// `release()` is intended to free all memory without regard to the
312 /// contents of that memory. Specifically, `release()` can *not* call
313 /// object destructors for any allocated objects, since it has no
314 /// knowledge of their type. If object destruction is required, use
315 /// `ConcurrentFixedPool::deleteObject()`.
316 void release();
317
318 /// Reserve memory from this pool to satisfy memory requests for at
319 /// least the specified `numObjects` before the pool replenishes. The
320 /// behavior is undefined unless `0 <= numObjects`. Return 0 on success
321 /// and the number of objects that could not be reserved otherwise.
322 /// Note that this method fails if the number of memory blocks already
323 /// allocated plus `numObjects` exceeds `poolSize()`.
324 int reserveCapacity(int numObjects);
325
326 /// Configure this pool with the specified non-negative `backoffLevel`
327 /// that controls the amount of spinning that occurs when calls to this
328 /// pool encounter contention. Setting `backoffLevel` to 0 disables
329 /// spinning. Greater values of `backoffLevel` correspond to greater
330 /// amounts of spinning. The behavior is undefined unless
331 /// `0 <= backoffLevel`. Note that both contention detection and
332 /// spinning strategy are implementation defined.
334
335 // ACCESSORS
336
337 /// Return the non-negative `backoffLevel` that controls the amount of
338 /// spinning that occurs when calls to this pool encounter contention.
339 int backoffLevel() const;
340
341 /// Return an index in the range from 0 to the maximum size of this pool
342 /// that uniquely identifies the memory block at the specified
343 /// `address`. The behavior is undefined unless `address` corresponds
344 /// to a memory block allocated from this pool.
345 int indexFromAddress(void *address) const;
346
347 /// Return the size of the memory blocks allocated from this object.
348 /// Note that all blocks have the same size.
349 int objectSize() const;
350
351 /// Return the address of the memory block identified by the specified
352 /// `index`. The behavior is undefined unless the index has been
353 /// obtained through `indexFromAddress`.
354 void *addressFromIndex(int index) const;
355
356 /// Return the maximum size of this pool.
357 int poolSize() const;
358
359 // Aspects
360
361 /// Return the allocator used by this object to allocate memory. Note
362 /// that this allocator can not be used to deallocate memory
363 /// allocated through this pool.
365};
366
367} // close package namespace
368
369
370// FREE OPERATORS
371
372/// Allocate memory of the specified `size` bytes from the specified `pool`,
373/// and return the address of the allocated memory. The behavior is
374/// undefined unless `size` is the same as the `objectSize` with which
375/// `pool` was constructed. Note that an object may allocate additional
376/// memory internally within its constructor, requiring the allocator to be
377/// passed in as a constructor argument:
378/// @code
379/// my_Type *newMyType(bdlma::ConcurrentFixedPool *pool,
380/// bslma::Allocator *basicAllocator) {
381/// return new (*pool) my_Type(..., basicAllocator);
382/// }
383/// @endcode
384/// Note also that the analogous version of operator `delete` should not be
385/// called directly. Instead, this component provides a template member
386/// function `bdlma::ConcurrentFixedPool::deleteObject` parameterized by
387/// `TYPE` that performs the equivalent of the following:
388/// @code
389/// void deleteMyType(bdlma::ConcurrentFixedPool *pool, my_Type *t) {
390/// t->~my_Type();
391/// pool->deallocate(t);
392/// }
393/// @endcode
394inline
395void *operator new(bsl::size_t size,
396 BloombergLP::bdlma::ConcurrentFixedPool& pool);
397
398/// Use the specified `pool` to deallocate the memory at the specified
399/// `address`. The behavior is undefined unless `address` was allocated
400/// using `pool` and has not already been deallocated. This operator is
401/// supplied solely to allow the compiler to arrange for it to be called in
402/// case of an exception. Client code should not call it; use
403/// `bdlma::ConcurrentFixedPool::deleteObject()` instead.
404inline
405void operator delete(void *address,
406 BloombergLP::bdlma::ConcurrentFixedPool& pool);
407
408
409namespace bdlma {
410
411// ============================================================================
412// INLINE DEFINITIONS
413// ============================================================================
414
415 // -------------------------
416 // class ConcurrentFixedPool
417 // -------------------------
418
419// MANIPULATORS
420template<class TYPE>
421inline
422void ConcurrentFixedPool::deleteObject(const TYPE *object)
423{
425}
426
427template<class TYPE>
428inline
430{
432}
433
434inline
436{
437 d_backoffLevel = backoffLevel;
438}
439
440// ACCESSORS
441inline
443{
444 Node * node = const_cast<Node *>(d_nodes[index]);
445
446 BSLS_ASSERT(node);
447 return (char *)node + d_dataOffset;
448}
449
450inline
452{
453 return d_backoffLevel;
454}
455
456inline
458{
459 const Node * const node = (const Node *)(void *)
460 ((char *)address - d_dataOffset);
461 return (node->d_next & d_sizeMask) - 1;
462}
463
464inline
466{
467 return d_objectSize;
468}
469
470inline
472{
473 return static_cast<int>(d_nodes.size());
474}
475
476// Aspects
477
478inline
480{
481 return d_nodePool.allocator();
482}
483
484} // close package namespace
485
486
487inline
488void *operator new(bsl::size_t size,
489 BloombergLP::bdlma::ConcurrentFixedPool& pool)
490{
491 using namespace BloombergLP;
492 BSLS_ASSERT((int) size <= pool.objectSize()
495
496 (void)size; // suppress "unused parameter" warnings
497 return pool.allocate();
498}
499
500inline
501void operator delete(void *address,
502 BloombergLP::bdlma::ConcurrentFixedPool& pool)
503{
504 pool.deallocate(address);
505}
506
507#endif
508
509// ----------------------------------------------------------------------------
510// Copyright 2015 Bloomberg Finance L.P.
511//
512// Licensed under the Apache License, Version 2.0 (the "License");
513// you may not use this file except in compliance with the License.
514// You may obtain a copy of the License at
515//
516// http://www.apache.org/licenses/LICENSE-2.0
517//
518// Unless required by applicable law or agreed to in writing, software
519// distributed under the License is distributed on an "AS IS" BASIS,
520// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
521// See the License for the specific language governing permissions and
522// limitations under the License.
523// ----------------------------- END-OF-FILE ----------------------------------
524
525/** @} */
526/** @} */
527/** @} */
Definition bdlma_concurrentfixedpool.h:209
void * addressFromIndex(int index) const
Definition bdlma_concurrentfixedpool.h:442
int reserveCapacity(int numObjects)
bslma::Allocator * allocator() const
Definition bdlma_concurrentfixedpool.h:479
int objectSize() const
Definition bdlma_concurrentfixedpool.h:465
int backoffLevel() const
Definition bdlma_concurrentfixedpool.h:451
int indexFromAddress(void *address) const
Definition bdlma_concurrentfixedpool.h:457
void deleteObject(const TYPE *object)
Definition bdlma_concurrentfixedpool.h:422
int poolSize() const
Return the maximum size of this pool.
Definition bdlma_concurrentfixedpool.h:471
void setBackoffLevel(int backoffLevel)
Definition bdlma_concurrentfixedpool.h:435
~ConcurrentFixedPool()
Destroy this object and release all associated memory.
void deallocate(void *address)
void deleteObjectRaw(const TYPE *object)
Definition bdlma_concurrentfixedpool.h:429
ConcurrentFixedPool(int objectSize, int poolSize, bslma::Allocator *basicAllocator=0)
Definition bdlma_pool.h:335
bslma::Allocator * allocator() const
Definition bdlma_pool.h:621
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements in this vector.
Definition bslstl_vector.h:2664
Definition bslstl_vector.h:1025
Definition bslma_allocator.h:457
Definition bslmt_mutex.h:315
Definition bsls_atomic.h:743
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
Definition bdlma_alignedallocator.h:276
Definition bdlma_concurrentfixedpool.h:189
unsigned d_next
Definition bdlma_concurrentfixedpool.h:192
static void deleteObject(const TYPE *object, ALLOCATOR *allocator)
Definition bslma_deleterhelper.h:196
static void deleteObjectRaw(const TYPE *object, ALLOCATOR *allocator)
Definition bslma_deleterhelper.h:217
static int calculateAlignmentFromSize(std::size_t size)
Definition bsls_alignmentutil.h:344