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
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.
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;
}
....
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]
This wording is relative to N4527.
Add a new row to table 120.
27.10 Synchronized output streams <syncstream>
Add a new section major section.
Add a new section.
The header
<syncstream>
provides a mechanism to synchronize execution agents writing to the same stream. It defines a class templatebasic_osyncstream
to buffer output and indivisibly transfer the buffered content into anostream
. The transfer occurs either on destruction or by explicit request by callingemit()
.
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;
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 abasic_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]
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 thestreambuf
will not act immediately on thebasic_ostream
, but will be deferred until immediately after the transfer of characters to thebasic_ostream
.basic_osyncstream(basic_osyncstream &osb);
Effects: Stores a reference to the
basic_ostream
referenced byosb
, which will be the final destination of characters.May construct a mutex.
Constructs a
streambuf
. Any flush on thestreambuf
will not act immediately on thebasic_ostream
, but will be deferred until immediately after the transfer of characters to thebasic_ostream
.[Example: This overload exists to allow wrapping a
basic_osyncstream
again with abasic_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.]
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]
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 ofbasic_osyncstream
on that stream. If and only if a flush was requested on thestreambuf
, the stream will be flushed.Synchronization: May or may not acquire a mutex while transfering characters.
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 abasic_stringbuf
except that flushes of the stream using it, which incur in calling thesync()
member function, are noted instead of being a no-op.
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 toemit()
in the owningbasic_osyncstream
will flush the stream being synchronized. —end note]Returns:
0
.
This paper revises N4187 C++ Ostream Buffers
Updated introduction with recent history.
Rename ostream_buffer
to osyncstream
to reflect its appearance is more like a stream than like a buffer.
Add an example of using osyncstream
as a temporary object.
Add an example of a osyncstream
constructed with another osyncstream
.
Clarify the behavior of nested osyncstream
executions.
Clarify the behavior of exceptions
occuring with the osyncstream
destructor.
Clarify the deferral of flush from the
osyncstream
's streambuf
to the final basic_ostream
.
Limit the number of references to noteflush_stringbuf
in anticipation of the committee removing it from the specification.
Rename noteflush_stringbuf
to noteflush_streambuf
to hide possible implementation details.
Change the base class of noteflush_streambuf
from basic_stringbuf
to basic_streambuf
.
N4187 revised N4069 C++ Ostream Buffers
Added note to sync as suggested by BSI via email.
N4069 revised N3978 C++ Ostream Buffers
Added a Design section.
Clarify the reference capturing behavior
of the ostream_buffer
constructors.
Added noexcept and const as appropriate to members.
Added note on throwing wrapped streams.
Change the
noteflush_stringbuf
public member variable
needsflush
to a public member query function flushed
.
Removed the public member function noteflush_stringbuf::clear
.
Minor synopsis formatting changes.
Incorporated feedback from SG1 and Dietmar Kühl in specific in Rapperswil.
N3978 revised N3892 C++ Ostream Buffers
Flush the ostream if and only if the ostream_buffer
was flushed.
Add the clear_for_reuse
function.
Change the design from inheriting from basic_ostream
to using a noteflush_stringbuf
,
which is a slightly modified basic_stringbuf
.
The modification is to note the flush rather than act upon it.
N3892 revised N3750 C++ Ostream Buffers
Change name to basic_ostream_buffer
and add the usual typedefs.
Change interface to inherit from basic_ostringstream
rather than provide access to a member of that type.
Add a Revisions section.