BDE 4.14.0 Production release
Loading...
Searching...
No Matches
balb_pipetaskmanager.h
Go to the documentation of this file.
1/// @file balb_pipetaskmanager.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// balb_pipetaskmanager.h -*-C++-*-
8#ifndef INCLUDED_BALB_PIPETASKMANAGER
9#define INCLUDED_BALB_PIPETASKMANAGER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup balb_pipetaskmanager balb_pipetaskmanager
15/// @brief Provide a pipe-based mechanism to process task control messages.
16/// @addtogroup bal
17/// @{
18/// @addtogroup balb
19/// @{
20/// @addtogroup balb_pipetaskmanager
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#balb_pipetaskmanager-purpose"> Purpose</a>
25/// * <a href="#balb_pipetaskmanager-classes"> Classes </a>
26/// * <a href="#balb_pipetaskmanager-description"> Description </a>
27/// * <a href="#balb_pipetaskmanager-configuring-the-balbl-pipetaskmanager"> Configuring the balbl::PipeTaskManager </a>
28/// * <a href="#balb_pipetaskmanager-thread-safety"> Thread Safety </a>
29/// * <a href="#balb_pipetaskmanager-requirements-for-the-named-pipe"> Requirements for the Named Pipe </a>
30/// * <a href="#balb_pipetaskmanager-message-requirements"> Message Requirements </a>
31/// * <a href="#balb_pipetaskmanager-pipe-atomicity"> Pipe Atomicity </a>
32/// * <a href="#balb_pipetaskmanager-usage"> Usage </a>
33/// * <a href="#balb_pipetaskmanager-example-1-basic-usage"> Example 1: Basic Usage </a>
34///
35/// # Purpose {#balb_pipetaskmanager-purpose}
36/// Provide a pipe-based mechanism to process task control messages.
37///
38/// # Classes {#balb_pipetaskmanager-classes}
39///
40/// - balb::PipeTaskManager: message-to-handler dispatcher
41///
42/// @see balb_controlmanager, balb_pipecontrolchannel
43///
44/// # Description {#balb_pipetaskmanager-description}
45/// This component provides a mechanism, `balb::PipeTaskManager`,
46/// that listens on a named pipe for messages that are typically used to
47/// influence the behavior of a (running) task.
48///
49/// For example, a `balb::PipeTaskManager` might be configured to listen on a
50/// well known named pipe (e.g., `myapplication.ctrl`), for the control messages
51/// starting with:
52/// * "EXIT",
53/// * "RESTART",
54/// * "LOG", and
55/// * "HELP".
56///
57/// The use of imperative verbs for the first field of a message is a common
58/// practice. The first field is called the message "prefix". On receipt of a
59/// message with a known prefix, a previously registered handler functor is
60/// invoked with two arguments:
61/// 1. the prefix value, and
62/// 2. an `bsl::istream` from which the rest of the message (if any) can be
63/// read.
64/// Thus, we have a mechanism by which a running task can be sent commands and,
65/// optionally, arguments for those commands.
66///
67/// Once the relationship between `prefix` and handler has been specified, the
68/// `start` method is used to create the named pipe (or re-open an existing
69/// named pipe) and a thread created to listen for messages.
70///
71/// A human user on a console might then use a command line application to send
72/// control messages to the `myapplication.ctrl` pipe to configure the behavior
73/// of the running task. In the example above, the handler for the `LOG` prefix
74/// expects additional parameters. Thus:
75/// @code
76/// echo "LOG VERBOSITY 4" > $SOCKDIR/myapplication.ctrl
77/// @endcode
78/// changes the logging verbosity of the task to level "4". See
79/// @ref bdls_pipeutil for functions that can be invoked from C++ code to send
80/// messages to a named pipe.
81///
82/// ## Configuring the balbl::PipeTaskManager {#balb_pipetaskmanager-configuring-the-balbl-pipetaskmanager}
83///
84///
85/// A default constructed `balbl::PipeTaskManager` has no registered handlers.
86/// Users can use the exposed `balb::ControlManager`, to register different
87/// control message prefixes (typically "verbs") to dispatch received messages
88/// to an appropriate functor.
89///
90/// Alternatively, one can construct a `balb::PipeTaskManager` using a
91/// separately created and configured a `balb::ControlManager` object. Doing so
92/// allows that single `balb::ControlManager` to be shared among several
93/// `balb::PipeTaskManagrer` objects, each listening on a different named pipe.
94///
95/// ## Thread Safety {#balb_pipetaskmanager-thread-safety}
96///
97///
98/// This component is thread-safe but not thread-enabled, meaning that multiple
99/// threads may safely use their own instances of `balb::PipeTaskManager`, but
100/// may not manipulate the same instance of `balb::PipeTaskManager`
101/// simultaneously. Note that the contained `balb::ControlManager` object is
102/// available via both `const` and non=`const` references and that object *is*
103/// safe for multiple threads.
104///
105/// ## Requirements for the Named Pipe {#balb_pipetaskmanager-requirements-for-the-named-pipe}
106///
107///
108/// The `balb::PipeTaskManger` objects waits for messages from a named pipe
109/// provided to the `start` method. The argument to `start` -- mapped to all
110/// lower case, if needed -- determines the basename of the named pipe. The
111/// directory of that named pipe depends on the platform and environment
112/// variables.
113///
114/// * On Windows, that directory is: "\\.\pipe\".
115/// * On Unix systems, that directory is determined by
116/// - the `SOCKDIR` environment variable, if set; otherwise,
117/// - the `TMPDIR` environment variable, if set; otherwise,
118/// - the current directory.
119///
120/// See the `makeCanonicalName` overloads in @ref bdls_pipeutil for details.
121///
122/// Moreover, the `start` method must be able to *freshly* create a named pipe.
123/// In general, `start` will fail if a named pipe of the calculated canonical
124/// name already exists. On Unix, if that named pipe is not in use (not open
125/// for reading), the `start` attempts to remove and re-create that named pipe.
126///
127/// On Unix systems, named pipes are created having the permission `0666` (read
128/// and write for user, group, and other) limited by the current `umask` value
129/// of the process.
130///
131/// On successful completion of `start`, the (full) pathname of the created
132/// named pipe is provided by the `pipeName` accessor. The full path name must
133/// be passed to sending processes so they can open that named pipe and write
134/// control messages.
135///
136/// ## Message Requirements {#balb_pipetaskmanager-message-requirements}
137///
138///
139/// Each message consists of a sequence of fields separated by blanks and/or
140/// tabs and terminated by a newline ('\n') character. The terminating newline
141/// is not passed to the message handler.
142///
143/// The first field is called the message "prefix" and is used to find a
144/// previously registered handler for the message. The handler lookup is case
145/// insensitive. Empty messages (newline only) and messages for which no
146/// handler can be found are silently ignored.
147///
148/// Note that this facility provides a one-way flow of information from the
149/// writer to a named pipe to the registered message handler. There is no
150/// mechanism here for validating message (e.g., a given prefix has required
151/// additional fields) or returning status. Many applications provide output by
152/// writing to the console or to a log.
153///
154/// ### Pipe Atomicity {#balb_pipetaskmanager-pipe-atomicity}
155///
156///
157/// Users that expect multiple concurrent writers to a single pipe must be aware
158/// that the message content might be corrupted (interleaved) unless:
159///
160/// 1. Each message is written to the pipe in a single `write` system call.
161/// 2. The length of each message is less than `PIPE_BUF` (the limit for
162/// guaranteed atomicity).
163///
164/// The value `PIPE_BUF` depends on the platform:
165/// @code
166/// +------------------------------+------------------+
167/// | Platform | PIPE_BUF (bytes) |
168/// +------------------------------+------------------+
169/// | POSIX (minimum requirement)) | 512 |
170/// | IBM | 32,768 |
171/// | SUN | 32,768 |
172/// | Linux | 65,536 |
173/// | Windows | 65,536 |
174/// +------------------------------+------------------+
175/// @endcode
176/// Also note that Linux allows the `PIPE_BUF` size to be changed via the
177/// `fcntl` system call.
178///
179/// ## Usage {#balb_pipetaskmanager-usage}
180///
181///
182/// This section illustrates intended use of this component.
183///
184/// ### Example 1: Basic Usage {#balb_pipetaskmanager-example-1-basic-usage}
185///
186///
187/// Suppose one is creating an application that allows for dynamically changing
188/// its logging verbosity level, resetting to its initial state, to shutdown
189/// cleanly, and the listing a description of supported messages.
190///
191/// The `balb::PipeTaskManager` class can be used to provide support for
192/// messages that are sent via a named pipe and have the syntax shown below:
193/// @code
194/// This process responds to the following messages:
195/// EXIT no arguments
196/// Terminate the application.
197/// HELP
198/// Display this message
199/// LOG <GET|SET <level> >
200/// Get/set verbosity level.
201/// RESTART no arguments
202/// Restart the application.
203/// @endcode
204/// Note that the above description corresponds to the output produced by our
205/// application in response to a "HELP" message.
206///
207/// First, define several global, atomic variables that will be used to exchange
208/// information between the thread that monitors the named pipe and the other
209/// threads of the application.
210/// @code
211/// static bsls::AtomicBool done(false);
212/// static bsls::AtomicInt progress(0);
213/// static bsls::AtomicInt myLoggingManagerLevel(0);
214/// @endcode
215/// Then, we define helper functions `myLoggingManagerGet` and
216/// `myLoggingManagerSet` so that the handler for "LOG" messages can delegate
217/// processing the "GET" and "SET" subcommands. The other defined messages have
218/// minimal syntax so use of a delegation pattern is overkill in those cases.
219/// @code
220/// void myLoggingManagerGet()
221/// // Print the current log level to the console.
222/// {
223/// bsl::cout << "LOG LEVEL IS NOW" << ": "
224/// << myLoggingManagerLevel << bsl::endl;
225/// }
226///
227/// void myLoggingManagerSet(bsl::istream& message)
228/// // Set the log level to the value obtained from the specified
229/// // 'message' and print that value to the console.
230/// {
231/// int newLogLevel;
232/// message >> newLogLevel; // Cannot stream to an 'bsls::AtomicInt'.
233///
234/// myLoggingManagerLevel = newLogLevel;
235///
236/// bsl::cout << "LOG LEVEL SET TO" << ": "
237/// << myLoggingManagerLevel << bsl::endl;
238/// }
239/// @endcode
240/// Next, define handler functions for the "EXIT", "RESTART", and "LOG"
241/// messages.
242/// @code
243/// void onExit(const bsl::string_view& , bsl::istream& )
244/// // Handle a "EXIT" message.
245/// {
246/// bsl::cout << "onExit" << bsl::endl;
247/// done = true;
248/// }
249///
250/// void onRestart(const bsl::string_view& , bsl::istream& )
251/// // Handle a "RESTART" message.
252/// {
253/// bsl::cout << "onRestart" << bsl::endl;
254/// progress = 0;
255/// }
256///
257/// void onLog(const bsl::string_view& , bsl::istream& message)
258/// // Handle a "LOG" message supporting sub command "GET" and "SET". If
259/// // the subcommand is "SET" the new log level is obtained from the
260/// // specified 'message'.
261/// {
262/// bsl::cout << "onLog" << bsl::endl;
263///
264/// bsl::string subCommand;
265/// message >> subCommand;
266/// // See the registration of the 'onLog' handler below for the
267/// // details of the supported sub commands and their arguments.
268///
269/// if ("GET" == subCommand) {
270/// myLoggingManagerGet();
271/// }
272/// else if ("SET" == subCommand) {
273/// myLoggingManagerSet(message);
274/// }
275/// else {
276/// bsl::cout << "onLog" << ": "
277/// << "unknown subcommand" << ": "
278/// << subCommand << bsl::endl;
279/// }
280/// }
281/// @endcode
282/// Notice that no handler is yet defined for the "HELP" message. That
283/// functionally can be provided using methods of the contained
284/// `balb::ControlManager` object. See below.
285///
286/// Then, create a `balb::PipeTaskManger` object and register the above
287/// functions as handlers:
288/// @code
289/// int myApplication1()
290/// // Run Application1 and return status;
291/// {
292/// balb::PipeTaskManager taskManager;
293///
294/// int rc;
295///
296/// rc = taskManager.controlManager().registerHandler(
297/// "EXIT",
298/// "no arguments",
299/// "Terminate the application.",
300/// onExit);
301/// assert(0 == rc);
302/// rc = taskManager.controlManager().registerHandler(
303/// "RESTART",
304/// "no arguments",
305/// "Restart the application.",
306/// onRestart);
307/// assert(0 == rc);
308/// rc = taskManager.controlManager().registerHandler(
309/// "LOG",
310/// "<GET|SET <level> >",
311/// "Get/set verbosity level.",
312/// onLog);
313/// assert(0 == rc);
314/// @endcode
315/// and add an additional handler that provides a list of the registered
316/// messages and the syntax for using them:
317/// @code
318/// rc = taskManager.controlManager().registerUsageHandler(bsl::cout);
319/// assert(0 == rc);
320/// @endcode
321/// Next, if we are on a Unix system, we confirm that our named pipes will be
322/// created in the directory named by the `TMPDIR` environment variable:
323/// @code
324/// #if defined(BSLS_PLATFORM_OS_UNIX)
325/// rc = unsetenv("SOCKDIR"); // 'SOCKDIR' has precedence over 'TMPDIR'.
326/// assert(0 == rc);
327///
328/// const char *expectedDirectory = getenv("TMPDIR");
329/// #elif defined(BSLS_PLATFORM_OS_WINDOWS)
330/// const char *expectedDirectory = "\\\\.\\pipe\\";
331/// #else
332/// #error "Unexpected platform."
333/// #endif
334/// @endcode
335/// Then, start listening for incoming messages at pipe having the name based on
336/// on the name "MyApplication.CTRL".
337/// @code
338/// rc = taskManager.start("MyApplication.CTRL");
339/// assert(0 == rc);
340/// @endcode
341/// Next, for expository purposes, confirm that a pipe of that name exists in
342/// the expected directory and is open for reading.
343/// @code
344/// const bsl::string_view pipeName = taskManager.pipeName();
345///
346/// assert(bdls::PathUtil::isAbsolute (pipeName));
347/// #ifdef BSLS_PLATFORM_OS_UNIX
348/// assert(bdls::PipeUtil::isOpenForReading(pipeName));
349/// #endif
350///
351/// bsl::string canonicalDirname, canonicalLeafName;
352///
353/// bdls::PathUtil::getDirname(&canonicalDirname, pipeName);
354/// bdls::PathUtil::getLeaf (&canonicalLeafName, pipeName);
355///
356/// assert(0 == bsl::strcmp(expectedDirectory, canonicalDirname.c_str()));
357/// assert("myapplication.ctrl" == canonicalLeafName);
358/// @endcode
359/// Notice that given `baseName` has been canonically converted to lowercase.
360///
361/// Now, our application can continue doing useful work while the background
362/// thread monitors the named pipe for incoming messages:
363/// @code
364/// while (!done) {
365/// // Do useful work while background thread responds to incoming
366/// // commands from the named pipe.
367/// }
368///
369/// return 0;
370/// }
371/// @endcode
372/// Finally, in some other programming context, say `mySender`, a context in
373/// another process that has been passed the value of `pipeName`, control
374/// messages can be sent to `myApplication1` above.
375/// @code
376/// void mySender(const bsl::string& pipeName)
377/// // Write control messages into the pipe named by the specified
378/// // 'pipeName'.
379/// {
380/// int rc;
381/// rc = bdls::PipeUtil::send(pipeName, "LoG GET\n");
382/// assert(0 == rc);
383/// rc = bdls::PipeUtil::send(pipeName, "Log SET 4\n");
384/// assert(0 == rc);
385/// rc = bdls::PipeUtil::send(pipeName, "log GET\n");
386/// assert(0 == rc);
387/// rc = bdls::PipeUtil::send(pipeName, "\n"); // empty
388/// assert(0 == rc);
389/// rc = bdls::PipeUtil::send(pipeName, "RESET\n"); // invalid
390/// assert(0 == rc);
391/// rc = bdls::PipeUtil::send(pipeName, "RESTART\n");
392/// assert(0 == rc);
393/// rc = bdls::PipeUtil::send(pipeName, "EXIT\n");
394/// assert(0 == rc);
395/// }
396/// @endcode
397/// Notice that:
398///
399/// * Each message must be terminated by a newline character.
400/// * Although each registered message prefix was all capital letters, the
401/// prefix field in the sent message is case insensitive -- "LoG", "Log", and
402/// "log" all invoke the intended handler. If we wanted case insensitivity
403/// for the subcommands "GET" and "SET" we would change of implementation of
404/// `onLog` accordingly.
405/// * The empty message and the unregistered "RESET" message are silently
406/// ignored. The console output (see below) shows no indication that these
407/// were sent.
408///
409/// The console log of our application shows the response for each received
410/// control message. In general, these messages will be interleaved with the
411/// output of the "useful work" done in the `for` loop of 'myApplicaton1.
412/// @code
413/// onLog
414/// LOG LEVEL IS NOW: 0
415/// onLog
416/// LOG LEVEL SET TO: 4
417/// onLog
418/// LOG LEVEL IS NOW: 4
419/// onRestart
420/// onExit
421/// @endcode
422/// @}
423/** @} */
424/** @} */
425
426/** @addtogroup bal
427 * @{
428 */
429/** @addtogroup balb
430 * @{
431 */
432/** @addtogroup balb_pipetaskmanager
433 * @{
434 */
435
436#include <balscm_version.h>
437
438#include <balb_controlmanager.h>
440
441#include <bslma_allocator.h>
442
444
445#include <bsls_assert.h>
446
447#include <bsl_memory.h> // 'bsl::shared_ptr' 'bsl::allocate_shared'
448#include <bsl_string.h>
449#include <bsl_string_view.h>
450
451
452namespace balb {
453
454 // =====================
455 // class PipeTaskManager
456 // =====================
457
458/// This class provides a mechanism route messages received on a named pipe
459/// to the registered handler (functor).
460///
461/// See @ref balb_pipetaskmanager
463
464 // DATA
465 bslma::Allocator *d_allocator_p; // allocator (held)
466 PipeControlChannel *d_controlChannel_p; // message IPC mech.
467 bsl::shared_ptr<ControlManager> d_controlManager_p; // callback registry
468
469 private:
470 // NOT IMPLEMENTED
471 PipeTaskManager(const PipeTaskManager& ); // = delete
472 PipeTaskManager& operator=(const PipeTaskManager& ); // = delete
473
474 public:
475 // TRAITS
478
479 // CREATORS
480
481 /// Create a task manager having no message handlers. Optionally
482 /// specify a `basicAllocator` used to supply memory. If
483 /// `basicAllocator` is 0, the currently installed default allocator is
484 /// used. Message handlers can be supplied using the return value of
485 /// the `controlManager` method. If that return value is used to create
486 /// other `PipeTaskManager` objects, this object (the owner of the
487 /// internal `ControlManager` must outlive those other objects. The
488 /// `start` method must be called successfully before messages will be
489 /// received (on a separate thread created by `start`). Note that the
490 /// name of the message pipe is supplied as an argument to `start`.
491 explicit PipeTaskManager(bslma::Allocator *basicAllocator = 0);
492
493 /// Create a task manager that uses the handlers of the specified shared
494 /// `controlManager`. Optionally specify a `basicAllocator` used to
495 /// supply memory. If `basicAllocator` is 0, the currently installed
496 /// default allocator is used. The `start` method must be called
497 /// successfully before messages will be received (on a separate thread
498 /// created by `start`). Note that the name of the message pipe is
499 /// supplied as an argument to `start`. Also note that the handlers of
500 /// `controlManager` can be manipulated via the return value of the
501 /// `controlManager` method. Finally note that the allocator of
502 /// `controlManger` need not equal `basicAllocator`.
503 explicit
505 bslma::Allocator *basicAllocator = 0);
506
507 /// Shutdown message handling, and release the shared reference to the
508 /// `controlManager` (destroying the `ConstrolManager` and its message
509 /// handlers if this is the last shared reference), and destroy this
510 /// object.
512
513 // MANIPULATORS
514
515 /// Return a non-`const` reference to the `ControlManager` object of
516 /// this `PipeTaskManager`.
518
519// BDE_VERIFY pragma: -FABC01 // not in alphanumeric order
520
521 /// Create a named pipe using the specified `pipeBasename` (not a
522 /// pathname), and execute the task manager event processor in a
523 /// background thread. Return 0 on success, and a non-zero value
524 /// otherwise. See {Requirements for the Named Pipe} for expectations
525 /// for `pipeBasename`. After a successful return, calls to `start`
526 /// fail until `stop` has been called (successfully); afterwards,
527 /// `start` can be called again with the same or some other
528 /// `pipeBasename`. Note that the `pipeName()` method provides the
529 /// (full) pathname to the created named pipe.
530 int start(const bsl::string_view& pipeBasename);
531
532 /// Stop processing incoming messages by the background thread. If a
533 /// handler is executing at the time of invocation, that handler is
534 /// allowed to complete. This method does not block the caller, and the
535 /// background thread persists until it is joined by a call to `stop`.
536 /// Note that `shutdown` and `stop` are typically called in succession.
537 void shutdown();
538
539 /// Block until the background processing thread has shutdown (which
540 /// must be initiated by a separate call to `shutdown`) then join the
541 /// background thread, remove the named pipe, and return. Return 0 on
542 /// success, and a non-zero value otherwise. If `shutdown` has not been
543 /// called, this method will block indefinitely until another thread
544 /// calls `shutdown`. Once `stop` has returned, `start` can be called
545 /// again with the same or a different named pipe. Note that frequently
546 /// user code will call `shutdown` and then immediately call `stop` on a
547 /// `TaskManager` object.
548 int stop();
549
550// BDE_VERIFY pragma: +FABC01 // not in alphanumeric order
551
552 // ACCESSORS
553
554 /// Return a `const` reference to the `ControlManager` object of this
555 /// `PipeTaskManager`.
557
558 /// Return the path of the named pipe used by the implementation. The
559 /// behavior is undefined unless the task manager has been started.
560 const bsl::string& pipeName() const;
561
562 // Aspects
563
564 /// Return the allocator used by this object to supply memory. Note
565 /// that if no allocator was supplied at construction the default
566 /// allocator in effect at construction is used.
568};
569
570// ============================================================================
571// INLINE DEFINITIONS
572// ============================================================================
573
574 // ---------------------
575 // class PipeTaskManager
576 // ---------------------
577
578// MANIPULATORS
579inline
581{
582 return *d_controlManager_p;
583}
584
585inline
587{
588 d_controlChannel_p->shutdown();
589}
590
591inline
593{
594 d_controlChannel_p->stop();
595 return 0;
596}
597
598// ACCESSORS
599inline
601{
602 return *d_controlManager_p;
603}
604
605inline
607{
608 BSLS_ASSERT(d_controlChannel_p);
609
610 return d_controlChannel_p->pipeName();
611}
612
613 // Aspects
614
615inline
617{
618 return d_allocator_p;
619}
620
621} // close package namespace
622
623
624#endif
625
626// ----------------------------------------------------------------------------
627// Copyright 2023 Bloomberg Finance L.P.
628//
629// Licensed under the Apache License, Version 2.0 (the "License");
630// you may not use this file except in compliance with the License.
631// You may obtain a copy of the License at
632//
633// http://www.apache.org/licenses/LICENSE-2.0
634//
635// Unless required by applicable law or agreed to in writing, software
636// distributed under the License is distributed on an "AS IS" BASIS,
637// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
638// See the License for the specific language governing permissions and
639// limitations under the License.
640// ----------------------------- END-OF-FILE ----------------------------------
641
642/** @} */
643/** @} */
644/** @} */
Definition balb_controlmanager.h:142
Definition balb_pipecontrolchannel.h:295
const bsl::string & pipeName() const
Return the fully qualified system name of the pipe.
Definition balb_pipecontrolchannel.h:518
Definition balb_pipetaskmanager.h:462
bslma::Allocator * allocator() const
Definition balb_pipetaskmanager.h:616
int stop()
Definition balb_pipetaskmanager.h:592
void shutdown()
Definition balb_pipetaskmanager.h:586
int start(const bsl::string_view &pipeBasename)
BSLMF_NESTED_TRAIT_DECLARATION(PipeTaskManager, bslma::UsesBslmaAllocator)
PipeTaskManager(bslma::Allocator *basicAllocator=0)
PipeTaskManager(bsl::shared_ptr< ControlManager > &controlManager, bslma::Allocator *basicAllocator=0)
const bsl::string & pipeName() const
Definition balb_pipetaskmanager.h:606
balb::ControlManager & controlManager()
Definition balb_pipetaskmanager.h:580
Definition bslstl_stringview.h:441
Definition bslstl_string.h:1281
Definition bslstl_sharedptr.h:1830
Definition bslma_allocator.h:457
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition balb_controlmanager.h:133
Definition bslma_usesbslmaallocator.h:343