BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlcc_objectcatalog.h
Go to the documentation of this file.
1/// @file bdlcc_objectcatalog.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_objectcatalog.h -*-C++-*-
8#ifndef INCLUDED_BDLCC_OBJECTCATALOG
9#define INCLUDED_BDLCC_OBJECTCATALOG
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlcc_objectcatalog bdlcc_objectcatalog
15/// @brief Provide an efficient indexed, thread-safe object container.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlcc
19/// @{
20/// @addtogroup bdlcc_objectcatalog
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlcc_objectcatalog-purpose"> Purpose</a>
25/// * <a href="#bdlcc_objectcatalog-classes"> Classes </a>
26/// * <a href="#bdlcc_objectcatalog-description"> Description </a>
27/// * <a href="#bdlcc_objectcatalog-usage"> Usage </a>
28/// * <a href="#bdlcc_objectcatalog-example-1-catalog-usage"> Example 1: Catalog Usage </a>
29/// * <a href="#bdlcc_objectcatalog-example-2-iterator-usage"> Example 2: Iterator Usage </a>
30///
31/// # Purpose {#bdlcc_objectcatalog-purpose}
32/// Provide an efficient indexed, thread-safe object container.
33///
34/// # Classes {#bdlcc_objectcatalog-classes}
35///
36/// - bdlcc::ObjectCatalog: templatized, thread-safe, indexed object container
37/// - bdlcc::ObjectCatalogIter: thread-safe iterator for `bdlcc::ObjectCatalog`
38///
39/// # Description {#bdlcc_objectcatalog-description}
40/// This component provides a thread-safe and efficient templatized
41/// catalog of objects. A `bdlcc::ObjectCatalog` supports efficient insertion
42/// of objects through the `add` method, which returns a handle that can be used
43/// for further reference to the newly added element. An element can be
44/// accessed by providing its handle to the `find` function. Thread-safe design
45/// implies that the element is returned by value into an object buffer rather
46/// than by reference (see this package documentation for a discussion of
47/// thread-safe container design). Likewise, an element can be modified by
48/// providing its handle and a new value to the `replace` method. Finally, an
49/// element can be removed by passing its handle to the `remove` method; the
50/// handle is then no longer valid and subsequent calls to `find` or `remove`
51/// with this handle will return 0.
52///
53/// `bdlcc::ObjectCatalogIter` provides thread safe iteration through all the
54/// objects of an object catalog of parameterized `TYPE`. The order of the
55/// iteration is implementation defined. Thread safe iteration is provided by
56/// (read)locking the object catalog during the iterator's construction and
57/// unlocking it at the iterator's destruction. This guarantees that during the
58/// life time of an iterator, the object catalog can't be modified (however
59/// multiple threads can still concurrently read the object catalog).
60///
61/// Note that an object catalog has a maximum capacity of 2^23 items.
62///
63/// ## Usage {#bdlcc_objectcatalog-usage}
64///
65///
66/// This section illustrates intended use of this component.
67///
68/// ### Example 1: Catalog Usage {#bdlcc_objectcatalog-example-1-catalog-usage}
69///
70///
71/// Consider a client sending queries to a server asynchronously. When the
72/// response to a query arrives, the client needs to invoke the callback
73/// associated with that query. For good performance, the callback should be
74/// invoked as quickly as possible. One way to achieve this is as follows. The
75/// client creates a catalog for the functors associated with queries. It sends
76/// to the server the handle (obtained by passing the callback functor
77/// associated with the query to the `add` method of catalog), along with the
78/// query. The server does not interpret this handle in any way and sends it
79/// back to the client along with the computed query result. The client, upon
80/// receiving the response, gets the functor (associated with the query) back by
81/// passing the handle (contained in the response message) to the `find` method
82/// of catalog.
83///
84/// Assume the following declarations (we leave the implementations as
85/// undefined, as the definitions are largely irrelevant to this example):
86/// @code
87/// /// Class simulating the query.
88/// struct Query {
89/// };
90///
91/// /// Class simulating the result of a query.
92/// class QueryResult {
93/// };
94///
95/// /// Class encapsulating the request message. It encapsulates the
96/// /// actual query and the handle associated with the callback for the
97/// /// query.
98/// class RequestMsg
99/// {
100/// Query d_query;
101/// int d_handle;
102///
103/// public:
104/// /// Create a request message with the specified `query` and
105/// /// `handle`.
106/// RequestMsg(Query query, int handle)
107/// : d_query(query)
108/// , d_handle(handle)
109/// {
110/// }
111///
112/// /// Return the handle contained in this response message.
113/// int handle() const
114/// {
115/// return d_handle;
116/// }
117/// };
118///
119/// /// Class encapsulating the response message. It encapsulates the query
120/// /// result and the handle associated with the callback for the query.
121/// class ResponseMsg
122/// {
123/// int d_handle;
124///
125/// public:
126/// /// Set the "handle" contained in this response message to the
127/// /// specified `handle`.
128/// void setHandle(int handle)
129/// {
130/// d_handle = handle;
131/// }
132///
133/// /// Return the query result contained in this response message.
134/// QueryResult queryResult() const
135/// {
136/// return QueryResult();
137/// }
138///
139/// /// Return the handle contained in this response message.
140/// int handle() const
141/// {
142/// return d_handle;
143/// }
144/// };
145///
146/// /// Send the specified `msg` to the specified `peer`.
147/// void sendMessage(RequestMsg msg, RemoteAddress peer)
148/// {
149/// serverMutex.lock();
150/// peer->push(msg.handle());
151/// serverNotEmptyCondition.signal();
152/// serverMutex.unlock();
153/// }
154///
155/// /// Get the response from the specified `peer` into the specified `msg`.
156/// void recvMessage(ResponseMsg *msg, RemoteAddress peer)
157/// {
158/// serverMutex.lock();
159/// while (peer->empty()) {
160/// serverNotEmptyCondition.wait(&serverMutex);
161/// }
162/// msg->setHandle(peer->front());
163/// peer->pop();
164/// serverMutex.unlock();
165/// }
166///
167/// /// Set the specified `query` and `callBack` to the next `Query` and its
168/// /// associated functor (the functor to be called when the response to
169/// /// this `Query` comes in).
170/// void getQueryAndCallback(Query *query,
171/// bsl::function<void(QueryResult)> *callBack)
172/// {
173/// (void)query;
174/// *callBack = &queryCallBack;
175/// }
176/// @endcode
177/// Furthermore, let also the following variables be declared:
178/// @code
179/// RemoteAddress serverAddress; // address of remote server
180///
181/// /// Catalog of query callbacks, used by the client internally to keep
182/// /// track of callback functions across multiple queries. The invariant
183/// /// is that each element corresponds to a pending query (i.e., the
184/// /// callback function has not yet been or is in the process of being
185/// /// invoked).
186/// bdlcc::ObjectCatalog<bsl::function<void(QueryResult)> > catalog;
187/// @endcode
188/// Now we define functions that will be used in the thread entry functions:
189/// @code
190/// void testClientProcessQueryCpp()
191/// {
192/// int queriesToBeProcessed = NUM_QUERIES_TO_PROCESS;
193/// while (queriesToBeProcessed--) {
194/// Query query;
195/// bsl::function<void(QueryResult)> callBack;
196///
197/// // The following call blocks until a query becomes available.
198/// getQueryAndCallback(&query, &callBack);
199///
200/// // Register `callBack` in the object catalog.
201/// int handle = catalog.add(callBack);
202/// assert(handle);
203///
204/// // Send query to server in the form of a `RequestMsg`.
205/// RequestMsg msg(query, handle);
206/// sendMessage(msg, serverAddress);
207/// }
208/// }
209///
210/// void testClientProcessResponseCpp()
211/// {
212/// int queriesToBeProcessed = NUM_QUERIES_TO_PROCESS;
213/// while (queriesToBeProcessed--) {
214/// // The following call blocks until some response is available in
215/// // the form of a `ResponseMsg`.
216///
217/// ResponseMsg msg;
218/// recvMessage(&msg, serverAddress);
219/// int handle = msg.handle();
220/// QueryResult result = msg.queryResult();
221///
222/// // Process query `result` by applying registered `callBack` to it.
223/// // The `callBack` function is retrieved from the `catalog` using
224/// // the given `handle`.
225///
226/// bsl::function<void(QueryResult)> callBack;
227/// assert(0 == catalog.find(handle, &callBack));
228/// callBack(result);
229///
230/// // Finally, remove the no-longer-needed `callBack` from the
231/// // `catalog`. Assert so that `catalog` may not grow unbounded if
232/// // remove fails.
233///
234/// assert(0 == catalog.remove(handle));
235/// }
236/// }
237/// @endcode
238/// In some thread, the client executes the following code.
239/// @code
240/// extern "C" void *testClientProcessQuery(void *)
241/// {
242/// testClientProcessQueryCpp();
243/// return 0;
244/// }
245/// @endcode
246/// In some other thread, the client executes the following code.
247/// @code
248/// extern "C" void *testClientProcessResponse(void *)
249/// {
250/// testClientProcessResponseCpp();
251/// return 0;
252/// }
253/// @endcode
254///
255/// ### Example 2: Iterator Usage {#bdlcc_objectcatalog-example-2-iterator-usage}
256///
257///
258/// The following code fragment shows how to use bdlcc::ObjectCatalogIter to
259/// iterate through all the objects of `catalog` (a catalog of objects of type
260/// `MyType`).
261/// @code
262/// void use(bsl::function<void(QueryResult)> object)
263/// {
264/// (void)object;
265/// }
266/// @endcode
267/// Now iterate through the `catalog`:
268/// @code
269/// for (bdlcc::ObjectCatalogIter<MyType> it(catalog); it; ++it) {
270/// bsl::pair<int, MyType> p = it(); // p.first contains the handle and
271/// // p.second contains the object
272/// use(p.second); // the function 'use' uses the
273/// // object in some way
274/// }
275/// // 'it' is now destroyed out of the scope, releasing the lock.
276/// @endcode
277/// Note that the associated catalog is (read)locked when the iterator is
278/// constructed and is unlocked only when the iterator is destroyed. This means
279/// that until the iterator is destroyed, all the threads trying to modify the
280/// catalog will remain blocked (even though multiple threads can concurrently
281/// read the object catalog). So clients must make sure to destroy their
282/// iterators after they are done using them. One easy way is to use the
283/// `for (bdlcc::ObjectCatalogIter<MyType> it(catalog); ...` as above.
284/// @}
285/** @} */
286/** @} */
287
288/** @addtogroup bdl
289 * @{
290 */
291/** @addtogroup bdlcc
292 * @{
293 */
294/** @addtogroup bdlcc_objectcatalog
295 * @{
296 */
297
298#include <bdlscm_version.h>
299
300#include <bslmt_rwmutex.h>
301#include <bslmt_readlockguard.h>
302#include <bslmt_writelockguard.h>
303
304#include <bdlma_pool.h>
305
307
308#include <bslma_allocator.h>
309#include <bslma_default.h>
311
312#include <bslmf_assert.h>
313#include <bslmf_movableref.h>
315
316#include <bsls_alignmentutil.h>
317#include <bsls_assert.h>
318#include <bsls_atomic.h>
320#include <bsls_keyword.h>
321#include <bsls_libraryfeatures.h>
322#include <bsls_objectbuffer.h>
323#include <bsls_platform.h>
324#include <bsls_review.h>
325
326#include <bsl_utility.h>
327#include <bsl_vector.h>
328
329#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
330#include <bslalg_typetraits.h>
331#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
332
333#include <vector>
334
335
336namespace bdlcc {
337
338template <class TYPE>
339class ObjectCatalog_AutoCleanup;
340template <class TYPE>
341class ObjectCatalogIter;
342template <class TYPE>
343class ObjectCatalog;
344
345 // =====================================
346 // local class ObjectCatalog_AutoCleanup
347 // =====================================
348
349/// This class provides a specialized proctor object that, upon destruction and
350/// unless the `release` method is called (1) removes a managed node from the
351/// `ObjectCatalog`, and (2) deallocates all associated memory as necessary.
352///
353/// See @ref bdlcc_objectcatalog
354template <class TYPE>
356
357 ObjectCatalog<TYPE> *d_catalog_p; // temporarily managed catalog
359 *d_node_p; // temporarily managed node
360 bool d_deallocateFlag; // how to return the managed node
361
362 private:
363 // NOT IMPLEMENTED
368
369 public:
370 // CREATORS
371
372 /// Create a proctor to manage the specified `catalog`.
374
375 /// Remove a managed node from the `ObjectCatalog` (by returning it to
376 /// the catalog's free list or node pool, as specified in `manageNode`),
377 /// deallocate all associated memory, and destroy this object.
379
380 // MANIPULATORS
381
382 /// Release from management the catalog node, if any, currently managed
383 /// by this object and begin managing the specified catalog `node`. The
384 /// specified `deallocateFlag` tells the destructor how to dispose of
385 /// `node` if `node` is managed during the destruction of this object.
386 void manageNode(typename ObjectCatalog<TYPE>::Node *node,
387 bool deallocateFlag);
388
389 /// Release from management the catalog node, if any, currently managed
390 /// by this object, if any.
391 void releaseNode();
392
393 /// Release from management all resources currently managed by this
394 /// object, if any.
395 void release();
396};
397
398 // ===================
399 // class ObjectCatalog
400 // ===================
401
402/// This class defines an efficient indexed object catalog of `TYPE`
403/// objects. This container is *exception* *neutral* with no guarantee of
404/// rollback: if an exception is thrown during the invocation of a method on
405/// a pre-existing instance, the object is left in a valid but undefined
406/// state. In no event is memory leaked or a mutex left in a locked state.
407///
408/// See @ref bdlcc_objectcatalog
409template <class TYPE>
411
412 // PRIVATE TYPES
414
415 enum {
416 // Masks used for breaking up a handle. Note: a handle (of type int)
417 // is always 4 bytes, even on 64 bit modes.
418
419 k_INDEX_MASK = 0x007fffff,
420 k_BUSY_INDICATOR = 0x00800000,
421 k_GENERATION_INC = 0x01000000,
422 k_GENERATION_MASK = 0xff000000
423 };
424
425 struct Node {
426 // PUBLIC DATA
427 typedef union {
428 // PUBLIC DATA
430
431 Node *d_next_p; // when free, pointer
432 // to next free node
433 } Payload;
434 Payload d_payload;
435 int d_handle;
436 };
437
438 // DATA
439 bsl::vector<Node *> d_nodes;
440 bdlma::Pool d_nodePool;
441 Node *d_nextFreeNode_p;
442 bsls::AtomicInt d_length;
443 mutable bslmt::RWMutex d_lock;
444
445 private:
446 // NOT IMPLEMENTED
449
450 // FRIENDS
451 friend class ObjectCatalog_AutoCleanup<TYPE>;
452 friend class ObjectCatalogIter<TYPE>;
453
454 private:
455 // PRIVATE CLASS METHODS
456
457 /// Return a pointer to the `d_value` field of the specified `node`.
458 ///
459 /// \pre The behavior is undefined unless `0 != node` and
460 /// `node->d_payload.d_value` is initialized to a `TYPE` object.
461 static TYPE *getNodeValue(Node *node);
462
463 // PRIVATE MANIPULATORS
464
465 /// Add the specified `node` to the free node list. Destruction of the
466 /// object held in the node must be handled by the `remove` function
467 /// directly. (This is because `freeNode` is also used in the
468 /// `ObjectCatalog_AutoCleanup` guard, but there it should not invoke
469 /// the object's destructor.)
470 void freeNode(Node *node);
471
472 /// Remove all objects that are currently held in this catalog and
473 /// optionally load into the optionally specified `buffer` the removed
474 /// objects.
475 template <class VECTOR>
476 void removeAllImp(VECTOR *buffer);
477
478 // PRIVATE ACCESSORS
479
480 /// Return a pointer to the node with the specified `handle`, or 0 if
481 /// not found.
482 Node *findNode(int handle) const;
483
484 public:
485 // TRAITS
487
488 // CREATORS
489
490 /// Create an empty object catalog, using the optionally specified
491 /// `allocator` to supply any memory.
492 explicit
494
495 /// Destroy this object catalog.
497
498 // MANIPULATORS
499
500 /// Add the value of the specified `object` to this catalog and return a
501 /// non-zero integer handle that may be used to refer to the object in future calls to this catalog.
502 ///
503 /// \pre The behavior is undefined if the
504 /// catalog was full.
505 int add(TYPE const& object);
506
507 /// Add the value of the specified `object` to this catalog and return a
508 /// non-zero integer handle that may be used to refer to the object in
509 /// future calls to this catalog, leaving `object` in an unspecified but valid state.
510 ///
511 /// \pre The behavior is undefined if the catalog was full.
513
514 /// Optionally load into the optionally specified `valueBuffer` the
515 /// value of the object having the specified `handle` and remove it from
516 /// this catalog. Return zero on success, and a non-zero value if the `handle` is not contained in this catalog.
517 ///
518 /// \note Note that `valueBuffer`
519 /// is assigned into, and thus must point to a valid `TYPE` instance.
520 int remove(int handle, TYPE *valueBuffer = 0);
521
522 /// Remove all objects that are currently held in this catalog and
523 /// optionally load into the optionally specified `buffer` the removed
524 /// objects.
525 void removeAll();
527 void removeAll(std::vector<TYPE> *buffer);
528#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
529 void removeAll(std::pmr::vector<TYPE> *buffer);
530#endif
531
532 /// Replace the object having the specified `handle` with the specified
533 /// `newObject`. Return 0 on success, and a non-zero value if the
534 /// handle is not contained in this catalog.
535 int replace(int handle, const TYPE& newObject);
536
537 /// Replace the object having the specified `handle` with the specified
538 /// `newObject`, leaving `newObject` in an unspecified but valid state.
539 /// Return 0 on success, and a non-zero value if the handle is not
540 /// contained in this catalog.
541 int replace(int handle, bslmf::MovableRef<TYPE> newObject);
542
543 // ACCESSORS
544
545 /// Return the allocator used by this object.
547
548 /// Locate the object having the specified `handle` and optionally load
549 /// its value into the optionally specified `valueBuffer`. Return zero
550 /// on success, and a non-zero value if the `handle` is not contained in this catalog.
551 ///
552 /// \note Note that `valueBuffer` is assigned into, and thus
553 /// must point to a valid `TYPE` instance.
554 /// \note Note that the overload with
555 /// `valueBuffer` passed is not supported unless `TYPE` has a copy
556 /// constructor.
557 int find(int handle) const;
558 int find(int handle, TYPE *valueBuffer) const;
559
560 /// Return `true` if the catalog contains an item that compares equal to
561 /// the specified `object` and `false` otherwise.
562 bool isMember(const TYPE& object) const;
563
564 /// Return a "snapshot" of the number of items currently contained in
565 /// this catalog.
566 int length() const;
567
568 /// Return a `const` reference to the object having the specified `handle`.
569 ///
570 /// \pre The behavior is undefined unless `handle` is contained in
571 /// this catalog.
572 ///
573 /// This method is *DEPRECATED* because it is not thread-safe. Use
574 /// `find`, `isMember`, or access the object through an iterator.
575 BSLS_DEPRECATE_FEATURE("bde", "ObjectCataloog::value(handle)",
576 "use 'ObjectCatalogIter::value()' instead")
577 const TYPE& value(int handle) const;
578
579 // FOR TESTING PURPOSES ONLY
580
581 /// Verify that this catalog is in a consistent state. This function is
582 /// introduced for testing purposes only.
583 void verifyState() const;
584};
585
586 // =======================
587 // class ObjectCatalogIter
588 // =======================
589
590/// Provide thread safe iteration through all the objects of an object
591/// catalog of parameterized `TYPE`. The order of the iteration is
592/// implementation defined. An iterator is *valid* if it is associated with
593/// an object in the catalog, otherwise it is *invalid*. Thread-safe
594/// iteration is provided by (read)locking the object catalog during the
595/// iterator's construction and unlocking it at the iterator's destruction.
596/// This guarantees that during the life time of an iterator, the object
597/// catalog can't be modified (nevertheless, multiple threads can
598/// concurrently read the object catalog).
599///
600/// See @ref bdlcc_objectcatalog
601template <class TYPE>
603
604 const ObjectCatalog<TYPE> *d_catalog_p;
605 bsl::ptrdiff_t d_index;
606
607 private:
608 // NOT IMPLEMENTED
610 ObjectCatalogIter& operator=(const ObjectCatalogIter&)
612 bool operator==(const ObjectCatalogIter&) const BSLS_KEYWORD_DELETED;
613 bool operator!=(const ObjectCatalogIter&) const BSLS_KEYWORD_DELETED;
614 template <class BDE_OTHER_TYPE>
615 bool operator==(
617 template <class BDE_OTHER_TYPE>
618 bool operator!=(
620
621 public:
622 // CREATORS
623
624 /// Create an iterator for the specified `catalog` and associate it with
625 /// the first member of the `catalog`. If the `catalog` is empty then
626 /// the iterator is initialized to be invalid. The `catalog` is locked
627 /// for read for the duration of iterator's life.
628 explicit ObjectCatalogIter(const ObjectCatalog<TYPE>& catalog);
629
630 /// Create an iterator for the specified `catalog` and associate it with
631 /// the member of the `catalog` associated with the specified `handle`.
632 /// If the `catalog` is empty or if `handle` is invalid, then the
633 /// iterator is initialized to be invalid. The `catalog` is locked for
634 /// read for the duration of iterator's life.
635 ObjectCatalogIter(const ObjectCatalog<TYPE>& catalog, int handle);
636
637 /// Destroy this iterator and unlock the catalog associated with it.
639
640 // MANIPULATORS
641
642 /// Advance this iterator to refer to the next object of the associated
643 /// catalog; if there is no next object in the associated catalog, then this iterator becomes *invalid*.
644 ///
645 /// \pre The behavior is undefined unless this iterator is valid.
646 ///
647 /// \note Note that the order of the iteration is not
648 /// specified.
649 void operator++();
650
651 // ACCESSORS
652
653 /// Return non-zero if the iterator is *valid*, and 0 otherwise.
654 operator const void *() const;
655
656 /// Return a pair containing the handle (as the first element of the
657 /// pair) and the object (as the second element of the pair) associated with this iterator.
658 ///
659 /// \pre The behavior is undefined unless the iterator
660 /// is *valid*.
661 bsl::pair<int, TYPE> operator()() const;
662
663 /// Return the handle referred to by the iterator.
664 ///
665 /// \pre The behavior is undefined unless the iterator is *valid*.
666 int handle() const;
667
668 /// Return a `const` reference to the value referred to by the iterator.
669 ///
670 /// \pre The behavior is undefined unless the iterator is *valid*.
671 const TYPE& value() const;
672};
673
674// ----------------------------------------------------------------------------
675// INLINE DEFINITIONS
676// ----------------------------------------------------------------------------
677
678 // -------------------------------------
679 // local class ObjectCatalog_AutoCleanup
680 // -------------------------------------
681
682// CREATORS
683template <class TYPE>
685 ObjectCatalog<TYPE> *catalog)
686: d_catalog_p(catalog)
687, d_node_p(0)
688, d_deallocateFlag(false)
689{
690}
691
692template <class TYPE>
694{
695 if (d_catalog_p && d_node_p) {
696 if (d_deallocateFlag) {
697 // Return node to the pool.
698
699 d_catalog_p->d_nodePool.deallocate(d_node_p);
700 } else {
701 // Return node to the catalog's free list.
702
703 d_catalog_p->freeNode(d_node_p);
704 }
705 }
706}
707
708// MANIPULATORS
709template <class TYPE>
711 typename ObjectCatalog<TYPE>::Node *node,
712 bool deallocateFlag)
713{
714 d_node_p = node;
715 d_deallocateFlag = deallocateFlag;
716}
717
718template <class TYPE>
720{
721 d_node_p = 0;
722}
723
724template <class TYPE>
726{
727 d_catalog_p = 0;
728 d_node_p = 0;
729}
730
731 // -------------------
732 // class ObjectCatalog
733 // -------------------
734
735// PRIVATE CLASS METHODS
736template <class TYPE>
737inline
739 typename ObjectCatalog<TYPE>::Node *node)
740{
741 BSLS_ASSERT(node);
742
743 return node->d_payload.d_value.address();
744}
745
746// PRIVATE MANIPULATORS
747template <class TYPE>
748inline
749void ObjectCatalog<TYPE>::freeNode(typename ObjectCatalog<TYPE>::Node *node)
750{
751 BSLS_ASSERT(node->d_handle & k_BUSY_INDICATOR);
752
753 node->d_handle += k_GENERATION_INC;
754 node->d_handle &= ~k_BUSY_INDICATOR;
755
756 node->d_payload.d_next_p = d_nextFreeNode_p;
757 d_nextFreeNode_p = node;
758}
759
760
761template <class TYPE>
762template <class VECTOR>
763void ObjectCatalog<TYPE>::removeAllImp(VECTOR *buffer)
764{
765 static const bool isVector =
766 bsl::is_same<bsl::vector<TYPE>, VECTOR>::value
767#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
768 || bsl::is_same<std::pmr::vector<TYPE>, VECTOR>::value
769#endif
770 || bsl::is_same<std::vector<TYPE>, VECTOR>::value;
771 BSLMF_ASSERT(isVector);
772
773 typedef typename bsl::vector<Node *>::iterator VIt;
774
776
777 for (VIt it = d_nodes.begin(); it != d_nodes.end(); ++it) {
778 if ((*it)->d_handle & k_BUSY_INDICATOR) {
779 TYPE *value = getNodeValue(*it);
780
781 if (buffer) {
782 buffer->push_back(bslmf::MovableRefUtil::move(*value));
783 }
784 value->~TYPE();
785 }
786 }
787
788 // Even though we get rid of the container of 'Node *' without returning
789 // the nodes to the pool prior, the release of the pool immediately after
790 // will properly (and efficiently) dispose of those nodes without leaking
791 // memory.
792
793 d_nodes.clear();
794 d_nodePool.release();
795 d_nextFreeNode_p = 0;
796 d_length = 0;
797}
798
799// PRIVATE ACCESSORS
800template <class TYPE>
801inline
802typename ObjectCatalog<TYPE>::Node *
803ObjectCatalog<TYPE>::findNode(int handle) const
804{
805 int index = handle & k_INDEX_MASK;
806 // if (d_nodes.size() < index || !(handle & k_BUSY_INDICATOR)) return 0;
807
808 if (0 > index ||
809 index >= (int)d_nodes.size() ||
810 !(handle & k_BUSY_INDICATOR)) {
811 return 0; // RETURN
812 }
813
814 Node *node = d_nodes[index];
815
816 return node->d_handle == handle ? node : 0;
817}
818
819// CREATORS
820template <class TYPE>
821inline
823: d_nodes(allocator)
824, d_nodePool(sizeof(Node), allocator)
825, d_nextFreeNode_p(0)
826, d_length(0)
827{
828}
829
830template <class TYPE>
831inline
833{
834 removeAll();
835}
836
837// MANIPULATORS
838template <class TYPE>
839int ObjectCatalog<TYPE>::add(const TYPE& object)
840{
841 int handle;
844 Node *node;
845
846 if (d_nextFreeNode_p) {
847 node = d_nextFreeNode_p;
848 d_nextFreeNode_p = node->d_payload.d_next_p;
849
850 proctor.manageNode(node, false);
851 // Destruction of this proctor will put node back onto the free list.
852 } else {
853 // If 'd_nodes' grows as big as the flags used to indicate BUSY and
854 // generations, then the handle will be all mixed up!
855
856 BSLS_REVIEW_OPT(d_nodes.size() < k_BUSY_INDICATOR);
857
858 node = static_cast<Node *>(d_nodePool.allocate());
859 proctor.manageNode(node, true);
860 // Destruction of this proctor will deallocate node.
861
862 d_nodes.push_back(node);
863 node->d_handle = static_cast<int>(d_nodes.size()) - 1;
864 proctor.manageNode(node, false);
865 // Destruction of this proctor will put node back onto the free list,
866 // which is now OK since the 'push_back' succeeded without throwing.
867 }
868
869 node->d_handle |= k_BUSY_INDICATOR;
870 handle = node->d_handle;
871
872 // We need to use the copyConstruct logic to pass the allocator through.
873
875 getNodeValue(node), object, d_nodes.get_allocator().mechanism());
876
877 // If the copy constructor throws, the proctor will properly put the node
878 // back onto the free list. Otherwise, the proctor should do nothing.
879
880 proctor.release();
881
882 ++d_length;
883 return handle;
884}
885
886template <class TYPE>
888{
889 TYPE& local = object;
890
891 int handle;
894 Node *node;
895
896 if (d_nextFreeNode_p) {
897 node = d_nextFreeNode_p;
898 d_nextFreeNode_p = node->d_payload.d_next_p;
899
900 proctor.manageNode(node, false);
901 // Destruction of this proctor will put node back onto the free list.
902 } else {
903 // If 'd_nodes' grows as big as the flags used to indicate BUSY and
904 // generations, then the handle will be all mixed up!
905
906 BSLS_REVIEW_OPT(d_nodes.size() < k_BUSY_INDICATOR);
907
908 node = static_cast<Node *>(d_nodePool.allocate());
909 proctor.manageNode(node, true);
910 // Destruction of this proctor will deallocate node.
911
912 d_nodes.push_back(node);
913 node->d_handle = static_cast<int>(d_nodes.size()) - 1;
914 proctor.manageNode(node, false);
915 // Destruction of this proctor will put node back onto the free list,
916 // which is now OK since the 'push_back' succeeded without throwing.
917 }
918
919 node->d_handle |= k_BUSY_INDICATOR;
920 handle = node->d_handle;
921
922 // We need to use the moveConstruct logic to pass the allocator through.
923
925 getNodeValue(node), local, d_nodes.get_allocator().mechanism());
926
927 // If the copy constructor throws, the proctor will properly put the node
928 // back onto the free list. Otherwise, the proctor should do nothing.
929
930 proctor.release();
931
932 ++d_length;
933 return handle;
934}
935
936template <class TYPE>
937inline
938int ObjectCatalog<TYPE>::remove(int handle, TYPE *valueBuffer)
939{
941
942 Node *node = findNode(handle);
943
944 if (!node) {
945 return -1; // RETURN
946 }
947
948 TYPE *value = getNodeValue(node);
949
950 if (valueBuffer) {
951 *valueBuffer = bslmf::MovableRefUtil::move(*value);
952 }
953
954 value->~TYPE();
955 freeNode(node);
956
957 --d_length;
958 return 0;
959}
960
961template <class TYPE>
962inline
964{
965 removeAllImp(static_cast<bsl::vector<TYPE> *>(0));
966}
967
968template <class TYPE>
969inline
971{
972 removeAllImp(buffer);
973}
974
975template <class TYPE>
976inline
977void ObjectCatalog<TYPE>::removeAll(std::vector<TYPE> *buffer)
978{
979 removeAllImp(buffer);
980}
981
982#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
983template <class TYPE>
984inline
985void ObjectCatalog<TYPE>::removeAll(std::pmr::vector<TYPE> *buffer)
986{
987 removeAllImp(buffer);
988}
989#endif
990
991template <class TYPE>
992int ObjectCatalog<TYPE>::replace(int handle, const TYPE& newObject)
993{
995
996 Node *node = findNode(handle);
997
998 if (!node) {
999 return -1; // RETURN
1000 }
1001
1002 TYPE *value = getNodeValue(node);
1003
1004 value->~TYPE();
1005
1006 // We need to use the copyConstruct logic to pass the allocator through.
1007
1009 value, newObject, d_nodes.get_allocator().mechanism());
1010
1011 return 0;
1012}
1013
1014template <class TYPE>
1016{
1017 TYPE& local = newObject;
1018
1020
1021 Node *node = findNode(handle);
1022
1023 if (!node) {
1024 return -1; // RETURN
1025 }
1026
1027 TYPE *value = getNodeValue(node);
1028
1029 value->~TYPE();
1030
1031 // We need to use the moveConstruct logic to pass the allocator through.
1032
1034 value, local, d_nodes.get_allocator().mechanism());
1035
1036 return 0;
1037}
1038
1039// ACCESSORS
1040template <class TYPE>
1041inline
1043{
1044 return d_nodePool.allocator();
1045}
1046
1047template <class TYPE>
1048inline
1049int ObjectCatalog<TYPE>::find(int handle) const
1050{
1052
1053 return 0 == findNode(handle) ? -1 : 0;
1054}
1055
1056template <class TYPE>
1057inline
1058int ObjectCatalog<TYPE>::find(int handle, TYPE *valueBuffer) const
1059{
1061
1062 Node *node = findNode(handle);
1063
1064 if (!node) {
1065 return -1; // RETURN
1066 }
1067
1068 *valueBuffer = *getNodeValue(node);
1069
1070 return 0;
1071}
1072
1073template <class TYPE>
1074bool ObjectCatalog<TYPE>::isMember(const TYPE& object) const
1075{
1076 for (Iter it(*this); it; ++it) {
1077 if (it.value() == object) {
1078 return true; // RETURN
1079 }
1080 }
1081
1082 return false;
1083}
1084
1085template <class TYPE>
1086inline
1088{
1089 return d_length;
1090}
1091
1092template <class TYPE>
1093inline
1094const TYPE& ObjectCatalog<TYPE>::value(int handle) const
1095{
1097
1098 Node *node = findNode(handle);
1099
1100 BSLS_ASSERT(node);
1101
1102 return *getNodeValue(node);
1103}
1104
1105template <class TYPE>
1107{
1109
1110 BSLS_ASSERT( 0 <= d_length);
1111 BSLS_ASSERT(d_nodes.size() >= static_cast<unsigned>(d_length));
1112
1113 unsigned numBusy = 0, numFree = 0;
1114 for (unsigned ii = 0; ii < d_nodes.size(); ++ii) {
1115 const int handle = d_nodes[ii]->d_handle;
1116 BSLS_ASSERT((handle & k_INDEX_MASK) == ii);
1117 handle & k_BUSY_INDICATOR ? ++numBusy
1118 : ++numFree;
1119 }
1120 BSLS_ASSERT( numBusy == static_cast<unsigned>(d_length));
1121 BSLS_ASSERT(numFree + numBusy == d_nodes.size());
1122
1123 for (const Node *p = d_nextFreeNode_p; p; p = p->d_payload.d_next_p) {
1124 BSLS_ASSERT(!(p->d_handle & k_BUSY_INDICATOR));
1125 --numFree;
1126 }
1127 BSLS_ASSERT(0 == numFree);
1128}
1129
1130 // -----------------
1131 // ObjectCatalogIter
1132 // -----------------
1133
1134// CREATORS
1135template <class TYPE>
1136inline
1138: d_catalog_p(&catalog)
1139, d_index(-1)
1140{
1141 d_catalog_p->d_lock.lockRead();
1142 operator++();
1143}
1144
1145template <class TYPE>
1146inline
1148 int handle)
1149: d_catalog_p(&catalog)
1150{
1151 typedef ObjectCatalog<TYPE> Catalog;
1152 typedef typename Catalog::Node Node;
1153
1154 d_catalog_p->d_lock.lockRead();
1155
1156 Node *node = d_catalog_p->findNode(handle);
1157 d_index = node ? node->d_handle & Catalog::k_INDEX_MASK
1158 : bsl::ssize(d_catalog_p->d_nodes);
1159}
1160
1161template <class TYPE>
1162inline
1164{
1165 d_catalog_p->d_lock.unlock();
1166}
1167
1168// MANIPULATORS
1169template <class TYPE>
1171{
1172 ++d_index;
1173 while ((unsigned)d_index < d_catalog_p->d_nodes.size() &&
1174 !(d_catalog_p->d_nodes[d_index]->d_handle &
1176 ++d_index;
1177 }
1178}
1179
1180template <class TYPE>
1181inline
1183{
1184 BSLS_ASSERT(static_cast<unsigned>(d_index) < d_catalog_p->d_nodes.size());
1185
1186 return d_catalog_p->d_nodes[d_index]->d_handle;
1187}
1188
1189template <class TYPE>
1190inline
1192{
1193 BSLS_ASSERT(static_cast<unsigned>(d_index) < d_catalog_p->d_nodes.size());
1194
1195 return *ObjectCatalog<TYPE>::getNodeValue(d_catalog_p->d_nodes[d_index]);
1196}
1197
1198} // close package namespace
1199
1200// ACCESSORS
1201template <class TYPE>
1202inline
1204{
1205 return static_cast<unsigned>(d_index) < d_catalog_p->d_nodes.size()
1206 ? this
1207 : 0;
1208}
1209
1210namespace bdlcc {
1211
1212template <class TYPE>
1213inline
1215{
1216 typedef ObjectCatalog<TYPE> Catalog;
1217
1218 typename Catalog::Node *node = d_catalog_p->d_nodes[d_index];
1219
1220 return bsl::pair<int, TYPE>(node->d_handle, *Catalog::getNodeValue(node));
1221}
1222
1223} // close package namespace
1224
1225
1226#endif
1227
1228// ----------------------------------------------------------------------------
1229// Copyright 2015 Bloomberg Finance L.P.
1230//
1231// Licensed under the Apache License, Version 2.0 (the "License");
1232// you may not use this file except in compliance with the License.
1233// You may obtain a copy of the License at
1234//
1235// http://www.apache.org/licenses/LICENSE-2.0
1236//
1237// Unless required by applicable law or agreed to in writing, software
1238// distributed under the License is distributed on an "AS IS" BASIS,
1239// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1240// See the License for the specific language governing permissions and
1241// limitations under the License.
1242// ----------------------------- END-OF-FILE ----------------------------------
1243
1244/** @} */
1245/** @} */
1246/** @} */
Definition bdlcc_objectcatalog.h:602
~ObjectCatalogIter()
Destroy this iterator and unlock the catalog associated with it.
Definition bdlcc_objectcatalog.h:1163
const TYPE & value() const
Definition bdlcc_objectcatalog.h:1191
void operator++()
Definition bdlcc_objectcatalog.h:1170
int handle() const
Definition bdlcc_objectcatalog.h:1182
bsl::pair< int, TYPE > operator()() const
Definition bdlcc_objectcatalog.h:1214
Definition bdlcc_objectcatalog.h:355
void manageNode(typename ObjectCatalog< TYPE >::Node *node, bool deallocateFlag)
Definition bdlcc_objectcatalog.h:710
void releaseNode()
Definition bdlcc_objectcatalog.h:719
~ObjectCatalog_AutoCleanup()
Definition bdlcc_objectcatalog.h:693
void release()
Definition bdlcc_objectcatalog.h:725
Definition bdlcc_objectcatalog.h:410
int add(TYPE const &object)
Definition bdlcc_objectcatalog.h:839
int replace(int handle, const TYPE &newObject)
Definition bdlcc_objectcatalog.h:992
int find(int handle) const
Definition bdlcc_objectcatalog.h:1049
void removeAll()
Definition bdlcc_objectcatalog.h:963
ObjectCatalog(bslma::Allocator *allocator=0)
Definition bdlcc_objectcatalog.h:822
int remove(int handle, TYPE *valueBuffer=0)
Definition bdlcc_objectcatalog.h:938
int replace(int handle, bslmf::MovableRef< TYPE > newObject)
Definition bdlcc_objectcatalog.h:1015
void removeAll(std::vector< TYPE > *buffer)
Definition bdlcc_objectcatalog.h:977
BSLS_DEPRECATE_FEATURE("bde", "ObjectCataloog::value(handle)", "use 'ObjectCatalogIter::value()' instead") const TYPE &value(int handle) const
bslma::Allocator * allocator() const
Return the allocator used by this object.
Definition bdlcc_objectcatalog.h:1042
int add(bslmf::MovableRef< TYPE > object)
Definition bdlcc_objectcatalog.h:887
BSLMF_NESTED_TRAIT_DECLARATION(ObjectCatalog, bslma::UsesBslmaAllocator)
void removeAll(bsl::vector< TYPE > *buffer)
Definition bdlcc_objectcatalog.h:970
int length() const
Definition bdlcc_objectcatalog.h:1087
~ObjectCatalog()
Destroy this object catalog.
Definition bdlcc_objectcatalog.h:832
bool isMember(const TYPE &object) const
Definition bdlcc_objectcatalog.h:1074
void verifyState() const
Definition bdlcc_objectcatalog.h:1106
int find(int handle, TYPE *valueBuffer) const
Definition bdlcc_objectcatalog.h:1058
Definition bdlma_pool.h:338
bslma::Allocator * allocator() const
Definition bdlma_pool.h:639
void * allocate()
Definition bdlma_pool.h:580
void release()
Relinquish all memory currently allocated via this pool object.
Definition bdlma_pool.h:621
Definition bslstl_pair.h:1280
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements in this vector.
Definition bslstl_vector.h:3019
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:2866
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:2874
Definition bslstl_vector.h:1120
allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:4621
void push_back(const VALUE_TYPE &value)
Definition bslstl_vector.h:4343
VALUE_TYPE * iterator
Definition bslstl_vector.h:1152
void swap(vector &other) BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(AllocatorTraits void clear() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:1938
Definition bslma_allocator.h:545
Definition bslmf_movableref.h:752
Definition bslmt_rwmutex.h:148
Definition bslmt_readlockguard.h:287
Definition bslmt_writelockguard.h:221
Definition bsls_atomic.h:744
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:231
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
#define BSLS_REVIEW_OPT(X)
Definition bsls_review.h:1060
Definition bdlcc_boundedqueue.h:270
BSLS_KEYWORD_CONSTEXPR std::ptrdiff_t ssize(const TYPE(&)[DIMENSION]) BSLS_KEYWORD_NOEXCEPT
Return the dimension of the specified array argument.
Definition bslstl_iterator.h:1492
Definition bslmf_issame.h:146
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1660
static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1617
Definition bslma_usesbslmaallocator.h:344
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
Definition bdlcc_objectcatalog.h:427
Node * d_next_p
Definition bdlcc_objectcatalog.h:431
bsls::ObjectBuffer< TYPE > d_value
Definition bdlcc_objectcatalog.h:429
Definition bsls_objectbuffer.h:277