quiche: implement QuicMemSlice#6400
Conversation
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
|
/assign @wu-bin |
Signed-off-by: Dan Zhang <danzh@google.com>
wu-bin
left a comment
There was a problem hiding this comment.
I only finished the BUILD files, will continue on the c++ files.
| ) | ||
|
|
||
| envoy_cc_library( | ||
| name = "quic_mem_slice_storage_impl_lib", |
There was a problem hiding this comment.
nit: quic_platform_mem_slice_storage_impl_lib
|
I think @jmarantz is an ideal reviewer for this, as he has been doing other detailed reviews around buffer implementation recently. |
Signed-off-by: Dan Zhang <danzh@google.com>
jmarantz
left a comment
There was a problem hiding this comment.
flushing some comments. I haven't deeply looked at the interaction with buffers yet.
| // NOLINT(namespace-envoy) | ||
|
|
||
| // This file is part of the QUICHE platform implementation, and is not to be | ||
| // consumed or referenced directly by other Envoy |
|
|
||
| // Constructs a QuicMemSliceImpl by let |allocator| allocate a data buffer of | ||
| // |length|. | ||
| QuicMemSliceImpl(QuicBufferAllocator* allocator, size_t length) |
There was a problem hiding this comment.
Shall we go with Envoy style guide and use non-const refs for pointers which we can't allow to be null?
There was a problem hiding this comment.
Since all the impl's are used by quic API's, it inevitably has to match the expectation of its callers, and thus following quic style guide.
| allocator->New(length), length, | ||
| [allocator](const void* data, size_t, const Envoy::Buffer::BufferFragmentImpl* fragment) { | ||
| allocator->Delete(const_cast<char*>(static_cast<const char*>(data))); | ||
| delete fragment; |
There was a problem hiding this comment.
i'm surprised this 'delete' compiles since it's passed as a const pointer.
There was a problem hiding this comment.
deleter can still be called for const object so that it can be destructed.
| QuicMemSliceStorageImpl(QuicMemSliceStorageImpl&& other) = default; | ||
| QuicMemSliceStorageImpl& operator=(QuicMemSliceStorageImpl&& other) = default; | ||
|
|
||
| ~QuicMemSliceStorageImpl() = default; |
| // consumed or referenced directly by other Envoy code. It serves purely as a | ||
| // porting layer for QUICHE. | ||
|
|
||
| #include <stdint.h> |
Signed-off-by: Dan Zhang <danzh@google.com>
|
|
||
| inline void QuicPrintCommandLineFlagHelpImpl(const char* /*usage*/) {} | ||
|
|
||
| // porting layer for QUICHE. |
| #define QUIC_NOTREACHED_IMPL() NOT_REACHED_GCOVR_EXCL_LINE | ||
| #endif | ||
|
|
||
| #define DCHECK_GT(a, b) DCHECK(a > b) |
There was a problem hiding this comment.
Add parentheses to a and b to avoid problems with operator precedence:
#define DCHECK_GT(a, b) DCHECK((a) > (b))
Same below.
| // Constructs a QuicMemSliceImpl by let |allocator| allocate a data buffer of | ||
| // |length|. | ||
| QuicMemSliceImpl(QuicBufferAllocator* allocator, size_t length) | ||
| : length_(length), slice_(new Envoy::Buffer::OwnedImpl()) { |
There was a problem hiding this comment.
This function seems too heavyweight, in theory it only needs to do one memory allocation(the lifetime of the memory is the same as QuicMemSliceImpl itself), but it currently does
- 2 allocations for slice_ (OwnedImpl + shared memory control block. Can change to std::make_shared to combine them)
- 1 allocation for fragment
- 1 allocation for the buffer itself (allocator->New)
- 1 allocation for the "UnownedSlice" object (inside the addBufferFragment call)
I think, without changing the overall approach in this PR, we can change the shared_ptr to a unique_ptr, which allows us to combine 1, 2 and 3 in one allocation(allocator->New(total size needed)). I suspect we'll want to change to approach in the future(i.e. replace Buffer instance in this class by a real slice object, which represents a continuous span of memory) to further optimize it, though.
There was a problem hiding this comment.
I changed the class definition a bit:
Since mem slice doesn't need to share ownership of slice_ with other instance, slice_ doesn't need to be a shared_ptr.
Combined allocation of fragment and buffer itself.
Now there are only 2 allocations in this constructor.
|
|
||
| // Constructs a QuicMemSliceImpl from a Buffer::Instance whose getRawSlices() | ||
| // returns only 1 slice. | ||
| QuicMemSliceImpl(std::shared_ptr<Envoy::Buffer::Instance> slice) |
There was a problem hiding this comment.
(If we change slice_ to unique_ptr) slice should also be a unique_ptr.
Should this constructor be 'explicit', since it only has 1 arg?
There was a problem hiding this comment.
added explicit specifier.
| bool empty() const { return length() == 0; } | ||
|
|
||
| private: | ||
| size_t length_; |
There was a problem hiding this comment.
It seems length_ is not needed because slice_ already has a length() method.
|
|
||
| private: | ||
| size_t length_; | ||
| std::shared_ptr<Envoy::Buffer::Instance> slice_; |
There was a problem hiding this comment.
See my previous comment; I think this can be simplified to a unique_ptr.
Also a nit: The term "slice" often implies a continuous span of memory, I'd prefer something like 'single_slice_buffer_' here.
There was a problem hiding this comment.
Rename to single_slice_buffer_.
| // TODO(danzh): investigate the cost of allocating one buffer per slice. | ||
| // If it turns out to be expensive, add a new function to free data in the middle in buffer | ||
| // interface and re-design QuicMemSliceImpl. | ||
| single_slice_buffer->move(buffer_, slice.len_); |
There was a problem hiding this comment.
nit: DCHECK to make sure single_slice_buffer only has 1 slice?
There was a problem hiding this comment.
There is ASSERT in QuicMemSliceImpl() one line below. Skipped this temporary variable here and passed in buffer_ directly.
| void* dst = allocator->New(slice_len); | ||
| QuicUtils::CopyToBuffer(iov, iov_count, io_offset, slice_len, static_cast<char*>(dst)); | ||
| io_offset += slice_len; | ||
| auto fragment = new Envoy::Buffer::BufferFragmentImpl( |
There was a problem hiding this comment.
Similar here, the allocation of data and BufferFragment can be combined.
Signed-off-by: Dan Zhang <danzh@google.com>
|
|
||
| OwnedImpl::OwnedImpl(OwnedImpl&& other) | ||
| : old_impl_(other.old_impl_), slices_(std::move(other.slices_)), length_(other.length_), | ||
| buffer_(std::move(other.buffer_)) {} |
There was a problem hiding this comment.
add unit tests for these new ctors/assign-ops in buffer_impl_test.cc. we should definitely not be just testing them from quic only.
| # wrapping Envoy data type to QUIC data type. | ||
| # | ||
| # See a detailed description of QUIC platform API dependency model at: | ||
| # https://quiche.googlesource.com/quiche/+/refs/heads/master/quic/platform/api/README.md |
There was a problem hiding this comment.
btw this is excellent doc, thank you. Please render the dot diagram as a png also.
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
jmarantz
left a comment
There was a problem hiding this comment.
Thanks for your patience while we worked through it! It's much simpler now.
| # wrapping Envoy data type to QUIC data type. | ||
| # | ||
| # See a detailed description of QUIC platform API dependency model at: | ||
| # https://quiche.googlesource.com/quiche/+/refs/heads/master/quic/platform/api/README.md |
There was a problem hiding this comment.
What's the plan for fixing the diagram? Doesn't block this PR I suppose, but I am afraid if you don't do it someone will come along later and just delete the work you've done since it does not render as you intended.
Signed-off-by: Dan Zhang <danzh@google.com>
|
/retest |
|
🔨 rebuilding |
|
merged with master branch. PTAL! |
jmarantz
left a comment
There was a problem hiding this comment.
I think Matt's gotta look at this next.
mattklein123
left a comment
There was a problem hiding this comment.
LGTM from a skim. A couple of small questions. Thank you!
/wait
|
|
||
| OwnedImpl::OwnedImpl(OwnedImpl&& other) : OwnedImpl() { move(other); } | ||
|
|
||
| OwnedImpl& OwnedImpl::operator=(const OwnedImpl& other) { |
There was a problem hiding this comment.
Having the copy constructor and copy assignment operators can potentially lead to unintentional copies through C++ magic, so I would prefer to not have them. Is it possible to hide these behind explicit methods so they are very intentional?
There was a problem hiding this comment.
I brought up the previous discussion about why we added copy contractor. Please refer to that thread.
There was a problem hiding this comment.
I think the previous state was a strange explicit upcast which had more or less then same effect.
How about an explicit copyFrom() method instead, which you could call from the constructor body. Would that work?
There was a problem hiding this comment.
Yes, +1 to an explicit method. I don't think we need the constructor/assignment overrides but could have explicit methods (possible static) which do what we need. you could also make these operators private, and then access them via public static factory methods.
/wait
There was a problem hiding this comment.
Buffer::Instance already has similar method: add(). I switch to that one.
| Envoy::Buffer::RawSlice iovec; | ||
| uint64_t num_iov = single_slice_buffer_.reserve(length, &iovec, 1); | ||
| ASSERT(num_iov == 1); | ||
| // evbuffer may return a slice longer than needed, trim it to requested length. |
There was a problem hiding this comment.
nit: we are moving away from evbuffer internally. I would rephrase the comment to say this is how Envoy's buffer implementation works, etc.
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Add QuicMemSliceImpl, QuicMemSliceSpanImpl, QuicMemSliceStorageImpl, QuicTestMemSliceVectorImpl using Envoy::Buffer;
Add several quic core targets into quiche.BUILD file to compile some quic objects including quic_types.h which is needed in QuicMemSliceSpan and quic_utils.h which is needed in QuicTestMemSliceVectorImpl;
Risk Level: low, not in use
Testing: tested impl's with quic_mem_slice_test.cc, quic_mem_slice_span_test.cc, quic_mem_slice_storage_test.cc from quiche.
Part of #2557