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