This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
DynamicBuffer object lifetimes underspecifiedSection: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], 17.6 [networking.ts::buffer.async.read], 17.8 [networking.ts::buffer.async.write], 17.10 [networking.ts::buffer.async.read.until] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-26 Last modified: 2020-09-06
Priority: 3
View other active issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all other issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all issues with New status.
Discussion:
Addresses: networking.tsThe DynamicBuffer overloads of async_read and async_write, and
async_read_until, are underspecified with respect to the lifetime of the dynamic
buffer argument b.
Asio's implementation (and the intended specification) performs DECAY_COPY(b)
in the async_read, async_write, and async_read_until
initiating functions. All operations performed on b are actually performed on that
decay-copy, or on a move-constructed descendant of it. The copy is intended to refer to the same
underlying storage and be otherwise interchangeable with the original in every way.
Most initiating functions' argument lifetimes are covered by [async.reqmts.async.lifetime]. As an rvalue reference it falls under the second bullet, which specifies that the object is copied (but doesn't say decay-copied).
The proposed resolution adds a postcondition for DynamicBuffer move construction, and
specifies that DECAY_COPY(b) be used for each of these functions. The following
two alternative resolutions may also be considered:
Add an extra bullet to [async.reqmts.async.lifetime] to cover rvalue parameters (but specifically exclude CompletionTokens).
Change the DynamicBuffer arguments to be by-value. (And also change the corresponding synchronous operations to be consistent.)
However, the proposed resolution below is presented as a change that minimizes the scope of the impact.
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4711.
Edit 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] as indicated:
-3- In Table 14,
xdenotes a value of typeX,x1denotes a (possibly const) value of typeX,andmx1denotes an xvalue of typeX,ndenotes a (possibly const) value of typesize_t, andudenotes an identifier.
Table 14 — DynamicBuffer requirements expression type assertion/note pre/post-conditions X u(mx1);post:
u.size()is equal to the prior value ofmx1.size().u.max_size()is equal to the prior value ofmx1.max_size().u.capacity()is equal to the prior value ofmx1.capacity().u.data()satisfies the ConstBufferSequence requirements (16.2.2 [buffer.reqmts.constbuffersequence]) as if copy constructed from the prior value ofmx1.data().- All valid const or mutable buffer sequences that were previously obtained using
mx1.data()ormx1.prepare()remain valid.
Edit 17.6 [networking.ts::buffer.async.read] as indicated:
-11- Let
bdbe the result ofDECAY_COPY(b). Data is placed into the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to eachbbdread_somecall usingbd.prepare(N), whereNis an unspecified value less than or equal tobd.max_size() - bd.size(). [Note: Implementations are encouraged to usebd.capacity()when determiningN, to minimize the number ofread_somecalls performed on the stream. -- end note] After eachread_somecall, the implementation performsbd.commit(n), wherenis the return value fromread_some.[…]
-13- The synchronous read operation continues until:
bd.size() == bd.max_size(); orthe completion condition returns
0.
Edit 17.8 [networking.ts::buffer.async.write] as indicated:
-11- Let
bdbe the result ofDECAY_COPY(b). Data is written from the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) objectbd. A constant buffer sequence (16.2.2 [buffer.reqmts.constbuffersequence]) is obtained usingbd.data(). After the data has been written to the stream, the implementation performsbd.consume(n), wherenis the number of bytes successfully written.[…]
-13- The asynchronous write operation continues until:
bd.size() == 0; orthe completion condition returns
0.
Edit 17.10 [networking.ts::buffer.async.read.until] as indicated:
-3- Effects: Let
bdbe the result ofDECAY_COPY(b). Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream (17.1.2 [buffer.stream.reqmts.asyncreadstream]) objectstreamby performing zero or more asynchronous read_some operations on the stream, until the readable bytes of the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) objectbdcontain the specified delimiterdelim.-4- Data is placed into the dynamic buffer object
bd. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to eachasync_read_somecall usingbd.prepare(N), whereNis an unspecified value such thatN <= max_size() - size(). [Note: Implementations are encouraged to usebd.capacity()when determiningN, to minimize the number of asynchronous read_some operations performed on the stream. — end note] After the completion of each asynchronousread_someoperation, the implementation performsbd.commit(n), wherenis the value passed to the asynchronousread_someoperation's completion handler.-5- The asynchronous
read_untiloperation continues until:
the readable bytes of
bdcontain the delimiterdelim; or
bd.size() == bd.max_size(); oran asynchronous
read_someoperation fails.[…]
-8- On completion of the asynchronous operation, if the readable bytes of
bdcontain the delimiter,ecis set such that!ecistrue. Otherwise, ifbd.size() == bd.max_size(),ecis set such thatec == stream_errc::not_found. Ifbd.size() < bd.max_size(),ecis theerror_codefrom the most recent asynchronousread_someoperation.nis the number of readable bytes inbdup to and including the delimiter, if present, otherwise0.