BDE 4.14.0 Production release
Loading...
Searching...
No Matches
balb_pipecontrolchannel.h
Go to the documentation of this file.
1/// @file balb_pipecontrolchannel.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// balb_pipecontrolchannel.h -*-C++-*-
8#ifndef INCLUDED_BALB_PIPECONTROLCHANNEL
9#define INCLUDED_BALB_PIPECONTROLCHANNEL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup balb_pipecontrolchannel balb_pipecontrolchannel
15/// @brief Provide a mechanism for reading control messages from a named pipe.
16/// @addtogroup bal
17/// @{
18/// @addtogroup balb
19/// @{
20/// @addtogroup balb_pipecontrolchannel
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#balb_pipecontrolchannel-purpose"> Purpose</a>
25/// * <a href="#balb_pipecontrolchannel-classes"> Classes </a>
26/// * <a href="#balb_pipecontrolchannel-description"> Description </a>
27/// * <a href="#balb_pipecontrolchannel-thread-safety"> Thread Safety </a>
28/// * <a href="#balb_pipecontrolchannel-pipe-atomicity"> Pipe Atomicity </a>
29/// * <a href="#balb_pipecontrolchannel-pipe-names"> Pipe Names </a>
30/// * <a href="#balb_pipecontrolchannel-message-format"> Message Format </a>
31/// * <a href="#balb_pipecontrolchannel-platform-specific-pipe-name-encoding-caveats"> Platform-Specific Pipe Name Encoding Caveats </a>
32/// * <a href="#balb_pipecontrolchannel-usage"> Usage </a>
33/// * <a href="#balb_pipecontrolchannel-example-1-controlling-a-simple-server"> Example 1: Controlling a Simple Server </a>
34///
35/// # Purpose {#balb_pipecontrolchannel-purpose}
36/// Provide a mechanism for reading control messages from a named pipe.
37///
38/// # Classes {#balb_pipecontrolchannel-classes}
39///
40/// - balb::PipeControlChannel: Mechanism for reading messages from a named pipe
41///
42/// @see bdls_pipeutil
43///
44/// # Description {#balb_pipecontrolchannel-description}
45/// This component provides a platform-independent mechanism,
46/// `balb::PipeControlChannel`, for establishing, monitoring, and shutting down
47/// a named pipe. It reads text messages (generally short operational commands)
48/// from the pipe and passes them to a callback function. Note that this
49/// component does *not* provide a general transport mechanism over a named
50/// pipe: it is specialized for the case that the messages to be read are
51/// relatively short newline-terminated strings.
52///
53/// ## Thread Safety {#balb_pipecontrolchannel-thread-safety}
54///
55///
56/// This component is thread-safe but not thread-enabled, meaning that multiple
57/// threads may safely use their own instances of PipeControlChannel, but may
58/// not manipulate the same PipeControlChannel simultaneously (except that
59/// `shutdown` may always be called safely). The `start` function creates a
60/// new thread which listens to the pipe for messages until `shutdown` is
61/// called.
62///
63/// ### Pipe Atomicity {#balb_pipecontrolchannel-pipe-atomicity}
64///
65///
66/// Users that expect multiple concurrent writers to a single pipe must be aware
67/// that the message content might be corrupted (interleaved) unless:
68///
69/// 1. Each message is written to the pipe in a single `write` system call.
70/// 2. The length of each message is less than `PIPE_BUF` (the limit for
71/// guaranteed atomicity).
72///
73/// The value `PIPE_BUF` depends on the platform:
74/// @code
75/// +------------------------------+------------------+
76/// | Platform | PIPE_BUF (bytes) |
77/// +------------------------------+------------------+
78/// | POSIX (minimum requirement)) | 512 |
79/// | IBM | 32,768 |
80/// | SUN | 32,768 |
81/// | Linux | 65,536 |
82/// | Windows | 65,536 |
83/// +------------------------------+------------------+
84/// @endcode
85///
86/// Also note that Linux allows the `PIPE_BUF` size to be changed via the
87/// `fcntl` system call.
88///
89/// ## Pipe Names {#balb_pipecontrolchannel-pipe-names}
90///
91///
92/// This component requires a fully-qualified native pipe name.
93/// `bdlsu::PipeUtil` provides a portable utility method to generate such names.
94///
95/// ## Message Format {#balb_pipecontrolchannel-message-format}
96///
97///
98/// This component requires a trailing newline ('\n') character at the end of
99/// each message. This trailing newline is stripped from the message before
100/// the message is passed to the control callback.
101///
102/// ## Platform-Specific Pipe Name Encoding Caveats {#balb_pipecontrolchannel-platform-specific-pipe-name-encoding-caveats}
103///
104///
105/// Pipe-name encodings have the following caveats for the following operating
106/// systems:
107///
108/// * On Windows, methods of `balb::PipeControlChannel` that take or return a
109/// pipe name as `bsl::string` (or a reference to it) type assume that the
110/// name is encoded in UTF-8. The routines attempt to convert the name to a
111/// UTF-16 `wchar_t` string via `bdlde::CharConvertUtf16::utf8ToUtf16`, and
112/// if the conversion succeeds, call the Windows wide-character `W` APIs with
113/// the UTF-16 name. If the conversion fails, the method fails.
114/// - Narrow-character pipe names in other encodings, containing characters
115/// with values in the range 128 - 255, will likely result in pipes being
116/// created with names that appear garbled if the conversion from UTF-8 to
117/// UTF-16 happens to succeed.
118/// - Neither `utf8ToUtf16` nor the Windows `W` APIs do any normalization of
119/// the UTF-16 strings resulting from UTF-8 conversion, and it is therefore
120/// possible to have sets of pipe names that have the same visual
121/// representation but are treated as different names by the system.
122/// * On Posix, a pipe name supplied to methods of `balb::PipeControlChannel`
123/// as `bsl::string` type is passed unchanged to the underlying system file
124/// APIs. Because the pipe names are passed unchanged,
125/// `balb::PipeControlChannel` methods will work correctly on Posix with any
126/// encoding, but will *interoperate* only with processes that use the same
127/// encoding as the current process.
128/// * For compatibility with most modern Posix installs, and consistency with
129/// this component's Windows API, best practice is to encode all pipe names
130/// in UTF-8.
131///
132/// ## Usage {#balb_pipecontrolchannel-usage}
133///
134///
135/// This section illustrates intended use of this component.
136///
137/// ### Example 1: Controlling a Simple Server {#balb_pipecontrolchannel-example-1-controlling-a-simple-server}
138///
139///
140/// This example illustrates how to construct a simple server that records
141/// messages sent on a pipe control channel until "EXIT" is received, at which
142/// point the channel is closed and the server stops.
143///
144/// First, let's define the implementation of our server.
145/// @code
146/// // ===================
147/// // class ControlServer
148/// // ===================
149///
150/// class ControlServer {
151///
152/// // DATA
153/// balb::PipeControlChannel d_channel;
154/// bsl::vector<bsl::string> d_messages;
155///
156/// // PRIVATE MANIPULATORS
157/// void onMessage(const bslstl::StringRef& message)
158/// {
159/// if ("EXIT" != message) {
160/// d_messages.push_back(message);
161/// }
162/// else {
163/// shutdown();
164/// }
165/// }
166///
167/// private:
168/// // NOT IMPLEMENTED
169/// ControlServer(const ControlServer&); // = delete
170/// ControlServer& operator=(const ControlServer&); // = delete
171///
172/// public:
173/// // CREATORS
174/// explicit ControlServer(bslma::Allocator *basicAllocator = 0)
175/// : d_channel(bdlf::BindUtil::bind(&ControlServer::onMessage,
176/// this,
177/// bdlf::PlaceHolders::_1),
178/// basicAllocator)
179/// , d_messages(basicAllocator)
180/// {}
181///
182/// // MANIPULATORS
183/// int start(const bslstl::StringRef& pipeName)
184/// {
185/// return d_channel.start(pipeName);
186/// }
187///
188/// void shutdown()
189/// {
190/// d_channel.shutdown();
191/// }
192///
193/// void stop()
194/// {
195/// d_channel.stop();
196/// }
197///
198/// // ACCESSORS
199/// bsl::size_t numMessages() const
200/// {
201/// return d_messages.size();
202/// }
203///
204/// const bsl::string& message(int index) const
205/// {
206/// return d_messages[index];
207/// }
208/// };
209/// @endcode
210/// Now, construct and run the server using a canonical name for the pipe:
211/// @code
212/// bsl::string pipeName;
213/// int rc = bdls::PipeUtil::makeCanonicalName(&pipeName,
214/// "ctrl.pcctest");
215/// assert(0 == rc);
216///
217/// ControlServer server;
218///
219/// rc = server.start(pipeName);
220/// if (0 != rc) {
221/// cout << "ERROR: Failed to start pipe control channel" << endl;
222/// }
223/// @endcode
224/// Once the server is started, clients can send messages to the server.
225/// @code
226/// const char MSG0[] = "this is the first message";
227/// const char MSG1[] = "this is the second message";
228///
229/// rc = bdls::PipeUtil::send(pipeName, bsl::string(MSG0) + "\n");
230/// assert(0 == rc);
231/// rc = bdls::PipeUtil::send(pipeName, bsl::string(MSG1) + "\n");
232/// assert(0 == rc);
233/// rc = bdls::PipeUtil::send(pipeName, "EXIT\n");
234/// assert(0 == rc);
235/// @endcode
236/// The server shuts down once it processes the "EXIT" control message.
237/// @code
238/// server.stop(); // block until shutdown
239/// @endcode
240/// Finally, let's ensure the server received each control message sent.
241/// @code
242/// assert(2 == server.numMessages());
243/// assert(bsl::string(MSG0) == server.message(0));
244/// assert(bsl::string(MSG1) == server.message(1));
245/// @endcode
246/// @}
247/** @} */
248/** @} */
249
250/** @addtogroup bal
251 * @{
252 */
253/** @addtogroup balb
254 * @{
255 */
256/** @addtogroup balb_pipecontrolchannel
257 * @{
258 */
259
260#include <balscm_version.h>
261
263#include <bslmt_threadutil.h>
264
265#include <bslmf_assert.h>
266
267#include <bsls_atomic.h>
268#include <bsls_libraryfeatures.h>
269
270#include <bsl_functional.h>
271#include <bsl_string.h>
272#include <bsl_vector.h>
273
274#include <bslma_allocator.h>
275
276#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
277#include <bslalg_typetraits.h>
278#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
279
280#include <string> // 'std::string', 'std::pmr::string'
281
282
283namespace balb {
284 // ========================
285 // class PipeControlChannel
286 // ========================
287
288/// This class is a mechanism for reading control messages from a named
289/// pipe. Use `start` to spawn a thread that handles messages arriving at
290/// the pipe, and `shutdown` to stop reading. `stop` blocks until the
291/// processing thread has been terminated by a call to `shutdown` (either
292/// before `stop` is called, or from some other thread).
293///
294/// See @ref balb_pipecontrolchannel
296
297 public:
298 // TYPES
299
300 /// This type of function is called to handle control messages received
301 /// on the pipe. The `message` is one complete message read from the
302 /// pipe, without the terminating newline character.
303 typedef bsl::function<void(const bslstl::StringRef& message)>
305
306 private:
307 // TYPES
308 enum BackgroundThreadState {
309 e_STOPPED, // The background thread is not running or about to exit.
310 e_RUNNING, // The background thread is running normally.
311 e_STOPPING // The background thread is requested to stop.
312 };
313
314 // INSTANCE DATA
315 ControlCallback d_callback; // callback for control messages
316 bsl::string d_pipeName; // full path name of pipe
317 bsl::vector<char> d_buffer; // message buffer
318 bslmt::ThreadUtil::Handle d_thread; // background processing thread
319 bsls::AtomicInt d_backgroundState; // the background thread state
320 bool d_isPipeOpen; // true if the pipe is still open
321
322 union {
323 struct {
324 int d_readFd; // fifo descriptor (read-only)
325 int d_writeFd; // fifo descriptor (write-only)
327
328 struct {
329 void* d_handle; // pipe handle
331
332 } d_impl; // platform-specific imp
333
334 // PRIVATE MANIPULATORS
335
336 /// Loop while d_isRunningFlag is true, reading and dispatching messages
337 /// from the named pipe.
338 void backgroundProcessor();
339
340 /// Open a named pipe having the specified `pipeName`. Return 0 on
341 /// success, and a non-zero value otherwise.
342 int createNamedPipe(const char *pipeName);
343
344 /// Dispatch the message that extends up to (but not including) the
345 /// specified `iter` (which is an iterator into `d_buffer`), then erase
346 /// the prefix that extends up to (and including) `iter`.
347 void dispatchMessageUpTo(const bsl::vector<char>::iterator& iter);
348
349 /// If there is a newline character in `d_buffer`, call
350 /// `dispatchMessageUpTo` with the location of that newline character
351 /// and return `true`; otherwise, return `false`.
352 bool dispatchLeftoverMessage();
353
354 /// Close the named pipe.
355 void destroyNamedPipe();
356
357 /// Block until bytes are available on the named pipe, and read them
358 /// into the internal buffer. If a message is encountered, dispatch it.
359 /// Return 0 on success, and a non-zero value otherwise.
360 int readNamedPipe();
361
362 /// Writes a '\n' character, only, to the pipe. Returns 0 on success, a
363 /// value greater than 0 on error, and a value less than 0 in case of a
364 /// timeout. Used to unblock the reading thread so it can detect a
365 /// shutdown condition. Note that this method is not to be called
366 /// anywhere except from `shutdown`.
367 int sendEmptyMessage();
368
369 private:
370 // NOT IMPLEMENTED
371 PipeControlChannel(const PipeControlChannel&);
372 PipeControlChannel& operator=(const PipeControlChannel&);
373
374 public:
375 // CREATORS
376
377 /// Create a pipe control mechanism that dispatches messages to the
378 /// specified `callback`. Optionally specify `basicAllocator` to supply
379 /// memory. If `basicAllocator` is zero, the currently installed
380 /// default allocator is used.
381 explicit
383 bslma::Allocator *basicAllocator = 0);
384
385 /// Destroy this object. Shut down the processing thread if it is still
386 /// running and block until it terminates. Close the named pipe and
387 /// clean up any associated system resources.
389
390 // MANIPULATORS
391
392 /// Open a named pipe having the specified `pipeName`, and start a
393 /// thread to read messages and dispatch them to the callback specified
394 /// at construction. Optionally specify `attributes` of the background
395 /// processing thread. If `attributes` is not supplied, a default
396 /// constructed `ThreadAttributes` object will be used. Return 0 on
397 /// success, and a non-zero value otherwise. In particular, return a
398 /// non-zero value if the pipe cannot be opened or if it is detected
399 /// that another process is reading from the pipe. `pipeName` must be
400 /// of the types `const char *`, `char *`, `bsl::string`, `std::string`,
401 /// `std::pmr::string` (if supported), or `bslstl::StringRef`.
402 int start(const char *pipeName);
403 template <class STRING_TYPE>
404 int start(const STRING_TYPE& pipeName);
405 int start(const char *pipeName,
406 const bslmt::ThreadAttributes& attributes);
407 template <class STRING_TYPE>
408 int start(const STRING_TYPE& pipeName,
409 const bslmt::ThreadAttributes& attributes);
410
411 /// Stop reading from the pipe and dispatching messages. If the
412 /// background thread has begun processing a message, this method will
413 /// block until a message that is currently being processed completes.
414 void shutdown();
415
416 /// Block until the background thread has been terminated by a call to
417 /// `shutdown`. Then close the pipe and clean up the associated file.
418 void stop();
419
420 // ACCESSORS
421
422 /// Return the fully qualified system name of the pipe.
423 const bsl::string& pipeName() const;
424
425 // Aspects
426
427 /// Return the allocator used by this object to supply memory. Note
428 /// that if no allocator was supplied at construction the default
429 /// allocator in effect at construction is used.
431};
432
433 // ====================================
434 // class PipeControlChannel_CStringUtil
435 // ====================================
436
437/// This component-private utility `struct` provides a namespace for the
438/// `flatten` overload set intended to be used in concert with an overload
439/// set consisting of a function template with a deduced argument and an
440/// non-template overload accepting a `const char *`. The actual
441/// implementation of the functionality would be in the `const char *`
442/// overload whereas the purpose of the function template is to invoke the
443/// `const char *` overload with a null-terminated string.
444///
445/// The function template achieves null-termination by recursively calling
446/// the function and supplying it with the result of `flatten` invoked on
447/// the deduced argument. This `flatten` invocation will call `c_str()` on
448/// various supported `string` types, will produce a temporary `bsl::string`
449/// for possibly non-null-terminated `bslstl::StringRef`, and will result in
450/// a `BSLMF_ASSERT` for any unsupported type. Calling the function with
451/// the temporary `bsl::string` produced from `bslstl::StringRef` will
452/// result in a second invocation of `flatten`, this time producing
453/// `const char *`, and finally calling the function with a null-terminated
454/// string.
455///
456/// Note that the `bslstl::StringRef` overload for `flatten` is provided for
457/// backwards compatibility. Without it, the `bsl::string` and
458/// `std::string` overloads would be ambiguous. In new code, it is
459/// preferable to not provide `bslstl::StringRef` overload in a similar
460/// facility and require the clients to explicitly state the string type in
461/// their code, making a potential allocation obvious. The
462/// `bsl::string_view` overload is not provided for the same reason.
463///
464/// Also note that since the constructor for `string` types from
465/// `bsl::string_view` is explicit, it is not necessary to support
466/// `bsl::string_view` for backwards compatibility, and it is not supported.
468
469 // CLASS METHODS
470
471 /// Return the specified `cString`.
472 static const char *flatten(char *cString);
473 static const char *flatten(const char *cString);
474
475 static const char *flatten(const bsl::string& string);
476 static const char *flatten(const std::string& string);
477#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING
478 static const char *flatten(const std::pmr::string& string);
479#endif
480 // Return the result of invoking 'c_str()' on the specified 'string'.
481
482 /// Return a temporary `bsl::string` constructed from the specified
483 /// `stringRef`.
484 static bsl::string flatten(const bslstl::StringRef& stringRef);
485
486 /// Produce a compile-time error informing the caller that the
487 /// parameterized `TYPE` is not supported as the parameter for the call.
488 template <class TYPE>
489 static const char *flatten(const TYPE&);
490};
491
492// ============================================================================
493// INLINE DEFINITIONS
494// ============================================================================
495
496 // ------------------------
497 // class PipeControlChannel
498 // ------------------------
499
500// MANIPULATORS
501template <class STRING_TYPE>
507
508template <class STRING_TYPE>
509int PipeControlChannel::start(const STRING_TYPE& pipeName,
510 const bslmt::ThreadAttributes& threadAttributes)
511{
513 threadAttributes);
514}
515
516// ACCESSORS
517inline
519{
520 return d_pipeName;
521}
522
523 // Aspects
524
525inline
527{
528 return d_pipeName.get_allocator().mechanism();
529}
530
531 // ------------------------------------
532 // class PipeControlChannel_CStringUtil
533 // ------------------------------------
534
535// CLASS METHODS
536inline
538{
539 return cString;
540}
541
542inline
543const char *PipeControlChannel_CStringUtil::flatten(const char *cString)
544{
545 return cString;
546}
547
548inline
550{
551 return string.c_str();
552}
553
554inline
555const char *PipeControlChannel_CStringUtil::flatten(const std::string& string)
556{
557 return string.c_str();
558}
559
560#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING
561inline
563 const std::pmr::string& string)
564{
565 return string.c_str();
566}
567#endif
568
569inline
571 const bslstl::StringRef& stringRef)
572{
573 return stringRef;
574}
575
576template <class TYPE>
577inline
579{
580 BSLMF_ASSERT(("Unsupported parameter type." && !sizeof(TYPE)));
581 return 0;
582}
583
584} // close package namespace
585
586#endif
587
588// ----------------------------------------------------------------------------
589// Copyright 2017 Bloomberg Finance L.P.
590//
591// Licensed under the Apache License, Version 2.0 (the "License");
592// you may not use this file except in compliance with the License.
593// You may obtain a copy of the License at
594//
595// http://www.apache.org/licenses/LICENSE-2.0
596//
597// Unless required by applicable law or agreed to in writing, software
598// distributed under the License is distributed on an "AS IS" BASIS,
599// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
600// See the License for the specific language governing permissions and
601// limitations under the License.
602// ----------------------------- END-OF-FILE ----------------------------------
603
604/** @} */
605/** @} */
606/** @} */
Definition balb_pipecontrolchannel.h:295
int d_readFd
Definition balb_pipecontrolchannel.h:324
int d_writeFd
Definition balb_pipecontrolchannel.h:325
struct balb::PipeControlChannel::@0::@2 d_windows
PipeControlChannel(const ControlCallback &callback, bslma::Allocator *basicAllocator=0)
const bsl::string & pipeName() const
Return the fully qualified system name of the pipe.
Definition balb_pipecontrolchannel.h:518
int start(const char *pipeName)
int start(const char *pipeName, const bslmt::ThreadAttributes &attributes)
void * d_handle
Definition balb_pipecontrolchannel.h:329
bslma::Allocator * allocator() const
Definition balb_pipecontrolchannel.h:526
struct balb::PipeControlChannel::@0::@1 d_unix
bsl::function< void(const bslstl::StringRef &message)> ControlCallback
Definition balb_pipecontrolchannel.h:304
Definition bslstl_string.h:1281
allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT
Return the allocator used by this string to supply memory.
Definition bslstl_string.h:6723
Forward declaration.
Definition bslstl_function.h:934
Definition bslstl_vector.h:1025
VALUE_TYPE * iterator
Definition bslstl_vector.h:1057
Definition bslma_allocator.h:457
Definition bslmt_threadattributes.h:356
Definition bsls_atomic.h:743
Definition bslstl_stringref.h:372
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:229
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition balb_controlmanager.h:133
Definition balb_pipecontrolchannel.h:467
static const char * flatten(char *cString)
Return the specified cString.
Definition balb_pipecontrolchannel.h:537
Imp::Handle Handle
Definition bslmt_threadutil.h:385