C++ Synchronized Buffered Ostream

ISO/IEC JTC1 SC22 WG21 P0053 - 2015-09-23

Lawrence Crowl, Lawrence@Crowl.org
Peter Sommerlad, Peter.Sommerlad@hsr.ch
Nicolai Josuttis

Introduction
Solution
Design
Wording
    27.1 General [input.output.general]
    27.10 Synchronized Output Stream [sync.stream]
    27.10.1 Overview [sync.stream]
    27.10.2 Class template basic_osyncstream [sync.stream.osyncstream]
    27.10.2.1 Constructor [sync.stream.osyncstream.ctor]
    27.10.2.2 Destructor [sync.stream.osyncstream.dtor]
    27.10.2.3 Member Functions [sync.stream.osyncstream.mfun]
    27.10.3 Class template noteflush_streambuf [sync.stream.streambuf]
    27.10.3.1 Member Functions [sync.stream.streambuf.mfun]
Revisions
References

Introduction

At present, stream output operations guarantee that they will not produce race conditions, but do not guarantee that the effect will be sensible. Some form of external synchronization is required. Unfortunately, without a standard mechanism for synchronizing, independently developed software will be unable to synchronize.

N3535 C++ Stream Mutexes proposed a standard mechanism for finding and sharing a mutex on streams. At the Spring 2013 standards meeting, the Concurrency Study Group requested a change away from a full mutex definition to a definition that also enabled buffering.

N3678 C++ Stream Guards proposed a standard mechanism for batching operations on a stream. That batching may be implemented as mutexees, as buffering, or some combination of both. It was the response to the Concurrency Study Group. A draft of that paper was reviewed in the Library Working Group, who found too many open issues on what was reasonably exposed to the 'buffering' part.

N3665 Uninterleaved Sring Output Streaming proposed making streaming of strings of length less than BUFSIZ appear uninterleaved in the output. It was a "minimal" functionality change to the existing standard to address the problem. The full Committee chose not to adopt that minimal solution.

N3750 C++ Ostream Buffers proposed an explicit buffering, at the direction of the general consensus in the July 2013 meeting of the Concurrency Study Group. In November 2014 a further updated version N4187 was discussed in Library Evolution in Urbana and it was consensus to work with a library expert to get the naming and wording better suited to LWG expectations. Nico Josuttis volunteered to help the original authors. More information on the discussion is available at LEWG wiki and the corresponding LEWG bug tracker entry (20). This paper address issues raised with N4187. This paper has a change of title to reflect a change in class name, but contains the same fundamental approach.

Solution

We propose a basic_osyncstream, that buffers output operations for a wrapped stream. The basic_osyncstream will atomically transfer the contents of an internal stream buffer to an ostream on destruction of the basic_osyncstream.

The transfer on destruction simplifies the code and ensures at least some output in the presence of an exception.

The intent is that the basic_osyncstream is an automatic-duration variable with a relatively small scope which constructs the text to appear uninterleaved. For example,

....
{
  std::osyncstream bout(std::cout);
  bout << "Hello, " << "World!" << std::endl;
}
....

Design

We follow typical stream conventions of basic_ prefixes and typedefs.

The constructors for osyncstream take non-const references to either a basic_ostream or a basic_osyncstream. This non-const reference indicates that the destruction of the buffer may write to the stream associated with the argument.

The constructors for osyncstream from another osyncstream is not a copy constructor, but an attachment to the argument buffer. This approach enables nested passing an osyncstream to a function expecting an ostream, which then in turn uses an osyncstream on that ostream

The wording below permits implementation of basic_osyncstream with either a stream_mutex from N3535 or with implementations suitable for N3665, e.g. with Posix file locks [PSL]

Wording

This wording is relative to N4527.

27.1 General [input.output.general]

Add a new row to table 120.

27.10 Synchronized output streams <syncstream>

27.10 Synchronized Output Stream [sync.stream]

Add a new section major section.

27.10.1 Overview [sync.stream]

Add a new section.

The header <syncstream> provides a mechanism to synchronize execution agents writing to the same stream. It defines a class template basic_osyncstream to buffer output and indivisibly transfer the buffered content into an ostream. The transfer occurs either on destruction or by explicit request by calling emit().

Add a synopsis for header <syncstream>.

template <class charT,
          class traits = char_traits<charT>,
          class Allocator = allocator<charT> >
  class basic_osyncstream;
typedef basic_osyncstream<char> osyncstream;
typedef basic_osyncstream<wchar_t> wosyncstream;

template <class charT,
          class traits = char_traits<charT>,
          class Allocator = allocator<charT> >
  class noteflush_streambuf;

27.10.2 Class template basic_osyncstream [sync.stream.osyncstream]

Add a new section.

template <class charT,
          class traits = std::char_traits<charT>,
          class Allocator = std::allocator<charT> >
class basic_osyncstream
  : public std::basic_ostream<charT,traits,Allocator>
{
public:
  typedef charT                          char_type;
  typedef traits                         traits_type;
  typedef typename traits_type::int_type int_type;
  typedef typename traits_type::pos_type pos_type;
  typedef typename traits_type::off_type off_type;
  typedef Allocator                      allocator_type;

  basic_osyncstream(basic_osyncstream &osb);
  explicit basic_osyncstream(std::basic_ostream<charT,traits> &os);
  ~basic_osyncstream();
  void emit();
};

The class template basic_osyncstream supports buffering into an internal buffer and then indivisibly transfering the contents of the buffer to a basic_ostream.

[Example: Use a named variable within a block statement for streaming acrosss multiple statments.

....
{
  std::osyncstream bout(std::cout);
  bout << "Hello, ";
  bout << "World!";
  bout << std::endl;
}
....

end example]

[Example: Use a temporary object for streaming within a single statement.

....
std::osyncstream(std::cout) << "Hello, " << "World!" << std::endl;
....

end example]

27.10.2.1 Constructor [sync.stream.osyncstream.ctor]

Add a new section.

basic_osyncstream(basic_ostream<charT,traits> &os);

Effects: Stores a reference to the basic_ostream, which will be the final destination of characters.

May construct a mutex.

Constructs a streambuf. Any flush on the streambuf will not act immediately on the basic_ostream, but will be deferred until immediately after the transfer of characters to the basic_ostream.

basic_osyncstream(basic_osyncstream &osb);

Effects: Stores a reference to the basic_ostream referenced by osb, which will be the final destination of characters.

May construct a mutex.

Constructs a streambuf. Any flush on the streambuf will not act immediately on the basic_ostream, but will be deferred until immediately after the transfer of characters to the basic_ostream.

[Example: This overload exists to allow wrapping a basic_osyncstream again with a basic_osyncstream. For example,

....
{
  std::osyncstream bout1(std::cout);
  bout1 << "Hello, ";
  {
    std::osyncstream bout2(bout1);
    bout2 << "Goodbye, " << "Planet!" << std::endl;
  }
  bout1 << "World!" << std::endl;
}
....

produces the uninterleaved output

Goodbye, Planet!
Hello, World!

end example.]

27.10.2.2 Destructor [sync.stream.osyncstream.dtor]

Add a new section.

~basic_osyncstream() noexcept;

Effects: as if emit(). May destroy a lock.

If the underlying stream throws an exception, the exception is ignored and any output is unspecified. If any underlying mutex lock throws an exception, the exception is ignored and no output occurs. it is returned to the environment, as in lock_guard. If any underlying mutex unlock throws an exception, the exception is ignored and any output is unspecified. [Note: An exception in mutex unlock is not permitted for standard-conformant mutexes. See 30.4.1.2 Mutex types [thread.mutex.requirements.mutex]. —end note]

Synchronization: May or may not acquire a mutex.

[Example: To protect against exceptions from within the destructor, use emit() to catch them.

{
  std::osyncstream bout(std::cout);
  bout << "Hello, " << "World!" << std::endl;
  try {
    bout.emit();
  }
  catch ( ... ) {
    ....
  }
}

end example]

27.10.2.3 Member Functions [sync.stream.osyncstream.mfun]

Add a new section.

void emit();

Effects: Transfers the contents of the internal streambuf to the stream specified in the constructor as an indivisible uninterleaved sequence of characters, with respect to all other uses of basic_osyncstream on that stream. If and only if a flush was requested on the streambuf, the stream will be flushed.

Synchronization: May or may not acquire a mutex while transfering characters.

27.10.3 Class template noteflush_streambuf [sync.stream.streambuf]

Add a new section.

template <class charT, class traits, class Allocator>
class noteflush_streambuf
  : public std::basic_streambuf<charT,traits,Allocator>
{
  protected:
    virtual int sync();
};

The class template noteflush_streambuf behaves like a basic_stringbuf except that flushes of the stream using it, which incur in calling the sync() member function, are noted instead of being a no-op.

27.10.3.1 Member Functions [sync.stream.streambuf.mfun]

Add a new section.

int sync() noexcept;

Effects: Notes the request of a flush within the object.

[Note: Any call to sync() ensures that a subsequent call to emit() in the owning basic_osyncstream will flush the stream being synchronized. —end note]

Returns: 0.

Revisions

This paper revises N4187 C++ Ostream Buffers

N4187 revised N4069 C++ Ostream Buffers

N4069 revised N3978 C++ Ostream Buffers

N3978 revised N3892 C++ Ostream Buffers

N3892 revised N3750 C++ Ostream Buffers

References

[PSL]
The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition, functions, flockfile, http://pubs.opengroup.org/onlinepubs/009695399/functions/flockfile.html