BDE 4.39.x Production Release
Loading...
Searching...
No Matches
balb_pipecontrolchannel

Detailed Description

Provide a mechanism for reading control messages from a named pipe.

Outline

Purpose

Provide a mechanism for reading control messages from a named pipe.

Classes

See also
bdls_pipeutil

Description

This component provides a platform-independent mechanism, balb::PipeControlChannel, for establishing, monitoring, and shutting down a named pipe. It reads text messages (generally short operational commands) from the pipe and passes them to a callback function. Note that this component does not provide a general transport mechanism over a named pipe: it is specialized for the case that the messages to be read are relatively short newline-terminated strings.

Thread Safety

This component is thread-safe but not thread-enabled, meaning that multiple threads may safely use their own instances of PipeControlChannel, but may not manipulate the same PipeControlChannel simultaneously (except that shutdown may always be called safely). The start function creates a new thread which listens to the pipe for messages until shutdown is called.

Pipe Atomicity

Users that expect multiple concurrent writers to a single pipe must be aware that the message content might be corrupted (interleaved) unless:

  1. Each message is written to the pipe in a single write system call.
  2. The length of each message is less than PIPE_BUF (the limit for guaranteed atomicity).

The value PIPE_BUF depends on the platform:

+------------------------------+------------------+
| Platform | PIPE_BUF (bytes) |
+------------------------------+------------------+
| POSIX (minimum requirement)) | 512 |
| IBM | 32,768 |
| SUN | 32,768 |
| Linux | 65,536 |
| Windows | 65,536 |
+------------------------------+------------------+

Also note that Linux allows the PIPE_BUF size to be changed via the fcntl system call.

Pipe Names

This component requires a fully-qualified native pipe name. bdlsu::PipeUtil provides a portable utility method to generate such names.

Message Format

This component requires a trailing newline ('
') character at the end of each message. This trailing newline is stripped from the message before the message is passed to the control callback.

Pipe Permissions

This component creates its underlying named pipe with a permission bit mask that defaults to k_DEFAULT_PERMISSIONS (0666, i.e., read and write for owner, group, and others). A different permission bit mask may be supplied at construction time via the constructor overload taking an int permissions argument, or may be changed after construction (but before start) by calling setPermissions. The bit-mask semantics follow the conventional Unix mode-bit layout enumerated by bdls::FilePermissions; callers may express the desired permissions either as an octal literal (e.g., 0640) or by OR-ing together bdls::FilePermissions enumerators (e.g., bdls::FilePermissions::k_OWNER_READ | ...). Only the nine owner/group/others read/write/execute bits are accepted (see bdls::FilePermissions::isValidBaseBits); k_SET_UID, k_SET_GID, and k_STICKY_BIT are not meaningful for a control pipe and result in undefined behavior.

On operating systems that do not support Unix-style permission bits (notably Windows) the requested permission bit mask is silently ignored and the pipe is created with the operating-system default permissions.

Platform-Specific Pipe Name Encoding Caveats

Pipe-name encodings have the following caveats for the following operating systems:

Usage

This section illustrates intended use of this component.

Example 1: Controlling a Simple Server

This example illustrates how to construct a simple server that records messages sent on a pipe control channel until "EXIT" is received, at which point the channel is closed and the server stops.

First, let's define the implementation of our server.

// ===================
// class ControlServer
// ===================
class ControlServer {
// DATA
// PRIVATE MANIPULATORS
void onMessage(const bslstl::StringRef& message)
{
if ("EXIT" != message) {
d_messages.push_back(message);
}
else {
shutdown();
}
}
private:
// NOT IMPLEMENTED
ControlServer(const ControlServer&); // = delete
ControlServer& operator=(const ControlServer&); // = delete
public:
// CREATORS
explicit ControlServer(bslma::Allocator *basicAllocator = 0)
: d_channel(bdlf::BindUtil::bind(&ControlServer::onMessage,
this,
bdlf::PlaceHolders::_1),
basicAllocator)
, d_messages(basicAllocator)
{}
// MANIPULATORS
int start(const bslstl::StringRef& pipeName)
{
return d_channel.start(pipeName);
}
void shutdown()
{
d_channel.shutdown();
}
void stop()
{
d_channel.stop();
}
// ACCESSORS
bsl::size_t numMessages() const
{
return d_messages.size();
}
const bsl::string& message(int index) const
{
return d_messages[index];
}
};
Definition balb_pipecontrolchannel.h:316
int start(const char *pipeName)
Definition bslstl_string.h:1252
size_type size() const BSLS_KEYWORD_NOEXCEPT
Return the number of elements in this vector.
Definition bslstl_vector.h:3019
Definition bslstl_vector.h:1120
void push_back(const VALUE_TYPE &value)
Definition bslstl_vector.h:4343
Definition bslma_allocator.h:545
Definition bslstl_stringref.h:374
const PlaceHolder< 1 > _1
Definition bdlf_bind.h:979

Now, construct and run the server using a canonical name for the pipe:

bsl::string pipeName;
"ctrl.pcctest");
assert(0 == rc);
ControlServer server;
rc = server.start(pipeName);
if (0 != rc) {
cout << "ERROR: Failed to start pipe control channel" << endl;
}
static int makeCanonicalName(bsl::string *pipeName, const bsl::string_view &baseName)

Once the server is started, clients can send messages to the server.

const char MSG0[] = "this is the first message";
const char MSG1[] = "this is the second message";
rc = bdls::PipeUtil::send(pipeName, bsl::string(MSG0) + "\n");
assert(0 == rc);
rc = bdls::PipeUtil::send(pipeName, bsl::string(MSG1) + "\n");
assert(0 == rc);
rc = bdls::PipeUtil::send(pipeName, "EXIT\n");
assert(0 == rc);
static int send(const bsl::string_view &pipeName, const bsl::string_view &message)

The server shuts down once it processes the "EXIT" control message.

server.stop(); // block until shutdown

Finally, let's ensure the server received each control message sent.

assert(2 == server.numMessages());
assert(bsl::string(MSG0) == server.message(0));
assert(bsl::string(MSG1) == server.message(1));