Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component balb_pipecontrolchannel
[Package balb]

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

Namespaces

namespace  balb

Detailed Description

Outline
Purpose:
Provide a mechanism for reading control messages from a named pipe.
Classes:
balb::PipeControlChannel Mechanism for reading messages from a named pipe
See also:
Component 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 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 (\n) character at the end of each message. This trailing newline is stripped from the message before the message is passed to the control callback.
Platform-Specific Pipe Name Encoding Caveats:
Pipe-name encodings have the following caveats for the following operating systems:
  • On Windows, methods of balb::PipeControlChannel that take or return a pipe name as bsl::string (or a reference to it) type assume that the name is encoded in UTF-8. The routines attempt to convert the name to a UTF-16 wchar_t string via bdlde::CharConvertUtf16::utf8ToUtf16, and if the conversion succeeds, call the Windows wide-character W APIs with the UTF-16 name. If the conversion fails, the method fails.

    • Narrow-character pipe names in other encodings, containing characters with values in the range 128 - 255, will likely result in pipes being created with names that appear garbled if the conversion from UTF-8 to UTF-16 happens to succeed.
    • Neither utf8ToUtf16 nor the Windows W APIs do any normalization of the UTF-16 strings resulting from UTF-8 conversion, and it is therefore possible to have sets of pipe names that have the same visual representation but are treated as different names by the system.

  • On Posix, a pipe name supplied to methods of balb::PipeControlChannel as bsl::string type is passed unchanged to the underlying system file APIs. Because the pipe names are passed unchanged, balb::PipeControlChannel methods will work correctly on Posix with any encoding, but will interoperate only with processes that use the same encoding as the current process.
  • For compatibility with most modern Posix installs, and consistency with this component's Windows API, best practice is to encode all pipe names in UTF-8.
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
      balb::PipeControlChannel d_channel;
      bsl::vector<bsl::string> d_messages;

      // 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];
      }
  };
Now, construct and run the server using a canonical name for the pipe:
  bsl::string pipeName;
  int         rc = bdls::PipeUtil::makeCanonicalName(&pipeName,
                                                     "ctrl.pcctest");
  assert(0 == rc);

  ControlServer server;

  rc = server.start(pipeName);
  if (0 != rc) {
      cout << "ERROR: Failed to start pipe control channel" << endl;
  }
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);
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));