BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlmt_multiqueuethreadpool.h
Go to the documentation of this file.
1/// @file bdlmt_multiqueuethreadpool.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlmt_multiqueuethreadpool.h -*-C++-*-
8
9#ifndef INCLUDED_BDLMT_MULTIQUEUETHREADPOOL
10#define INCLUDED_BDLMT_MULTIQUEUETHREADPOOL
11
12#include <bsls_ident.h>
13BSLS_IDENT("$Id: $")
14
15/// @defgroup bdlmt_multiqueuethreadpool bdlmt_multiqueuethreadpool
16/// @brief Provide a pool of queues, each processed serially by a thread pool.
17/// @addtogroup bdl
18/// @{
19/// @addtogroup bdlmt
20/// @{
21/// @addtogroup bdlmt_multiqueuethreadpool
22/// @{
23///
24/// <h1> Outline </h1>
25/// * <a href="#bdlmt_multiqueuethreadpool-purpose"> Purpose</a>
26/// * <a href="#bdlmt_multiqueuethreadpool-classes"> Classes </a>
27/// * <a href="#bdlmt_multiqueuethreadpool-description"> Description </a>
28/// * <a href="#bdlmt_multiqueuethreadpool-disabled-queues"> Disabled Queues </a>
29/// * <a href="#bdlmt_multiqueuethreadpool-paused-queues"> Paused Queues </a>
30/// * <a href="#bdlmt_multiqueuethreadpool-thread-safety"> Thread Safety </a>
31/// * <a href="#bdlmt_multiqueuethreadpool-job-execution-batch-size"> Job Execution Batch Size </a>
32/// * <a href="#bdlmt_multiqueuethreadpool-thread-names-for-sub-threads"> Thread Names for Sub-Threads </a>
33/// * <a href="#bdlmt_multiqueuethreadpool-usage"> Usage </a>
34/// * <a href="#bdlmt_multiqueuethreadpool-example-1-a-word-search-application"> Example 1: A Word Search Application </a>
35///
36/// # Purpose {#bdlmt_multiqueuethreadpool-purpose}
37/// Provide a pool of queues, each processed serially by a thread pool.
38///
39/// # Classes {#bdlmt_multiqueuethreadpool-classes}
40///
41/// - bdlmt::MultiQueueThreadPool: multi-threaded, serial processing of queues
42///
43/// @see bdlmt_threadpool
44///
45/// # Description {#bdlmt_multiqueuethreadpool-description}
46/// This component defines a dynamic, configurable pool of queues,
47/// each of which is processed by a thread in a thread pool, such that elements
48/// on a given queue are processed serially, regardless of which thread is
49/// processing the queue at a given time.
50///
51/// A `bdlmt::MultiQueueThreadPool` allows clients to create and delete queues,
52/// and to enqueue "jobs" (represented as client-specified functors) to specific
53/// queues. Queue processing is implemented on top of a `bdlmt::ThreadPool` by
54/// enqueuing a per-queue functor to the thread pool. Each functor dequeues the
55/// next item from its associated queue, processes it, and re-enqueues itself to
56/// the thread pool. Since there is at most one representative functor per
57/// queue, each queue is guaranteed to be processed serially by the thread pool.
58///
59/// In addition to the ability to create, delete, pause, and resume queues,
60/// clients are able to tune the underlying thread pool in accordance with the
61/// `bdlmt::ThreadPool` documentation.
62///
63/// ## Disabled Queues {#bdlmt_multiqueuethreadpool-disabled-queues}
64///
65///
66/// `bdlmt::MultiQueueThreadPool` allows clients to disable and re-enable
67/// queues. A disabled queue will allow no further jobs to be enqueued, but
68/// will continue to process the jobs that were enqueued prior to the call to
69/// `disableQueue`. Note that calling `disableQueue` will block the calling
70/// thread until the currently executing job (if any) on that queue completes.
71///
72/// ## Paused Queues {#bdlmt_multiqueuethreadpool-paused-queues}
73///
74///
75/// `bdlmt::MultiQueueThreadPool` also allows clients to pause and resume
76/// queues. Pausing a queue suspends the processing of jobs from a queue --
77/// i.e., after `pause` returns no further jobs will be processed on that queue
78/// until the queue is resumed. Note that calling `pauseQueue` will block the
79/// calling thread until the currently executing job (if any) on that queue
80/// completes.
81///
82/// ## Thread Safety {#bdlmt_multiqueuethreadpool-thread-safety}
83///
84///
85/// The `bdlmt::MultiQueueThreadPool` class is **fully thread-safe** (i.e., all
86/// public methods of a particular instance may safely execute concurrently).
87/// This class is also **thread-enabled** (i.e., the class does not function
88/// correctly in a non-multi-threading environment). See @ref bsldoc_glossary for
89/// complete definitions of **fully thread-safe** and **thread-enabled**.
90///
91/// ## Job Execution Batch Size {#bdlmt_multiqueuethreadpool-job-execution-batch-size}
92///
93///
94/// `bdlmt::MultiQueueThreadPool` allows clients to configure the maximum size
95/// of a group of jobs that a queue will execute "atomically". "Atomically", in
96/// this context, means that no state changes to the queue will be observed by
97/// that queue during the processing of the collection of jobs (e.g., a call to
98/// `pause` will only pause the queue after the currently executing group of
99/// jobs completes execution). By default a queue's batch size is 1.
100/// Configuring a larger batch size may improve throughput by reducing the
101/// synchronization overhead needed to execute a job. However, for many
102/// use-cases the overall throughput is limited by the time it takes to process
103/// a job (rather than synchronization overhead), so users are strongly
104/// encouraged to use benchmarks to guide their decision when setting this
105/// option.
106///
107/// ## Thread Names for Sub-Threads {#bdlmt_multiqueuethreadpool-thread-names-for-sub-threads}
108///
109///
110/// To facilitate debugging, users can provide a thread name as the `threadName`
111/// attribute of the `bslmt::ThreadAttributes` argument passed to the
112/// constructor, that will be used for all the sub-threads. The thread name
113/// should not be used programmatically, but will appear in debugging tools on
114/// platforms that support naming threads to help users identify the source and
115/// purpose of a thread. If no `ThreadAttributes` object is passed, or if the
116/// `threadName` attribute is not set, the default value "bdl.MultiQuePl" will
117/// be used. Note that this only applies to a `bdlmt::ThreadPool` automatically
118/// created by a `bdlmt::MultiQueueThreadPool`. If a thread pool already exists
119/// and is passed to the multi queue thread pool at construction, the subthreads
120/// will be named however was specified when that thread pool was created.
121///
122/// ## Usage {#bdlmt_multiqueuethreadpool-usage}
123///
124///
125/// This section illustrates intended use of this component.
126///
127/// ### Example 1: A Word Search Application {#bdlmt_multiqueuethreadpool-example-1-a-word-search-application}
128///
129///
130/// This example illustrates the use of a `bdlmt::MultiQueueThreadPool` in a
131/// word search application called `fastSearch`. `fastSearch` searches a list
132/// of files for a list of words, and returns the set of files which contain all
133/// of the specified words. `bdlmt::MultiQueueThreadPool` is used to provide
134/// concurrent processing of files, and to simplify the collection of results by
135/// serializing access to result sets which are maintained for each word.
136///
137/// First, we present a class used to manage a word, and the set of files which
138/// contain that word:
139/// @code
140/// /// This class defines a search profile consisting of a word and a set
141/// /// of files (given by name) that contain the word. Here, "word" is
142/// /// defined as any string of characters.
143/// class my_SearchProfile {
144///
145/// bsl::string d_word; // word to search for
146/// bsl::set<bsl::string> d_fileSet; // set of matching files
147///
148/// private:
149/// // not implemented
150/// my_SearchProfile(const my_SearchProfile&);
151/// my_SearchProfile& operator=(const my_SearchProfile&);
152///
153/// public:
154/// // CREATORS
155///
156/// /// Create a `my_SearchProfile` with the specified `word`.
157/// /// Optionally specify a `basicAllocator` used to supply memory. If
158/// /// `basicAllocator` is 0, the default memory allocator is used.
159/// my_SearchProfile(const char *word,
160/// bslma::Allocator *basicAllocator = 0);
161///
162/// /// Destroy this search profile.
163/// ~my_SearchProfile();
164///
165/// // MANIPULATORS
166///
167/// /// Insert the specified `file` into the file set maintained by this
168/// /// search profile.
169/// void insert(const char *file);
170///
171/// // ACCESSORS
172///
173/// /// Return `true` if the specified `file` matches this search profile.
174/// bool isMatch(const char *file) const;
175///
176/// /// Return a reference to the non-modifiable file set maintained by
177/// /// this search profile.
178/// const bsl::set<bsl::string>& fileSet() const;
179///
180/// /// Return a reference to the non-modifiable word maintained by this
181/// /// search profile.
182/// const bsl::string& word() const;
183/// };
184/// @endcode
185/// And the implementation:
186/// @code
187/// // CREATORS
188/// my_SearchProfile::my_SearchProfile(const char *word,
189/// bslma::Allocator *basicAllocator)
190/// : d_word(basicAllocator)
191/// , d_fileSet(bsl::less<bsl::string>(), basicAllocator)
192/// {
193/// assert(word);
194///
195/// d_word.assign(word);
196/// }
197///
198/// my_SearchProfile::~my_SearchProfile()
199/// {
200/// }
201///
202/// // MANIPULATORS
203/// inline
204/// void my_SearchProfile::insert(const char *file)
205/// {
206/// assert(file);
207///
208/// d_fileSet.insert(file);
209/// }
210///
211/// // ACCESSORS
212/// bool my_SearchProfile::isMatch(const char *file) const
213/// {
214/// assert(file);
215///
216/// bool found = false;
217/// bsl::ifstream ifs(file);
218/// bsl::string line;
219/// while (bsl::getline(ifs, line)) {
220/// if (bsl::string::npos != line.find(d_word)) {
221/// found = true;
222/// break;
223/// }
224/// }
225/// ifs.close();
226/// return found;
227/// }
228///
229/// inline
230/// const bsl::set<bsl::string>& my_SearchProfile::fileSet() const
231/// {
232/// return d_fileSet;
233/// }
234///
235/// inline
236/// const bsl::string& my_SearchProfile::word() const
237/// {
238/// return d_word;
239/// }
240/// @endcode
241/// Next, we define a helper function to perform a search of a word in a
242/// particular file. The function is parameterized by a search profile and a
243/// file name. If the specified file name matches the profile, it is inserted
244/// into the profile's file list.
245/// @code
246/// /// Insert the specified `file` to the file set of the specified search
247/// /// `profile` if `file` matches the `profile`.
248/// void my_SearchCb(my_SearchProfile* profile, const char *file)
249/// {
250///
251/// assert(profile);
252/// assert(file);
253///
254/// if (profile->isMatch(file)) {
255/// profile->insert(file);
256/// }
257/// }
258/// @endcode
259/// Lastly, we present the front end to the search application: `fastSearch`.
260/// `fastSearch` is parameterized by a list of words to search for, a list of
261/// files to search in, and a set which is populated with the search results.
262/// `fastSearch` instantiates a `bdlmt::MultiQueueThreadPool`, and creates a
263/// queue for each word. It then associates each queue with a search profile
264/// based on a word in the word list. Then, it enqueues a job to each queue for
265/// each file in the file list that tries to match the file to each search
266/// profile. Lastly, `fastSearch` collects the results, which is the set
267/// intersection of each file set maintained by the individual search profiles.
268/// @code
269/// /// Return the set of files, specified by `fileList`, containing every
270/// /// word in the specified `wordList`, in the specified `resultSet`.
271/// /// Optionally specify `repetitions`, the number of repetitions to run
272/// /// the search jobs (it is used to increase the load for performance
273/// /// testing). Optionally specify a `basicAllocator` used to supply
274/// /// memory. If `basicAllocator` is 0, the default memory allocator is
275/// /// used.
276/// void fastSearch(const bsl::vector<bsl::string>& wordList,
277/// const bsl::vector<bsl::string>& fileList,
278/// bsl::set<bsl::string>& resultSet,
279/// int repetitions = 1,
280/// bslma::Allocator *basicAllocator = 0)
281/// {
282///
283/// typedef bsl::vector<bsl::string> ListType;
284/// // This type is defined for notational convenience when iterating
285/// // over 'wordList' or 'fileList'.
286///
287/// typedef bsl::pair<int, my_SearchProfile*> RegistryValue;
288/// // This type is defined for notational convenience. The first
289/// // parameter specifies a queue ID. The second parameter specifies
290/// // an associated search profile.
291///
292/// typedef bsl::map<bsl::string, RegistryValue> RegistryType;
293/// // This type is defined for notational convenience. The first
294/// // parameter specifies a word. The second parameter specifies a
295/// // tuple containing a queue ID, and an associated search profile
296/// // containing the specified word.
297///
298/// enum {
299/// // thread pool configuration
300/// k_MIN_THREADS = 4,
301/// k_MAX_THREADS = 20,
302/// k_MAX_IDLE = 100 // use a very short idle time since new jobs
303/// // arrive only at startup
304/// };
305/// bslmt::ThreadAttributes defaultAttrs;
306/// bdlmt::MultiQueueThreadPool pool(defaultAttrs,
307/// k_MIN_THREADS,
308/// k_MAX_THREADS,
309/// k_MAX_IDLE,
310/// basicAllocator);
311/// RegistryType profileRegistry(bsl::less<bsl::string>(), basicAllocator);
312///
313/// // Create a queue and a search profile associated with each word in
314/// // 'wordList'.
315///
316/// for (ListType::const_iterator it = wordList.begin();
317/// it != wordList.end();
318/// ++it) {
319/// bslma::Allocator *allocator =
320/// bslma::Default::allocator(basicAllocator);
321///
322/// const bsl::string& word = *it;
323/// int id = pool.createQueue();
324/// LOOP_ASSERT(word, 0 != id);
325/// my_SearchProfile *profile = new (*allocator)
326/// my_SearchProfile(word.c_str(),
327/// allocator);
328///
329/// bslma::RawDeleterProctor<my_SearchProfile, bslma::Allocator>
330/// deleter(profile, allocator);
331///
332/// profileRegistry[word] = bsl::make_pair(id, profile);
333/// deleter.release();
334/// }
335///
336/// // Start the pool, enabling enqueuing and queue processing.
337/// pool.start();
338///
339/// // Enqueue a job which tries to match each file in 'fileList' with each
340/// // search profile.
341///
342/// for (ListType::const_iterator it = fileList.begin();
343/// it != fileList.end();
344/// ++it) {
345/// for (ListType::const_iterator jt = wordList.begin();
346/// jt != wordList.end();
347/// ++jt) {
348/// const bsl::string& file = *it;
349/// const bsl::string& word = *jt;
350/// RegistryValue& rv = profileRegistry[word];
351/// Func job;
352/// makeFunc(&job, my_SearchCb, rv.second, file.c_str());
353/// for (int i = 0; i < repetitions; ++i) {
354/// int rc = pool.enqueueJob(rv.first, job);
355/// LOOP_ASSERT(word, 0 == rc);
356/// }
357/// }
358/// }
359///
360/// // Stop the pool, and wait while enqueued jobs are processed.
361/// pool.stop();
362///
363/// // Construct the 'resultSet' as the intersection of file sets collected
364/// // in each search profile.
365///
366/// resultSet.insert(fileList.begin(), fileList.end());
367/// for (RegistryType::iterator it = profileRegistry.begin();
368/// it != profileRegistry.end();
369/// ++it) {
370/// my_SearchProfile *profile = it->second.second;
371/// const bsl::set<bsl::string>& fileSet = profile->fileSet();
372/// bsl::set<bsl::string> tmpSet;
373/// bsl::set_intersection(fileSet.begin(),
374/// fileSet.end(),
375/// resultSet.begin(),
376/// resultSet.end(),
377/// bsl::inserter(tmpSet, tmpSet.begin()));
378/// resultSet = tmpSet;
379/// bslma::Default::allocator(basicAllocator)->deleteObjectRaw(
380/// profile);
381/// }
382/// }
383/// @endcode
384/// @}
385/** @} */
386/** @} */
387
388/** @addtogroup bdl
389 * @{
390 */
391/** @addtogroup bdlmt
392 * @{
393 */
394/** @addtogroup bdlmt_multiqueuethreadpool
395 * @{
396 */
397
398#include <bdlscm_version.h>
399
400#include <bdlcc_objectpool.h>
401
402#include <bdlmt_threadpool.h>
403
404#include <bslma_allocator.h>
406
407#include <bslmf_movableref.h>
409
410#include <bslmt_condition.h>
411#include <bslmt_latch.h>
412#include <bslmt_lockguard.h>
413#include <bslmt_mutex.h>
414#include <bslmt_mutexassert.h>
416#include <bslmt_readlockguard.h>
417#include <bslmt_writelockguard.h>
418
419#include <bsls_assert.h>
420#include <bsls_atomic.h>
421
422#include <bsl_deque.h>
423#include <bsl_functional.h>
424#include <bsl_map.h>
425
426
427namespace bdlmt {
428
429class MultiQueueThreadPool;
430
431 // ================================
432 // class MultiQueueThreadPool_Queue
433 // ================================
434
435/// This private class provides a thread-safe, lightweight job queue.
436///
437/// See @ref bdlmt_multiqueuethreadpool
439
440 public:
441 // PUBLIC TYPES
442 typedef bsl::function<void()> Job;
443
444 private:
445 // PRIVATE TYPES
446 enum EnqueueState {
447 // enqueue states
448 e_ENQUEUING_ENABLED, // enqueuing is enabled
449 e_ENQUEUING_DISABLED, // enqueuing is disabled
450 e_DELETING // deleting
451 };
452
453 enum RunState {
454 e_NOT_SCHEDULED, // running but not scheduled
455 e_SCHEDULED, // running and scheduled
456 e_PAUSING, // pause requested but not completed yet
457 e_PAUSED // paused
458 };
459
460 // DATA
461 MultiQueueThreadPool *d_multiQueueThreadPool_p;
462 // the `MultiQueueThreadPool`
463 // that owns this object
464
465 bsl::deque<Job> d_list; // queue of jobs to be
466 // executed
467
468 EnqueueState d_enqueueState; // maintains enqueue state
469
470 RunState d_runState; // maintains run state
471
472 bsl::vector<Job> d_batch; // batch of jobs
473
474 int d_batchSize; // execution batch size
475
476 mutable bslmt::Mutex d_lock; // protect queue and
477 // informational members
478
479 bslmt::Condition d_pauseCondition; // use to notify thread
480 // awaiting pause state
481
482 int d_pauseCount; // number of threads waiting
483 // for the pause to complete
484
485 Job d_processingCb; // bound processing callback
486 // for pool
487
488 bslmt::ThreadUtil::Handle d_processor; // current worker thread, or
489 // ThreadUtil::invalidHandle()
490
491 private:
492 // NOT IMPLEMENTED
493 MultiQueueThreadPool_Queue();
494 MultiQueueThreadPool_Queue(const MultiQueueThreadPool_Queue&);
495 MultiQueueThreadPool_Queue &operator=(const MultiQueueThreadPool_Queue &);
496
497 // PRIVATE MANIPULATORS
498
499 /// Mark this queue as paused, notify any threads blocked on
500 /// `d_pauseCondition`, and schedule the deletion job if this queue is to be deleted.
501 ///
502 /// \pre The behavior is undefined unless this queue's lock
503 /// is in a locked state and `e_PAUSING == d_runState`.
504 void setPaused();
505
506 public:
507 // TRAITS
510
511 // CREATORS
512
513 /// Create a `MultiQueueThreadPool_Queue` with an initial capacity of 0
514 /// and initialized to use the specified `multiQueueThreadPool` to track
515 /// aggregate values (e.g., the number of active queues) and to obtain
516 /// the thread pool used to execute jobs that are appended to this
517 /// queue. Optionally specify a `basicAllocator` used to supply memory.
518 /// If `basicAllocator` is 0, the default memory allocator is used.
519 explicit
521 bslma::Allocator *basicAllocator = 0);
522
523 /// Destroy this queue.
525
526 // MANIPULATORS
527
528 /// Enable enqueuing to this queue. Return 0 on success, and a non-zero
529 /// value otherwise. This method will fail (with an error) if
530 /// `prepareForDeletion` has already been called on this object.
531 int enable();
532
533 /// Disable enqueuing to this queue. Return 0 on success, and a non-zero
534 /// value otherwise. This method will fail (with an error) if
535 /// `prepareForDeletion` has already been called on this object.
536 int disable();
537
538 /// Block until all threads waiting for this queue to pause are released.
540
541 /// Execute the `Job` at the front of this queue, dequeue the `Job`, and
542 /// if the queue is not paused schedule a callback from the associated thread pool.
543 ///
544 /// \pre The behavior is undefined if this queue is empty.
546
547 /// Permanently disable enqueueing from this queue, and enqueue a job
548 /// that will delete this queue. Optionally specify `cleanupFunctor`,
549 /// which, if supplied, will be invoked immediately prior to this
550 /// queue's deletion. Optionally specify `completionSignal`, on which
551 /// (if the calling thread is not processing a job - or batch of jobs -
552 /// for this queue) to invoke `arrive` when the queue is deleted.
553 /// Return `true` if the current thread is the thread processing a job (or batch of jobs), and `false` otherwise.
554 ///
555 /// \note Note that if
556 /// `completionSignal` is supplied, a return status of `false` typically
557 /// indicates that `completionSignal->wait()` should be invoked from the
558 /// calling function', while a return status of `true` indicates this is
559 /// an attempt to delete the queue from within a job being processed on
560 /// the queue (so waiting on the queue's deletion would result in a
561 /// dead-lock).
562 bool enqueueDeletion(const Job& cleanupFunctor = Job(),
563 bslmt::Latch *completionSignal = 0);
564
565 /// Initiate the pausing of this queue, prevent jobs from being executed
566 /// on this queue (excluding the currently-executing job - or batch of
567 /// jobs - if there is one), and prevent the queue from being deleted.
568 /// Return 0 on success, and a non-zero value if the queue is already
569 /// paused or is being paused or deleted by another thread.
570 ///
571 /// \pre The behavior is undefined unless, after a successful invocation of
572 /// `initiatePause`, `waitWhilePausing` is invoked (to complete the
573 /// pause operation and allow the queue to, potentially, be deleted).
575
576 /// Enqueue the specified `functor` at the end of this queue. Return 0
577 /// on success, and a non-zero value if enqueuing is disabled. The value
578 /// of `functor` becomes unspecified but valid, and its allocator remains
579 /// unchanged.
581
582 /// Add the specified `functor` at the front of this queue. Return 0 on
583 /// success, and a non-zero value if enqueuing is disabled. The value of
584 /// `functor` becomes unspecified but valid, and its allocator remains
585 /// unchanged.
587
588 /// Reset this queue to its initial state.
589 ///
590 /// \pre The behavior is undefined unless this queue's lock is in an unlocked state. After this method
591 /// returns, the object is ready for use as though it were a new object.
592 ///
593 /// \note Note that this method is not thread-safe and is used by the object
594 /// pool contained within `*d_multiQueueThreadPool_p`.
595 void reset();
596
597 /// Allow jobs on the queue to begin executing. Return 0 on success,
598 /// and a non-zero value if the queue is not paused or `!d_list.empty()`
599 /// and the associated thread pool fails to enqueue a job.
600 int resume();
601
602 /// Configure this queue to process jobs in groups of the specified
603 /// `batchSize` (see {`Job Execution Batch Size`}). When a thread is
604 /// selecting jobs for processing, if fewer than `batchSize` jobs are
605 /// available then only the available jobs will be processed in the current batch.
606 ///
607 /// \pre The behavior is undefined unless `1 <= batchSize`.
608 ///
609 /// \note Note that the initial value for the execution batch size is 1 for
610 /// all queues.
612
613 /// Wait until any currently-executing job on the queue completes and the queue is paused.
614 ///
615 /// \note Note that pausing differs from `disable` in
616 /// that (1) `pause` stops processing for a queue, and (2) does *not*
617 /// prevent additional jobs from being enqueued. The behavior of this
618 /// method is undefined unless it is invoked after a successful
619 /// `initiatePause` invocation.
621
622 // ACCESSORS
623
624 /// Return an instantaneous snapshot of the execution batch size (see
625 /// {`Job Execution Batch Size`}). When a thread is selecting jobs for
626 /// processing, if fewer than `batchSize` jobs are available then only
627 /// the available jobs will be processed in the current batch.
628 int batchSize() const;
629
630 /// Report whether all jobs in this queue are finished.
631 bool isDrained() const;
632
633 /// Report whether enqueuing to this object is enabled. This object is
634 /// constructed with enqueuing enabled.
635 bool isEnabled() const;
636
637 /// Report whether this object is paused.
638 bool isPaused() const;
639
640 /// Return an instantaneous snapshot of the length of this queue.
641 int length() const;
642};
643
644 // ==========================
645 // class MultiQueueThreadPool
646 // ==========================
647
648/// This class implements a dynamic, configurable pool of queues, each of
649/// which is processed serially by a thread pool.
650///
651/// See @ref bdlmt_multiqueuethreadpool
653
654 // FRIENDS
656
657 // PRIVATE TYPES
658 enum State {
659 // Internal running states.
660 e_STATE_RUNNING,
661 e_STATE_STOPPING,
662 e_STATE_STOPPED
663 };
664
665 public:
666 // PUBLIC TYPES
667 typedef bsl::function<void()> Job;
670
671 private:
672 // PRIVATE CLASS DATA
673 static const char s_defaultThreadName[16]; // Thread name to use
674 // when none is
675 // specified.
676
677 // PRIVATE DATA
678 bslma::Allocator *d_allocator_p; // memory allocator (held)
679
680 ThreadPool *d_threadPool_p; // threads for queue processing
681
682 bool d_threadPoolIsOwned; // `true` if thread pool is owned
683
688 > d_queuePool; // pool of queues
689
690 QueueRegistry d_queueRegistry; // registry of queues
691
692 int d_nextId; // next id to provide from
693 // `createQueue`
694
695 State d_state; // maintains internal state
696
698 d_lock; // locked for write when deleting
699 // queues or changing pool state
700
701 bsls::AtomicInt d_numActiveQueues; // number of non-empty queues
702
703 bsls::AtomicInt d_numExecuted; // the total number of requests
704 // processed by this pool since the
705 // last time this value was reset
706
707 bsls::AtomicInt d_numEnqueued; // the total number of requests
708 // enqueued into this pool since
709 // the last time this value was
710 // reset
711
712 bsls::AtomicInt d_numDeleted; // the total number of requests
713 // deleted from this pool since the
714 // last time this value was reset
715 private:
716 // NOT IMPLEMENTED
719
720 // PRIVATE MANIPULATORS
721
722 /// Delete the specified `queue`, if the specified `cleanup` is valid
723 /// invoke `cleanup`, if the specified `completionSignal` is not 0, call
724 /// `completionSignal->arrive`. `completionSignal` may be 0.
725 ///
726 /// \note Note that this callback provides a mechanism for proper lifetime management of
727 /// the `queue` by scheduling the deletion with the associated thread
728 /// pool since the `MultiQueueThreadPool` does not know *when* to delete
729 /// the queue and a `MultiQueueThreadPool_Queue` cannot delete itself at
730 /// the appropriate time.
731 void deleteQueueCb(MultiQueueThreadPool_Queue *queue,
732 const CleanupFunctor& cleanup,
733 bslmt::Latch *completionSignal);
734
735 /// Load into the specified `*queue` a pointer to the queue referenced
736 /// by the specified `id` if this `MultiQueueThreadPool` is in a state
737 /// where the `queue` can be used. Return 0 on success, and a non-zero
738 /// value if the `id` is not contained in `d_queueRegistry`, this
739 /// `MultiQueueThreadPool` is not in the running state, or `0 == d_threadPool_p->enabled()`.
740 ///
741 /// \pre The behavior is undefined unless
742 /// the invoking thread has a lock, read or write, on `d_lock`.
743 int findIfUsable(int id, MultiQueueThreadPool_Queue **queue);
744
745 public:
746 // TRAITS
749
750 // CREATORS
751
752 /// Construct a `MultiQueueThreadPool` with the specified
753 /// `threadAttributes`, the specified `minThreads` minimum number of
754 /// threads, the specified `maxThreads` maximum number of threads, and
755 /// the specified `maxIdleTime` idle time (in milliseconds) after which
756 /// a thread may be considered for destruction. Optionally specify a
757 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0,
758 /// the currently installed default allocator is used.
759 ///
760 /// \pre The behavior is undefined unless `0 <= minThreads`, `minThreads <= maxThreads`, and `0 <= maxIdleTime`.
761 ///
762 /// \note Note that the `MultiQueueThreadPool` is created
763 /// without any queues. Although queues may be created, `start` must be
764 /// called before enqueuing jobs.
766 int minThreads,
767 int maxThreads,
768 int maxIdleTime,
769 bslma::Allocator *basicAllocator = 0);
770
771 /// Construct a `MultiQueueThreadPool` with the specified `threadPool`.
772 /// Optionally specify a `basicAllocator` used to supply memory. If
773 /// `basicAllocator` is 0, the default memory allocator is used.
774 ///
775 /// \pre The behavior is undefined if `threadPool` is 0.
776 /// \note Note that the
777 /// `MultiQueueThreadPool` is created without any queues. Although
778 /// queues may be created, `start` must be called before enqueuing jobs.
779 explicit
781 bslma::Allocator *basicAllocator = 0);
782
783 /// Destroy this multi-queue thread pool. Disable queuing on all
784 /// queues, and wait until all queues are empty. Then, delete all
785 /// queues, and shut down the thread pool if the thread pool is owned by
786 /// this object. This method will block if any thread is executing
787 /// `start` or `stop` at the time of the call.
789
790 // MANIPULATORS
791
792 /// Add the specified `functor` at the front of the queue specified by
793 /// `id`. Return 0 if added successfully, and a non-zero value if
794 /// queuing is disabled. If passed by movable reference, the value of
795 /// `functor` becomes unspecified but valid, and its allocator remains unchanged.
796 ///
797 /// \pre The behavior is undefined unless `functor` is bound.
798 ///
799 /// \note Note that the position of `functor` relative to any currently queued jobs is
800 /// unspecified unless the queue is currently paused.
801 int addJobAtFront(int id, const Job& functor);
802 int addJobAtFront(int id, bslmf::MovableRef<Job> functor);
803
804 /// Create a queue with unlimited capacity and a default number of
805 /// initial elements. Return a non-zero queue ID. The queue ID can be
806 /// used to enqueue jobs to the queue, or to control or delete the
807 /// queue.
809
810 /// Disable enqueuing to the queue associated with the specified `id`,
811 /// and enqueue the specified `cleanupFunctor` to the *front* of the
812 /// queue. The `cleanupFunctor` is guaranteed to be the last queue
813 /// element processed, after which the queue is destroyed. This
814 /// function does not wait for the `cleanupFunctor` to be executed
815 /// (instead the caller is notified asynchronously through the execution
816 /// of the supplied `cleanupFunctor`). Return 0 on success, and a non-zero value otherwise.
817 ///
818 /// \note Note that this function will fail if this
819 /// pool is stopped.
820 int deleteQueue(int id, const CleanupFunctor& cleanupFunctor);
821
822 /// Disable enqueuing to the queue associated with the specified `id`,
823 /// and when the currently executing job (or batch of jobs) of that
824 /// queue, if any, is complete, then destroy the queue. Return 0 on
825 /// success, and a non-zero value otherwise. This function will fail if
826 /// the pool is stopped. Any other (non-executing) jobs on the queue
827 /// are deleted asynchronously. The calling thread blocks until
828 /// completion of the currently executing job (or batch of jobs), except
829 /// when `deleteQueue` is called from a job in the queue being deleted.
830 /// In that latter case, no block takes place, the queue is deleted (no
831 /// longer observable from the `MultiQueueThreadPool`), and the job
832 /// completes.
833 int deleteQueue(int id);
834
835 /// Disable enqueuing to the queue associated with the specified `id`. Return 0 on success, and a non-zero value otherwise.
836 ///
837 /// \note Note that this
838 /// method differs from `pauseQueue` in that (1) `disableQueue` does
839 /// *not* stop processing for a queue, and (2) prevents additional jobs
840 /// from being enqueued.
841 int disableQueue(int id);
842
843 /// Wait until all queues are empty. This method waits until all
844 /// non-paused queues are empty without disabling the queues (and may
845 /// thus wait indefinitely). The queues and/or the thread pool may be
846 /// either enabled or disabled when this method is called. This method
847 /// may be called on a stopped or started thread pool.
848 ///
849 /// \note Note that `drain` does not attempt to delete queues directly. However, as a
850 /// side-effect of emptying all queues, any queue for which
851 /// `deleteQueue` was called previously will be deleted before `drain`
852 /// returns. Note also that this method waits by repeatedly yielding.
853 void drain();
854
855 /// Wait until all jobs in the queue indicated by the specified `id` are
856 /// finished. This method simply waits until that queue is empty,
857 /// without disabling the queue; it may thus wait indefinitely if more
858 /// jobs are being added. The queue may be enabled or disabled when
859 /// this method is called. Return 0 on success, and a non-zero value if
860 /// the specified queue does not exist or is deleted while this method is waiting.
861 ///
862 /// \note Note that this method waits by repeatedly yielding.
863 int drainQueue(int id);
864
865 /// Enqueue the specified `functor` to the queue specified by `id`.
866 /// Return 0 if enqueued successfully, and a non-zero value if queuing
867 /// is disabled. If passed by movable reference, the value of `functor`
868 /// becomes unspecified but valid, and its allocator remains unchanged.
869 ///
870 /// \pre The behavior is undefined unless `functor` is bound.
871 int enqueueJob(int id, const Job& functor);
872 int enqueueJob(int id, bslmf::MovableRef<Job> functor);
873
874 /// Enable enqueuing to the queue associated with the specified `id`.
875 /// Return 0 on success, and a non-zero value otherwise. It is an error
876 /// to call `enableQueue` if a previous call to `stop` is being
877 /// executed.
878 int enableQueue(int id);
879
880 /// Load into the specified `numExecuted` and `numEnqueued` the number
881 /// of items dequeued / enqueued (respectively) since the last time
882 /// these values were reset and reset these values. Optionally specify
883 /// a `numDeleted` used to load into the number of items deleted since
884 /// the last time this value was reset. Reset the count of deleted
885 /// items.
886 void numProcessedReset(int *numExecuted,
887 int *numEnqueued,
888 int *numDeleted = 0);
889
890 /// Wait until any currently-executing job (or batch of jobs) on the
891 /// queue with the specified `id` completes, then prevent any more jobs
892 /// from being executed on that queue. Return 0 on success, and a
893 /// non-zero value if the queue is already paused or is being paused or deleted by another thread.
894 ///
895 /// \note Note that this method may be invoked
896 /// from a job executing on the given queue, in which case this method
897 /// does not wait. Note also that this method differs from
898 /// `disableQueue` in that (1) `pauseQueue` stops processing for a
899 /// queue, and (2) does *not* prevent additional jobs from being
900 /// enqueued.
901 int pauseQueue(int id);
902
903 /// Allow jobs on the queue with the specified `id` to begin executing.
904 /// Return 0 on success, and a non-zero value if the queue does not
905 /// exist or is not paused.
906 int resumeQueue(int id);
907
908 /// Configure the queue specified by `id` to process jobs in groups of
909 /// the specified `batchSize` (see {`Job Execution Batch Size`}). When
910 /// a thread is selecting jobs for processing, if fewer than `batchSize`
911 /// jobs are available then only the available jobs will be processed in
912 /// the current batch. Return 0 on success, and a non-zero value otherwise.
913 ///
914 /// \pre The behavior is undefined unless `1 <= batchSize`.
915 ///
916 /// \note Note that the initial value for the execution batch size is 1 for all
917 /// queues.
918 int setBatchSize(int id, int batchSize);
919
920 /// Disable queuing on all queues, and wait until all non-paused queues
921 /// are empty. Then, delete all queues, and shut down the thread pool
922 /// if the thread pool is owned by this object.
923 void shutdown();
924
925 /// Enable queuing on all queues, start the thread pool if the thread
926 /// pool is owned by this object, and ensure that at least the minimum
927 /// number of processing threads are started. Return 0 on success, and
928 /// a non-zero value otherwise. This method will block if any thread is
929 /// executing `stop` or `shutdown` at the time of the call. This method
930 /// has no effect if this thread pool has already been started.
931 ///
932 /// \note Note that any paused queues remain paused.
933 int start();
934
935 /// Disable queuing on all queues and wait until all non-paused queues
936 /// are empty. Then, stop the thread pool if the thread pool is owned by this object.
937 ///
938 /// \note Note that `stop` does not attempt to delete queues
939 /// directly. However, as a side-effect of emptying all queues, any
940 /// queue for which `deleteQueue` was called previously will be deleted
941 /// before `stop` unblocks.
942 void stop();
943
944 // ACCESSORS
945
946 /// Return an instantaneous snapshot of the execution batch size (see
947 /// @ref bdlmt_multiqueuethreadpool-job-execution-batch-size ) of the queue associated with the
948 /// specified `id`, or -1 if `id` is not a valid queue id. When a
949 /// thread is selecting jobs for processing, if fewer than `batchSize`
950 /// jobs are available then only the available jobs will be processed in
951 /// the current batch.
952 int batchSize(int id) const;
953
954 /// Return `true` if the queue associated with the specified `id` is
955 /// currently paused, or `false` otherwise (including if `id` is not a
956 /// valid queue id).
957 bool isPaused(int id) const;
958
959 /// Return `true` if the queue associated with the specified `id` is
960 /// currently enabled, or `false` otherwise (including if `id` is not a
961 /// valid queue id).
962 bool isEnabled(int id) const;
963
964 /// Return an instantaneous snapshot of the number of queues managed by
965 /// this object.
966 int numQueues() const;
967
968 /// Return an instantaneous snapshot of the total number of elements
969 /// enqueued.
970 int numElements() const;
971
972 /// Return an instantaneous snapshot of the number of elements enqueued in
973 /// the queue associated with the specified `id` as a non-negative integer,
974 /// or -1 if `id` does not specify a valid queue.
975 int numElements(int id) const;
976
977 /// Load into the specified `numExecuted` and `numEnqueued` the number of
978 /// items dequeued / enqueued (respectively) since the last time these
979 /// values were reset. Optionally specify a `numDeleted` used to load into
980 /// the number of items deleted since the last time this value was reset.
981 void numProcessed(int *numExecuted,
982 int *numEnqueued,
983 int *numDeleted = 0) const;
984
985 /// Return a reference to the non-modifiable thread pool owned by this
986 /// object.
987 const ThreadPool& threadPool() const;
988};
989
990// ============================================================================
991// INLINE DEFINITIONS
992// ============================================================================
993
994 // --------------------------------
995 // class MultiQueueThreadPool_Queue
996 // --------------------------------
997
998// ACCESSORS
999inline
1001{
1002 bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
1003
1004 return d_batchSize;
1005}
1006
1007inline
1009{
1010 bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
1011
1012 return 0 == d_list.size() && ( e_NOT_SCHEDULED == d_runState
1013 || e_PAUSED == d_runState);
1014}
1015
1016inline
1018{
1019 bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
1020
1021 return e_ENQUEUING_ENABLED == d_enqueueState;
1022}
1023
1024inline
1026{
1027 bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
1028
1029 return e_PAUSED == d_runState;
1030}
1031
1032inline
1034{
1035 bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
1036
1037 return static_cast<int>(d_list.size());
1038}
1039
1040 // --------------------------
1041 // class MultiQueueThreadPool
1042 // --------------------------
1043
1044// PRIVATE MANIPULATORS
1045inline
1046int MultiQueueThreadPool::findIfUsable(int id,
1048{
1049 if ( e_STATE_RUNNING != d_state
1050 || 0 == d_threadPool_p->enabled()) {
1051 return 1; // RETURN
1052 }
1053
1054 QueueRegistry::iterator iter = d_queueRegistry.find(id);
1055
1056 if (d_queueRegistry.end() == iter) {
1057 return 1; // RETURN
1058 }
1059
1060 *queue = iter->second;
1061
1062 return 0;
1063}
1064
1065// MANIPULATORS
1066inline
1067int MultiQueueThreadPool::addJobAtFront(int id, const Job& functor)
1068{
1069 Job temp(bsl::allocator_arg, d_allocator_p, functor);
1070
1072}
1073
1074inline
1076{
1078
1080
1081 if (findIfUsable(id, &queue)) {
1082 return 1; // RETURN
1083 }
1084
1085 if (0 == queue->pushFront(bslmf::MovableRefUtil::move(functor))) {
1086 ++d_numEnqueued;
1087 return 0; // RETURN
1088 }
1089
1090 return 1;
1091}
1092
1093inline
1094int MultiQueueThreadPool::enqueueJob(int id, const Job& functor)
1095{
1096 Job temp(bsl::allocator_arg, d_allocator_p, functor);
1097
1098 return enqueueJob(id, bslmf::MovableRefUtil::move(temp));
1099}
1100
1101inline
1103{
1105
1107
1108 if (findIfUsable(id, &queue)) {
1109 return 1; // RETURN
1110 }
1111
1112 if (0 == queue->pushBack(bslmf::MovableRefUtil::move(functor))) {
1113 ++d_numEnqueued;
1114 return 0; // RETURN
1115 }
1116
1117 return 1;
1118}
1119
1120inline
1122 int *numEnqueued,
1123 int *numDeleted)
1124{
1126
1127 // To maintain consistency, all three must be zeroed atomically.
1128
1129 *numExecuted = d_numExecuted.swap(0);
1130 if (numDeleted) {
1131 *numDeleted = d_numDeleted.swap(0);
1132 }
1133 else {
1134 d_numDeleted = 0;
1135 }
1136 *numEnqueued = d_numEnqueued.swap(0);
1137}
1138
1139inline
1140int MultiQueueThreadPool::setBatchSize(int id, int batchSize)
1141{
1143
1145
1147
1148 if (findIfUsable(id, &queue)) {
1149 return 1; // RETURN
1150 }
1151
1152 queue->setBatchSize(batchSize);
1153
1154 return 0;
1155}
1156
1157// ACCESSORS
1158inline
1160{
1162
1163 QueueRegistry::const_iterator iter = d_queueRegistry.find(id);
1164
1165 if (d_queueRegistry.end() != iter) {
1166 return iter->second->batchSize(); // RETURN
1167 }
1168
1169 return -1;
1170}
1171
1172inline
1174{
1176
1177 QueueRegistry::const_iterator iter = d_queueRegistry.find(id);
1178
1179 if (d_queueRegistry.end() != iter) {
1180 return iter->second->isEnabled(); // RETURN
1181 }
1182
1183 return false;
1184}
1185
1186inline
1188{
1190
1191 QueueRegistry::const_iterator iter = d_queueRegistry.find(id);
1192
1193 if (d_queueRegistry.end() != iter) {
1194 return iter->second->isPaused(); // RETURN
1195 }
1196
1197 return false;
1198}
1199
1200inline
1202{
1203 // Access 'd_numEnqueued' last to ensure the result is non-negative.
1204
1205 return -(d_numExecuted + d_numDeleted) + d_numEnqueued;
1206}
1207
1208inline
1210{
1212
1213 QueueRegistry::const_iterator iter = d_queueRegistry.find(id);
1214
1215 if (d_queueRegistry.end() != iter) {
1216 return iter->second->length(); // RETURN
1217 }
1218
1219 return -1;
1220}
1221
1222inline
1224 int *numEnqueued,
1225 int *numDeleted) const
1226{
1227 // Access 'd_numEnqueued' last to ensure
1228 // 'numEnqueued >= numExecuted + numDeleted'.
1229
1230 *numExecuted = d_numExecuted;
1231 if (numDeleted) {
1232 *numDeleted = d_numDeleted;
1233 }
1234 *numEnqueued = d_numEnqueued;
1235}
1236
1237inline
1239{
1241
1242 return static_cast<int>(d_queueRegistry.size());
1243}
1244
1245inline
1247{
1248 return *d_threadPool_p;
1249}
1250
1251} // close package namespace
1252
1253
1254#endif
1255
1256// ----------------------------------------------------------------------------
1257// Copyright 2020 Bloomberg Finance L.P.
1258//
1259// Licensed under the Apache License, Version 2.0 (the "License");
1260// you may not use this file except in compliance with the License.
1261// You may obtain a copy of the License at
1262//
1263// http://www.apache.org/licenses/LICENSE-2.0
1264//
1265// Unless required by applicable law or agreed to in writing, software
1266// distributed under the License is distributed on an "AS IS" BASIS,
1267// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1268// See the License for the specific language governing permissions and
1269// limitations under the License.
1270// ----------------------------- END-OF-FILE ----------------------------------
1271
1272/** @} */
1273/** @} */
1274/** @} */
Definition bdlcc_objectpool.h:446
Definition bdlcc_objectpool.h:694
Definition bdlmt_multiqueuethreadpool.h:438
bool isPaused() const
Report whether this object is paused.
Definition bdlmt_multiqueuethreadpool.h:1025
BSLMF_NESTED_TRAIT_DECLARATION(MultiQueueThreadPool_Queue, bslma::UsesBslmaAllocator)
bool isDrained() const
Report whether all jobs in this queue are finished.
Definition bdlmt_multiqueuethreadpool.h:1008
bsl::function< void()> Job
Definition bdlmt_multiqueuethreadpool.h:442
~MultiQueueThreadPool_Queue()
Destroy this queue.
int batchSize() const
Definition bdlmt_multiqueuethreadpool.h:1000
int pushBack(bslmf::MovableRef< Job > functor)
void setBatchSize(int batchSize)
bool enqueueDeletion(const Job &cleanupFunctor=Job(), bslmt::Latch *completionSignal=0)
MultiQueueThreadPool_Queue(MultiQueueThreadPool *multiQueueThreadPool, bslma::Allocator *basicAllocator=0)
bool isEnabled() const
Definition bdlmt_multiqueuethreadpool.h:1017
int pushFront(bslmf::MovableRef< Job > functor)
void drainWaitWhilePausing()
Block until all threads waiting for this queue to pause are released.
int length() const
Return an instantaneous snapshot of the length of this queue.
Definition bdlmt_multiqueuethreadpool.h:1033
Definition bdlmt_multiqueuethreadpool.h:652
MultiQueueThreadPool(const bslmt::ThreadAttributes &threadAttributes, int minThreads, int maxThreads, int maxIdleTime, bslma::Allocator *basicAllocator=0)
bsl::function< void()> Job
Definition bdlmt_multiqueuethreadpool.h:667
bool isPaused(int id) const
Definition bdlmt_multiqueuethreadpool.h:1187
int numElements() const
Definition bdlmt_multiqueuethreadpool.h:1201
int enqueueJob(int id, const Job &functor)
Definition bdlmt_multiqueuethreadpool.h:1094
void numProcessedReset(int *numExecuted, int *numEnqueued, int *numDeleted=0)
Definition bdlmt_multiqueuethreadpool.h:1121
int setBatchSize(int id, int batchSize)
Definition bdlmt_multiqueuethreadpool.h:1140
const ThreadPool & threadPool() const
Definition bdlmt_multiqueuethreadpool.h:1246
int batchSize(int id) const
Definition bdlmt_multiqueuethreadpool.h:1159
int addJobAtFront(int id, const Job &functor)
Definition bdlmt_multiqueuethreadpool.h:1067
void numProcessed(int *numExecuted, int *numEnqueued, int *numDeleted=0) const
Definition bdlmt_multiqueuethreadpool.h:1223
int deleteQueue(int id, const CleanupFunctor &cleanupFunctor)
bool isEnabled(int id) const
Definition bdlmt_multiqueuethreadpool.h:1173
friend class MultiQueueThreadPool_Queue
Definition bdlmt_multiqueuethreadpool.h:655
int numQueues() const
Definition bdlmt_multiqueuethreadpool.h:1238
BSLMF_NESTED_TRAIT_DECLARATION(MultiQueueThreadPool, bslma::UsesBslmaAllocator)
bsl::map< int, MultiQueueThreadPool_Queue * > QueueRegistry
Definition bdlmt_multiqueuethreadpool.h:669
MultiQueueThreadPool(ThreadPool *threadPool, bslma::Allocator *basicAllocator=0)
bsl::function< void()> CleanupFunctor
Definition bdlmt_multiqueuethreadpool.h:668
Definition bdlmt_threadpool.h:461
int enabled() const
Return the state (enabled or not) of the thread pool.
Definition bdlmt_threadpool.h:798
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements contained by this deque.
Definition bslstl_deque.h:2241
Definition bslstl_deque.h:814
Forward declaration.
Definition bslstl_function.h:946
Definition bslstl_map.h:653
BloombergLP::bslstl::TreeIterator< const value_type, Node, difference_type > const_iterator
Definition bslstl_map.h:758
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3308
iterator find(const key_type &key)
Definition bslstl_map.h:1885
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements in this map.
Definition bslstl_map.h:4039
BloombergLP::bslstl::TreeIterator< value_type, Node, difference_type > iterator
Definition bslstl_map.h:756
Definition bslstl_vector.h:1120
Definition bslma_allocator.h:545
Definition bslmf_movableref.h:752
Definition bslmt_condition.h:220
Definition bslmt_latch.h:349
Definition bslmt_lockguard.h:234
Definition bslmt_mutex.h:317
Definition bslmt_readlockguard.h:287
Definition bslmt_readerwritermutex.h:244
Definition bslmt_threadattributes.h:361
Definition bslmt_writelockguard.h:221
Definition bsls_atomic.h:744
int swap(int swapValue)
Definition bsls_atomic.h:1711
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlmt_eventscheduler.h:550
bsl::function< void(void *, bslma::Allocator *)> DefaultCreator
Definition bdlcc_objectpool.h:421
Definition bslma_usesbslmaallocator.h:344
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1067
Imp::Handle Handle
Definition bslmt_threadutil.h:389