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,
x
denotes a value of typeX
,x1
denotes a (possibly const) value of typeX
,andmx1
denotes an xvalue of typeX
,n
denotes a (possibly const) value of typesize_t
, andu
denotes 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
bd
be 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 each
bbdread_some
call usingbd.prepare(N)
, whereN
is 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_some
calls performed on the stream. -- end note] After eachread_some
call, the implementation performsbd.commit(n)
, wheren
is 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
bd
be 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)
, wheren
is 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
bd
be 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]) objectstream
by 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]) objectbd
contain 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_some
call usingbd.prepare(N)
, whereN
is 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_some
operation, the implementation performsbd.commit(n)
, wheren
is the value passed to the asynchronousread_some
operation's completion handler.-5- The asynchronous
read_until
operation continues until:
the readable bytes of
bd
contain the delimiterdelim
; or
bd.size() == bd.max_size()
; oran asynchronous
read_some
operation fails.[…]
-8- On completion of the asynchronous operation, if the readable bytes of
bd
contain the delimiter,ec
is set such that!ec
istrue
. Otherwise, ifbd.size() == bd.max_size()
,ec
is set such thatec == stream_errc::not_found
. Ifbd.size() < bd.max_size()
,ec
is theerror_code
from the most recent asynchronousread_some
operation.n
is the number of readable bytes inbd
up to and including the delimiter, if present, otherwise0
.