diff --git a/BUILD b/BUILD index 3dceb6d0922..026d78c8f42 100644 --- a/BUILD +++ b/BUILD @@ -201,6 +201,8 @@ GRPC_PUBLIC_EVENT_ENGINE_HDRS = [ "include/grpc/event_engine/memory_allocator.h", "include/grpc/event_engine/memory_request.h", "include/grpc/event_engine/internal/memory_allocator_impl.h", + "include/grpc/event_engine/slice.h", + "include/grpc/event_engine/slice_buffer.h", ] GRPCXX_SRCS = [ @@ -1840,10 +1842,16 @@ grpc_cc_library( name = "event_engine_common", srcs = [ "src/core/lib/event_engine/resolved_address.cc", + "src/core/lib/event_engine/slice.cc", + "src/core/lib/event_engine/slice_buffer.cc", ], deps = [ "event_engine_base_hdrs", "gpr_base", + "gpr_platform", + "ref_counted", + "slice", + "slice_refcount", ], ) diff --git a/CMakeLists.txt b/CMakeLists.txt index f00410d2f86..3a6fc866064 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -864,7 +864,6 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_c server_ssl_test) endif() add_dependencies(buildtests_c server_test) - add_dependencies(buildtests_c slice_buffer_test) add_dependencies(buildtests_c slice_split_test) add_dependencies(buildtests_c slice_string_helpers_test) add_dependencies(buildtests_c sockaddr_resolver_test) @@ -890,6 +889,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_c tcp_server_posix_test) endif() add_dependencies(buildtests_c test_core_gpr_time_test) + add_dependencies(buildtests_c test_core_slice_slice_buffer_test) add_dependencies(buildtests_c thd_test) add_dependencies(buildtests_c threadpool_test) add_dependencies(buildtests_c time_averaged_stats_test) @@ -1137,6 +1137,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx streams_not_seen_test) add_dependencies(buildtests_cxx string_ref_test) add_dependencies(buildtests_cxx table_test) + add_dependencies(buildtests_cxx test_core_event_engine_slice_buffer_test) add_dependencies(buildtests_cxx test_core_gprpp_time_test) add_dependencies(buildtests_cxx test_core_security_credentials_test) add_dependencies(buildtests_cxx test_core_slice_slice_test) @@ -2027,6 +2028,8 @@ add_library(grpc src/core/lib/event_engine/event_engine.cc src/core/lib/event_engine/memory_allocator.cc src/core/lib/event_engine/resolved_address.cc + src/core/lib/event_engine/slice.cc + src/core/lib/event_engine/slice_buffer.cc src/core/lib/event_engine/sockaddr.cc src/core/lib/gprpp/time.cc src/core/lib/http/format_request.cc @@ -2332,6 +2335,8 @@ foreach(_hdr include/grpc/event_engine/memory_allocator.h include/grpc/event_engine/memory_request.h include/grpc/event_engine/port.h + include/grpc/event_engine/slice.h + include/grpc/event_engine/slice_buffer.h include/grpc/fork.h include/grpc/grpc.h include/grpc/grpc_posix.h @@ -2636,6 +2641,8 @@ add_library(grpc_unsecure src/core/lib/event_engine/event_engine.cc src/core/lib/event_engine/memory_allocator.cc src/core/lib/event_engine/resolved_address.cc + src/core/lib/event_engine/slice.cc + src/core/lib/event_engine/slice_buffer.cc src/core/lib/event_engine/sockaddr.cc src/core/lib/gprpp/time.cc src/core/lib/http/format_request.cc @@ -2876,6 +2883,8 @@ foreach(_hdr include/grpc/event_engine/memory_allocator.h include/grpc/event_engine/memory_request.h include/grpc/event_engine/port.h + include/grpc/event_engine/slice.h + include/grpc/event_engine/slice_buffer.h include/grpc/fork.h include/grpc/grpc.h include/grpc/grpc_posix.h @@ -6570,33 +6579,6 @@ target_link_libraries(server_test ) -endif() -if(gRPC_BUILD_TESTS) - -add_executable(slice_buffer_test - test/core/slice/slice_buffer_test.cc -) - -target_include_directories(slice_buffer_test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} - ${_gRPC_RE2_INCLUDE_DIR} - ${_gRPC_SSL_INCLUDE_DIR} - ${_gRPC_UPB_GENERATED_DIR} - ${_gRPC_UPB_GRPC_GENERATED_DIR} - ${_gRPC_UPB_INCLUDE_DIR} - ${_gRPC_XXHASH_INCLUDE_DIR} - ${_gRPC_ZLIB_INCLUDE_DIR} -) - -target_link_libraries(slice_buffer_test - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - endif() if(gRPC_BUILD_TESTS) @@ -7018,6 +7000,33 @@ target_link_libraries(test_core_gpr_time_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(test_core_slice_slice_buffer_test + test/core/slice/slice_buffer_test.cc +) + +target_include_directories(test_core_slice_slice_buffer_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} +) + +target_link_libraries(test_core_slice_slice_buffer_test + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) @@ -15826,6 +15835,41 @@ target_link_libraries(table_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(test_core_event_engine_slice_buffer_test + test/core/event_engine/slice_buffer_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(test_core_event_engine_slice_buffer_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(test_core_event_engine_slice_buffer_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/Makefile b/Makefile index bd668f54a87..bbe079d80e2 100644 --- a/Makefile +++ b/Makefile @@ -1446,6 +1446,8 @@ LIBGRPC_SRC = \ src/core/lib/event_engine/event_engine.cc \ src/core/lib/event_engine/memory_allocator.cc \ src/core/lib/event_engine/resolved_address.cc \ + src/core/lib/event_engine/slice.cc \ + src/core/lib/event_engine/slice_buffer.cc \ src/core/lib/event_engine/sockaddr.cc \ src/core/lib/gprpp/time.cc \ src/core/lib/http/format_request.cc \ @@ -1696,6 +1698,8 @@ PUBLIC_HEADERS_C += \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ + include/grpc/event_engine/slice.h \ + include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ @@ -1898,6 +1902,8 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/event_engine/event_engine.cc \ src/core/lib/event_engine/memory_allocator.cc \ src/core/lib/event_engine/resolved_address.cc \ + src/core/lib/event_engine/slice.cc \ + src/core/lib/event_engine/slice_buffer.cc \ src/core/lib/event_engine/sockaddr.cc \ src/core/lib/gprpp/time.cc \ src/core/lib/http/format_request.cc \ @@ -2084,6 +2090,8 @@ PUBLIC_HEADERS_C += \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ + include/grpc/event_engine/slice.h \ + include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index cad7c6e2d89..0e86ac3f6fe 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -302,6 +302,8 @@ libs: - include/grpc/event_engine/memory_allocator.h - include/grpc/event_engine/memory_request.h - include/grpc/event_engine/port.h + - include/grpc/event_engine/slice.h + - include/grpc/event_engine/slice_buffer.h - include/grpc/fork.h - include/grpc/grpc.h - include/grpc/grpc_posix.h @@ -1403,6 +1405,8 @@ libs: - src/core/lib/event_engine/event_engine.cc - src/core/lib/event_engine/memory_allocator.cc - src/core/lib/event_engine/resolved_address.cc + - src/core/lib/event_engine/slice.cc + - src/core/lib/event_engine/slice_buffer.cc - src/core/lib/event_engine/sockaddr.cc - src/core/lib/gprpp/time.cc - src/core/lib/http/format_request.cc @@ -1778,6 +1782,8 @@ libs: - include/grpc/event_engine/memory_allocator.h - include/grpc/event_engine/memory_request.h - include/grpc/event_engine/port.h + - include/grpc/event_engine/slice.h + - include/grpc/event_engine/slice_buffer.h - include/grpc/fork.h - include/grpc/grpc.h - include/grpc/grpc_posix.h @@ -2241,6 +2247,8 @@ libs: - src/core/lib/event_engine/event_engine.cc - src/core/lib/event_engine/memory_allocator.cc - src/core/lib/event_engine/resolved_address.cc + - src/core/lib/event_engine/slice.cc + - src/core/lib/event_engine/slice_buffer.cc - src/core/lib/event_engine/sockaddr.cc - src/core/lib/gprpp/time.cc - src/core/lib/http/format_request.cc @@ -4045,15 +4053,6 @@ targets: - test/core/surface/server_test.cc deps: - grpc_test_util -- name: slice_buffer_test - build: test - language: c - headers: [] - src: - - test/core/slice/slice_buffer_test.cc - deps: - - grpc_test_util - uses_polling: false - name: slice_split_test build: test language: c @@ -4217,6 +4216,15 @@ targets: deps: - grpc_test_util uses_polling: false +- name: test_core_slice_slice_buffer_test + build: test + language: c + headers: [] + src: + - test/core/slice/slice_buffer_test.cc + deps: + - grpc_test_util + uses_polling: false - name: thd_test build: test language: c @@ -7706,6 +7714,15 @@ targets: - absl/types:optional - absl/utility:utility uses_polling: false +- name: test_core_event_engine_slice_buffer_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/event_engine/slice_buffer_test.cc + deps: + - grpc_test_util - name: test_core_gprpp_time_test gtest: true build: test diff --git a/config.m4 b/config.m4 index 96c25b1b105..79572b43faa 100644 --- a/config.m4 +++ b/config.m4 @@ -468,6 +468,8 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/event_engine/event_engine.cc \ src/core/lib/event_engine/memory_allocator.cc \ src/core/lib/event_engine/resolved_address.cc \ + src/core/lib/event_engine/slice.cc \ + src/core/lib/event_engine/slice_buffer.cc \ src/core/lib/event_engine/sockaddr.cc \ src/core/lib/gpr/alloc.cc \ src/core/lib/gpr/atm.cc \ diff --git a/config.w32 b/config.w32 index 75f77d0368a..ccd1f6b9483 100644 --- a/config.w32 +++ b/config.w32 @@ -434,6 +434,8 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\event_engine\\event_engine.cc " + "src\\core\\lib\\event_engine\\memory_allocator.cc " + "src\\core\\lib\\event_engine\\resolved_address.cc " + + "src\\core\\lib\\event_engine\\slice.cc " + + "src\\core\\lib\\event_engine\\slice_buffer.cc " + "src\\core\\lib\\event_engine\\sockaddr.cc " + "src\\core\\lib\\gpr\\alloc.cc " + "src\\core\\lib\\gpr\\atm.cc " + diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 053b5ea9448..fdbc2304f75 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -113,6 +113,8 @@ Pod::Spec.new do |s| 'include/grpc/event_engine/memory_allocator.h', 'include/grpc/event_engine/memory_request.h', 'include/grpc/event_engine/port.h', + 'include/grpc/event_engine/slice.h', + 'include/grpc/event_engine/slice_buffer.h', 'include/grpc/fork.h', 'include/grpc/grpc.h', 'include/grpc/grpc_posix.h', @@ -1036,6 +1038,8 @@ Pod::Spec.new do |s| 'src/core/lib/event_engine/event_engine_factory.h', 'src/core/lib/event_engine/memory_allocator.cc', 'src/core/lib/event_engine/resolved_address.cc', + 'src/core/lib/event_engine/slice.cc', + 'src/core/lib/event_engine/slice_buffer.cc', 'src/core/lib/event_engine/sockaddr.cc', 'src/core/lib/event_engine/sockaddr.h', 'src/core/lib/gpr/alloc.cc', diff --git a/grpc.gemspec b/grpc.gemspec index 07d91ce716c..daeee357be3 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -57,6 +57,8 @@ Gem::Specification.new do |s| s.files += %w( include/grpc/event_engine/memory_allocator.h ) s.files += %w( include/grpc/event_engine/memory_request.h ) s.files += %w( include/grpc/event_engine/port.h ) + s.files += %w( include/grpc/event_engine/slice.h ) + s.files += %w( include/grpc/event_engine/slice_buffer.h ) s.files += %w( include/grpc/fork.h ) s.files += %w( include/grpc/grpc.h ) s.files += %w( include/grpc/grpc_posix.h ) @@ -951,6 +953,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/event_engine/event_engine_factory.h ) s.files += %w( src/core/lib/event_engine/memory_allocator.cc ) s.files += %w( src/core/lib/event_engine/resolved_address.cc ) + s.files += %w( src/core/lib/event_engine/slice.cc ) + s.files += %w( src/core/lib/event_engine/slice_buffer.cc ) s.files += %w( src/core/lib/event_engine/sockaddr.cc ) s.files += %w( src/core/lib/event_engine/sockaddr.h ) s.files += %w( src/core/lib/gpr/alloc.cc ) diff --git a/grpc.gyp b/grpc.gyp index de8c620bde0..e3b947d3a96 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -801,6 +801,8 @@ 'src/core/lib/event_engine/event_engine.cc', 'src/core/lib/event_engine/memory_allocator.cc', 'src/core/lib/event_engine/resolved_address.cc', + 'src/core/lib/event_engine/slice.cc', + 'src/core/lib/event_engine/slice_buffer.cc', 'src/core/lib/event_engine/sockaddr.cc', 'src/core/lib/gprpp/time.cc', 'src/core/lib/http/format_request.cc', @@ -1262,6 +1264,8 @@ 'src/core/lib/event_engine/event_engine.cc', 'src/core/lib/event_engine/memory_allocator.cc', 'src/core/lib/event_engine/resolved_address.cc', + 'src/core/lib/event_engine/slice.cc', + 'src/core/lib/event_engine/slice_buffer.cc', 'src/core/lib/event_engine/sockaddr.cc', 'src/core/lib/gprpp/time.cc', 'src/core/lib/http/format_request.cc', diff --git a/include/grpc/event_engine/event_engine.h b/include/grpc/event_engine/event_engine.h index 0527c036fd7..8a0d882c465 100644 --- a/include/grpc/event_engine/event_engine.h +++ b/include/grpc/event_engine/event_engine.h @@ -26,6 +26,7 @@ #include #include #include +#include // TODO(hork): Define the Endpoint::Write metrics collection system namespace grpc_event_engine { diff --git a/include/grpc/event_engine/memory_allocator.h b/include/grpc/event_engine/memory_allocator.h index 8c812671311..ec106740a0f 100644 --- a/include/grpc/event_engine/memory_allocator.h +++ b/include/grpc/event_engine/memory_allocator.h @@ -26,24 +26,9 @@ #include #include -// forward-declaring an internal struct, not used publicly. -struct grpc_slice_buffer; - namespace grpc_event_engine { namespace experimental { -// TODO(nnoble): needs implementation -class SliceBuffer { - public: - SliceBuffer() { abort(); } - explicit SliceBuffer(grpc_slice_buffer*) { abort(); } - - grpc_slice_buffer* RawSliceBuffer() { return slice_buffer_; } - - private: - grpc_slice_buffer* slice_buffer_; -}; - // Tracks memory allocated by one system. // Is effectively a thin wrapper/smart pointer for a MemoryAllocatorImpl, // providing a convenient and stable API. diff --git a/include/grpc/event_engine/slice.h b/include/grpc/event_engine/slice.h new file mode 100644 index 00000000000..515cdfdcfd9 --- /dev/null +++ b/include/grpc/event_engine/slice.h @@ -0,0 +1,286 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GRPC_EVENT_ENGINE_SLICE_H +#define GRPC_EVENT_ENGINE_SLICE_H + +#include + +#include + +#include +#include +#include + +#include "absl/strings/string_view.h" + +#include +#include + +// This public slice definition largely based of the internal grpc_core::Slice +// implementation. Changes to this implementation might warrant changes to the +// internal grpc_core::Slice type as well. + +namespace grpc_event_engine { +namespace experimental { + +// Forward declarations +class Slice; +class MutableSlice; + +namespace slice_detail { + +// Returns an empty slice. +static constexpr grpc_slice EmptySlice() { return {nullptr, {}}; } + +// BaseSlice holds the grpc_slice object, but does not apply refcounting policy. +// It does export immutable access into the slice, such that this can be shared +// by all storage policies. +class BaseSlice { + public: + BaseSlice(const BaseSlice&) = delete; + BaseSlice& operator=(const BaseSlice&) = delete; + BaseSlice(BaseSlice&& other) = delete; + BaseSlice& operator=(BaseSlice&& other) = delete; + + // Iterator access to the underlying bytes + const uint8_t* begin() const { return GRPC_SLICE_START_PTR(c_slice()); } + const uint8_t* end() const { return GRPC_SLICE_END_PTR(c_slice()); } + const uint8_t* cbegin() const { return GRPC_SLICE_START_PTR(c_slice()); } + const uint8_t* cend() const { return GRPC_SLICE_END_PTR(c_slice()); } + + // Retrieve a borrowed reference to the underlying grpc_slice. + const grpc_slice& c_slice() const { return slice_; } + + // Retrieve the underlying grpc_slice, and replace the one in this object with + // EmptySlice(). + grpc_slice TakeCSlice() { + grpc_slice out = slice_; + slice_ = EmptySlice(); + return out; + } + + // As other things... borrowed references. + absl::string_view as_string_view() const { + return absl::string_view(reinterpret_cast(data()), size()); + } + + // Array access + uint8_t operator[](size_t i) const { + return GRPC_SLICE_START_PTR(c_slice())[i]; + } + + // Access underlying data + const uint8_t* data() const { return GRPC_SLICE_START_PTR(c_slice()); } + + // Size of the slice + size_t size() const { return GRPC_SLICE_LENGTH(c_slice()); } + size_t length() const { return size(); } + bool empty() const { return size() == 0; } + + // For inlined slices - are these two slices equal? + // For non-inlined slices - do these two slices refer to the same block of + // memory? + bool is_equivalent(const BaseSlice& other) const { + return grpc_slice_is_equivalent(slice_, other.slice_); + } + + uint32_t Hash() const; + + protected: + BaseSlice() : slice_(EmptySlice()) {} + explicit BaseSlice(const grpc_slice& slice) : slice_(slice) {} + ~BaseSlice() = default; + + void Swap(BaseSlice* other) { std::swap(slice_, other->slice_); } + void SetCSlice(const grpc_slice& slice) { slice_ = slice; } + + uint8_t* mutable_data() { return GRPC_SLICE_START_PTR(slice_); } + + grpc_slice* c_slice_ptr() { return &slice_; } + + private: + grpc_slice slice_; +}; + +inline bool operator==(const BaseSlice& a, const BaseSlice& b) { + return grpc_slice_eq(a.c_slice(), b.c_slice()) != 0; +} + +inline bool operator!=(const BaseSlice& a, const BaseSlice& b) { + return grpc_slice_eq(a.c_slice(), b.c_slice()) == 0; +} + +inline bool operator==(const BaseSlice& a, absl::string_view b) { + return a.as_string_view() == b; +} + +inline bool operator!=(const BaseSlice& a, absl::string_view b) { + return a.as_string_view() != b; +} + +inline bool operator==(absl::string_view a, const BaseSlice& b) { + return a == b.as_string_view(); +} + +inline bool operator!=(absl::string_view a, const BaseSlice& b) { + return a != b.as_string_view(); +} + +inline bool operator==(const BaseSlice& a, const grpc_slice& b) { + return grpc_slice_eq(a.c_slice(), b) != 0; +} + +inline bool operator!=(const BaseSlice& a, const grpc_slice& b) { + return grpc_slice_eq(a.c_slice(), b) == 0; +} + +inline bool operator==(const grpc_slice& a, const BaseSlice& b) { + return grpc_slice_eq(a, b.c_slice()) != 0; +} + +inline bool operator!=(const grpc_slice& a, const BaseSlice& b) { + return grpc_slice_eq(a, b.c_slice()) == 0; +} + +template +struct CopyConstructors { + static Out FromCopiedString(const char* s) { + return FromCopiedBuffer(s, strlen(s)); + } + static Out FromCopiedString(absl::string_view s) { + return FromCopiedBuffer(s.data(), s.size()); + } + static Out FromCopiedString(std::string s); + + static Out FromCopiedBuffer(const char* p, size_t len) { + return Out(grpc_slice_from_copied_buffer(p, len)); + } + + template + static Out FromCopiedBuffer(const Buffer& buffer) { + return FromCopiedBuffer(reinterpret_cast(buffer.data()), + buffer.size()); + } +}; + +} // namespace slice_detail + +class MutableSlice : public slice_detail::BaseSlice, + public slice_detail::CopyConstructors { + public: + MutableSlice() = default; + explicit MutableSlice(const grpc_slice& slice); + ~MutableSlice(); + + MutableSlice(const MutableSlice&) = delete; + MutableSlice& operator=(const MutableSlice&) = delete; + MutableSlice(MutableSlice&& other) noexcept + : slice_detail::BaseSlice(other.TakeCSlice()) {} + MutableSlice& operator=(MutableSlice&& other) noexcept { + Swap(&other); + return *this; + } + + static MutableSlice CreateUninitialized(size_t length) { + return MutableSlice(grpc_slice_malloc(length)); + } + + // Return a sub slice of this one. Leaves this slice in an indeterminate but + // valid state. + MutableSlice TakeSubSlice(size_t pos, size_t n) { + return MutableSlice(grpc_slice_sub_no_ref(TakeCSlice(), pos, pos + n)); + } + + // Iterator access to the underlying bytes + uint8_t* begin() { return mutable_data(); } + uint8_t* end() { return mutable_data() + size(); } + uint8_t* data() { return mutable_data(); } + + // Array access + uint8_t& operator[](size_t i) { return mutable_data()[i]; } +}; + +class Slice : public slice_detail::BaseSlice, + public slice_detail::CopyConstructors { + public: + Slice() = default; + ~Slice(); + explicit Slice(const grpc_slice& slice) : slice_detail::BaseSlice(slice) {} + explicit Slice(slice_detail::BaseSlice&& other) + : slice_detail::BaseSlice(other.TakeCSlice()) {} + + Slice(const Slice&) = delete; + Slice& operator=(const Slice&) = delete; + Slice(Slice&& other) noexcept : slice_detail::BaseSlice(other.TakeCSlice()) {} + Slice& operator=(Slice&& other) noexcept { + Swap(&other); + return *this; + } + + // A slice might refer to some memory that we keep a refcount to (this is + // owned), or some memory that's inlined into the slice (also owned), or some + // other block of memory that we know will be available for the lifetime of + // some operation in the common case (not owned). In the *less common* case + // that we need to keep that slice text for longer than our API's guarantee us + // access, we need to take a copy and turn this into something that we do own. + + // TakeOwned returns an owned slice regardless of current ownership, and + // leaves the current slice in a valid but externally unpredictable state - in + // doing so it can avoid adding a ref to the underlying slice. + Slice TakeOwned(); + + // AsOwned returns an owned slice but does not mutate the current slice, + // meaning that it may add a reference to the underlying slice. + Slice AsOwned() const; + + // TakeMutable returns a MutableSlice, and leaves the current slice in an + // indeterminate but valid state. + // A mutable slice requires only one reference to the bytes of the slice - + // this can be achieved either with inlined storage or with a single + // reference. + // If the current slice is refcounted and there are more than one references + // to that slice, then the slice is copied in order to achieve a mutable + // version. + MutableSlice TakeMutable(); + + // Return a sub slice of this one. Leaves this slice in an indeterminate but + // valid state. + Slice TakeSubSlice(size_t pos, size_t n) { + return Slice(grpc_slice_sub_no_ref(TakeCSlice(), pos, pos + n)); + } + + // Return a sub slice of this one. Adds a reference to the underlying slice. + Slice RefSubSlice(size_t pos, size_t n) const { + return Slice(grpc_slice_sub(c_slice(), pos, pos + n)); + } + + // Split this slice, returning a new slice containing (split:end] and + // leaving this slice with [begin:split). + Slice Split(size_t split) { + return Slice(grpc_slice_split_tail(c_slice_ptr(), split)); + } + + Slice Ref() const; + + Slice Copy() const { return Slice(grpc_slice_copy(c_slice())); } + + static Slice FromRefcountAndBytes(grpc_slice_refcount* r, + const uint8_t* begin, const uint8_t* end); +}; + +} // namespace experimental +} // namespace grpc_event_engine + +#endif // GRPC_EVENT_ENGINE_SLICE_H diff --git a/include/grpc/event_engine/slice_buffer.h b/include/grpc/event_engine/slice_buffer.h new file mode 100644 index 00000000000..609b6ea0841 --- /dev/null +++ b/include/grpc/event_engine/slice_buffer.h @@ -0,0 +1,112 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GRPC_EVENT_ENGINE_SLICE_BUFFER_H +#define GRPC_EVENT_ENGINE_SLICE_BUFFER_H + +#include + +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/utility/utility.h" + +#include +#include +#include +#include + +namespace grpc_event_engine { +namespace experimental { + +/// A Wrapper around \a grpc_slice_buffer pointer. +/// +/// A slice buffer holds the memory for a collection of slices. +/// The SliceBuffer object itself is meant to only hide the C-style API, +/// and won't hold the data itself. In terms of lifespan, the +/// grpc_slice_buffer ought to be kept somewhere inside the caller's objects, +/// like a transport or an endpoint. +/// +/// This lifespan rule is likely to change in the future, as we may +/// collapse the grpc_slice_buffer structure straight into this class. +/// +/// The SliceBuffer API is basically a replica of the grpc_slice_buffer's, +/// and its documentation will move here once we remove the C structure, +/// which should happen before the Event Engine's API is no longer +/// an experimental API. +class SliceBuffer { + public: + explicit SliceBuffer() { grpc_slice_buffer_init(&slice_buffer_); } + SliceBuffer(const SliceBuffer& other) = delete; + SliceBuffer(SliceBuffer&& other) noexcept + : slice_buffer_(other.slice_buffer_) { + grpc_slice_buffer_reset_and_unref(&slice_buffer_); + grpc_slice_buffer_swap(&slice_buffer_, &other.slice_buffer_); + } + /// Upon destruction, the underlying raw slice buffer is cleaned out and all + /// slices are unreffed. + ~SliceBuffer() { grpc_slice_buffer_destroy(&slice_buffer_); } + + /// Appends a new slice into the SliceBuffer and makes an attempt to merge + /// this slice with the last slice in the SliceBuffer. + void Append(Slice slice); + + /// Adds a new slice into the SliceBuffer at the next available index. + /// Returns the index at which the new slice is added. + size_t AppendIndexed(Slice slice); + + /// Returns the number of slices held by the SliceBuffer. + size_t Count() { return slice_buffer_.count; } + + /// Removes/deletes the last n bytes in the SliceBuffer. + void RemoveLastNBytes(size_t n) { + grpc_slice_buffer_trim_end(&slice_buffer_, n, nullptr); + } + + /// Move the first n bytes of the SliceBuffer into a memory pointed to by dst. + void MoveFirstNBytesIntoBuffer(size_t n, void* dst) { + grpc_slice_buffer_move_first_into_buffer(&slice_buffer_, n, dst); + } + + /// Removes and unrefs all slices in the SliceBuffer. + void Clear() { grpc_slice_buffer_reset_and_unref(&slice_buffer_); } + + /// Removes the first slice in the SliceBuffer and returns it. + Slice TakeFirst(); + + /// Prepends the slice to the the front of the SliceBuffer. + void Prepend(Slice slice); + + /// Increased the ref-count of slice at the specified index and returns the + /// associated slice. + Slice RefSlice(size_t index); + + /// The total number of bytes held by the SliceBuffer + size_t Length() { return slice_buffer_.length; } + + /// Return a pointer to the back raw grpc_slice_buffer + grpc_slice_buffer* RawSliceBuffer() { return &slice_buffer_; } + + private: + /// The backing raw slice buffer. + grpc_slice_buffer slice_buffer_; +}; + +} // namespace experimental +} // namespace grpc_event_engine + +#endif // GRPC_EVENT_ENGINE_SLICE_BUFFER_H diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h index 130e5efffd2..33c1f1b37bd 100644 --- a/include/grpc/impl/codegen/slice.h +++ b/include/grpc/impl/codegen/slice.h @@ -19,7 +19,7 @@ #ifndef GRPC_IMPL_CODEGEN_SLICE_H #define GRPC_IMPL_CODEGEN_SLICE_H -// IWYU pragma: private, include +// IWYU pragma: private #include diff --git a/package.xml b/package.xml index abf2ca44fca..fcb7abfb511 100644 --- a/package.xml +++ b/package.xml @@ -39,6 +39,8 @@ + + @@ -933,6 +935,8 @@ + + diff --git a/src/core/lib/event_engine/slice.cc b/src/core/lib/event_engine/slice.cc new file mode 100644 index 00000000000..1c87b6a9d19 --- /dev/null +++ b/src/core/lib/event_engine/slice.cc @@ -0,0 +1,102 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +#include +#include + +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_refcount.h" +#include "src/core/lib/slice/slice_refcount_base.h" + +namespace grpc_event_engine { +namespace experimental { + +namespace slice_detail { + +uint32_t BaseSlice::Hash() const { return grpc_slice_hash_internal(slice_); } + +template <> +MutableSlice CopyConstructors::FromCopiedString(std::string s) { + return MutableSlice(grpc_slice_from_cpp_string(std::move(s))); +} + +template <> +Slice CopyConstructors::FromCopiedString(std::string s) { + return Slice(grpc_slice_from_cpp_string(std::move(s))); +} + +} // namespace slice_detail + +MutableSlice::MutableSlice(const grpc_slice& slice) + : slice_detail::BaseSlice(slice) { + GPR_DEBUG_ASSERT(slice.refcount == nullptr || slice.refcount->IsUnique()); +} + +MutableSlice::~MutableSlice() { grpc_slice_unref_internal(c_slice()); } + +Slice Slice::TakeOwned() { + if (c_slice().refcount == nullptr) { + return Slice(c_slice()); + } + if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) { + return Slice(grpc_slice_copy(c_slice())); + } + return Slice(TakeCSlice()); +} + +Slice Slice::AsOwned() const { + if (c_slice().refcount == nullptr) { + return Slice(c_slice()); + } + if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) { + return Slice(grpc_slice_copy(c_slice())); + } + return Slice(grpc_slice_ref_internal(c_slice())); +} + +MutableSlice Slice::TakeMutable() { + if (c_slice().refcount == nullptr) { + return MutableSlice(c_slice()); + } + if (c_slice().refcount != grpc_slice_refcount::NoopRefcount() && + c_slice().refcount->IsUnique()) { + return MutableSlice(TakeCSlice()); + } + return MutableSlice(grpc_slice_copy(c_slice())); +} + +Slice::~Slice() { grpc_slice_unref_internal(c_slice()); } + +Slice Slice::Ref() const { return Slice(grpc_slice_ref_internal(c_slice())); } + +Slice Slice::FromRefcountAndBytes(grpc_slice_refcount* r, const uint8_t* begin, + const uint8_t* end) { + grpc_slice out; + out.refcount = r; + if (r != grpc_slice_refcount::NoopRefcount()) r->Ref(); + out.data.refcounted.bytes = const_cast(begin); + out.data.refcounted.length = end - begin; + return Slice(out); +} + +} // namespace experimental +} // namespace grpc_event_engine diff --git a/src/core/lib/event_engine/slice_buffer.cc b/src/core/lib/event_engine/slice_buffer.cc new file mode 100644 index 00000000000..446a87dd682 --- /dev/null +++ b/src/core/lib/event_engine/slice_buffer.cc @@ -0,0 +1,49 @@ +// Copyright 2022 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include + +#include "src/core/lib/slice/slice.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_refcount.h" + +namespace grpc_event_engine { +namespace experimental { + +void SliceBuffer::Append(Slice slice) { + grpc_slice_buffer_add(&slice_buffer_, slice.TakeCSlice()); +} + +size_t SliceBuffer::AppendIndexed(Slice slice) { + return grpc_slice_buffer_add_indexed(&slice_buffer_, slice.TakeCSlice()); +} + +Slice SliceBuffer::TakeFirst() { + return Slice(grpc_slice_buffer_take_first(&slice_buffer_)); +} + +void SliceBuffer::Prepend(Slice slice) { + grpc_slice_buffer_undo_take_first(&slice_buffer_, slice.TakeCSlice()); +} + +Slice SliceBuffer::RefSlice(size_t index) { + return Slice(grpc_slice_ref_internal(slice_buffer_.slices[index])); +} + +} // namespace experimental +} // namespace grpc_event_engine diff --git a/src/core/lib/slice/slice.h b/src/core/lib/slice/slice.h index fc560c714a1..24e2eff9b88 100644 --- a/src/core/lib/slice/slice.h +++ b/src/core/lib/slice/slice.h @@ -46,6 +46,11 @@ // MutableSlice - provides a guarantee of unique ownership, meaning the // underlying data can be mutated safely. +// This slice implementation is an extension of the event engine Slice +// implementation defined in . Changes to this +// implementation might warrant changes to the public event engine Slice +// type as well. + namespace grpc_core { namespace slice_detail { diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 3d5f8ca1c23..75e381b96cf 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -443,6 +443,8 @@ CORE_SOURCE_FILES = [ 'src/core/lib/event_engine/event_engine.cc', 'src/core/lib/event_engine/memory_allocator.cc', 'src/core/lib/event_engine/resolved_address.cc', + 'src/core/lib/event_engine/slice.cc', + 'src/core/lib/event_engine/slice_buffer.cc', 'src/core/lib/event_engine/sockaddr.cc', 'src/core/lib/gpr/alloc.cc', 'src/core/lib/gpr/atm.cc', diff --git a/test/core/event_engine/BUILD b/test/core/event_engine/BUILD index 1fb26e10c9b..8f5ff34b922 100644 --- a/test/core/event_engine/BUILD +++ b/test/core/event_engine/BUILD @@ -43,6 +43,16 @@ grpc_cc_test( ], ) +grpc_cc_test( + name = "slice_buffer_test", + srcs = ["slice_buffer_test.cc"], + external_deps = ["gtest"], + deps = [ + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) + grpc_cc_library( name = "test_init", srcs = ["test_init.cc"], diff --git a/test/core/event_engine/slice_buffer_test.cc b/test/core/event_engine/slice_buffer_test.cc new file mode 100644 index 00000000000..aa0b4ae39d1 --- /dev/null +++ b/test/core/event_engine/slice_buffer_test.cc @@ -0,0 +1,79 @@ +// Copyright 2022 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +#include +#include + +#include "src/core/lib/slice/slice.h" +#include "test/core/util/test_config.h" + +using ::grpc_event_engine::experimental::Slice; +using ::grpc_event_engine::experimental::SliceBuffer; + +static constexpr int kNewSliceLength = 100; + +Slice MakeSlice(size_t len) { + GPR_ASSERT(len > 0); + unsigned char* contents = reinterpret_cast(new char[len]); + memset(contents, 'a', len); + return Slice(grpc_slice_new(contents, len, gpr_free)); +} + +TEST(SliceBufferTest, AddAndRemoveTest) { + SliceBuffer sb; + Slice first_slice = MakeSlice(kNewSliceLength); + Slice second_slice = MakeSlice(kNewSliceLength); + Slice first_slice_copy = first_slice.Copy(); + sb.Append(std::move(first_slice)); + sb.Append(std::move(second_slice)); + ASSERT_EQ(sb.Count(), 2); + ASSERT_EQ(sb.Length(), 2 * kNewSliceLength); + Slice popped = sb.TakeFirst(); + ASSERT_EQ(popped, first_slice_copy); + ASSERT_EQ(sb.Count(), 1); + ASSERT_EQ(sb.Length(), kNewSliceLength); + sb.Prepend(std::move(popped)); + ASSERT_EQ(sb.Count(), 2); + ASSERT_EQ(sb.Length(), 2 * kNewSliceLength); + sb.Clear(); + ASSERT_EQ(sb.Count(), 0); + ASSERT_EQ(sb.Length(), 0); +} + +TEST(SliceBufferTest, SliceRefTest) { + SliceBuffer sb; + Slice first_slice = MakeSlice(kNewSliceLength); + Slice second_slice = MakeSlice(kNewSliceLength + 1); + Slice first_slice_copy = first_slice.Copy(); + Slice second_slice_copy = second_slice.Copy(); + ASSERT_EQ(sb.AppendIndexed(std::move(first_slice)), 0); + ASSERT_EQ(sb.AppendIndexed(std::move(second_slice)), 1); + Slice first_reffed = sb.RefSlice(0); + Slice second_reffed = sb.RefSlice(1); + ASSERT_EQ(first_reffed, first_slice_copy); + ASSERT_EQ(second_reffed, second_slice_copy); + ASSERT_EQ(sb.Count(), 2); + ASSERT_EQ(sb.Length(), 2 * kNewSliceLength + 1); + sb.Clear(); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++ index 3b7114f284f..13b1fbfe357 100644 --- a/tools/doxygen/Doxyfile.c++ +++ b/tools/doxygen/Doxyfile.c++ @@ -888,6 +888,8 @@ include/grpc/event_engine/internal/memory_allocator_impl.h \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ +include/grpc/event_engine/slice.h \ +include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index d890bb30c6d..bb74c642525 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -888,6 +888,8 @@ include/grpc/event_engine/internal/memory_allocator_impl.h \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ +include/grpc/event_engine/slice.h \ +include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ @@ -1933,6 +1935,8 @@ src/core/lib/event_engine/event_engine.cc \ src/core/lib/event_engine/event_engine_factory.h \ src/core/lib/event_engine/memory_allocator.cc \ src/core/lib/event_engine/resolved_address.cc \ +src/core/lib/event_engine/slice.cc \ +src/core/lib/event_engine/slice_buffer.cc \ src/core/lib/event_engine/sockaddr.cc \ src/core/lib/event_engine/sockaddr.h \ src/core/lib/gpr/alloc.cc \ diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core index 5457a2e075d..d234b2cec54 100644 --- a/tools/doxygen/Doxyfile.core +++ b/tools/doxygen/Doxyfile.core @@ -818,6 +818,8 @@ include/grpc/event_engine/internal/memory_allocator_impl.h \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ +include/grpc/event_engine/slice.h \ +include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index b17484e7919..805b50ccf08 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -818,6 +818,8 @@ include/grpc/event_engine/internal/memory_allocator_impl.h \ include/grpc/event_engine/memory_allocator.h \ include/grpc/event_engine/memory_request.h \ include/grpc/event_engine/port.h \ +include/grpc/event_engine/slice.h \ +include/grpc/event_engine/slice_buffer.h \ include/grpc/fork.h \ include/grpc/grpc.h \ include/grpc/grpc_posix.h \ @@ -1725,6 +1727,8 @@ src/core/lib/event_engine/event_engine.cc \ src/core/lib/event_engine/event_engine_factory.h \ src/core/lib/event_engine/memory_allocator.cc \ src/core/lib/event_engine/resolved_address.cc \ +src/core/lib/event_engine/slice.cc \ +src/core/lib/event_engine/slice_buffer.cc \ src/core/lib/event_engine/sockaddr.cc \ src/core/lib/event_engine/sockaddr.h \ src/core/lib/gpr/README.md \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index b308d5308d9..2e42a7dd2fa 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1961,30 +1961,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "slice_buffer_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": false - }, { "args": [], "benchmark": false, @@ -2333,6 +2309,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "test_core_slice_slice_buffer_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": false + }, { "args": [], "benchmark": false, @@ -6853,6 +6853,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "test_core_event_engine_slice_buffer_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,