This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

View File

@@ -0,0 +1,49 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_ENDPOINT_CONFIG_H
#define GRPC_EVENT_ENGINE_ENDPOINT_CONFIG_H
#include <grpc/support/port_platform.h>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace grpc_event_engine {
namespace experimental {
/// Collection of parameters used to configure client and server endpoints. The
/// \a EndpointConfig maps string-valued keys to values of type int,
/// string_view, or void pointer. Each EventEngine implementation should
/// document its set of supported configuration options.
class EndpointConfig {
public:
virtual ~EndpointConfig() = default;
// If the key points to an integer config, an integer value gets returned.
// Otherwise it returns an absl::nullopt_t
virtual absl::optional<int> GetInt(absl::string_view key) const = 0;
// If the key points to an string config, an string value gets returned.
// Otherwise it returns an absl::nullopt_t
virtual absl::optional<absl::string_view> GetString(
absl::string_view key) const = 0;
// If the key points to an void* config, a void* pointer value gets returned.
// Otherwise it returns nullptr
virtual void* GetVoidPointer(absl::string_view key) const = 0;
};
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_ENDPOINT_CONFIG_H

View File

@@ -0,0 +1,500 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_EVENT_ENGINE_H
#define GRPC_EVENT_ENGINE_EVENT_ENGINE_H
#include <grpc/support/port_platform.h>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include <grpc/event_engine/endpoint_config.h>
#include <grpc/event_engine/extensible.h>
#include <grpc/event_engine/memory_allocator.h>
#include <grpc/event_engine/port.h>
#include <grpc/event_engine/slice_buffer.h>
// TODO(vigneshbabu): Define the Endpoint::Write metrics collection system
namespace grpc_event_engine {
namespace experimental {
////////////////////////////////////////////////////////////////////////////////
/// The EventEngine Interface
///
/// Overview
/// --------
///
/// The EventEngine encapsulates all platform-specific behaviors related to low
/// level network I/O, timers, asynchronous execution, and DNS resolution.
///
/// This interface allows developers to provide their own event management and
/// network stacks. Motivating uses cases for supporting custom EventEngines
/// include the ability to hook into external event loops, and using different
/// EventEngine instances for each channel to better insulate network I/O and
/// callback processing from other channels.
///
/// A default cross-platform EventEngine instance is provided by gRPC.
///
/// Lifespan and Ownership
/// ----------------------
///
/// gRPC takes shared ownership of EventEngines via std::shared_ptrs to ensure
/// that the engines remain available until they are no longer needed. Depending
/// on the use case, engines may live until gRPC is shut down.
///
/// EXAMPLE USAGE (Not yet implemented)
///
/// Custom EventEngines can be specified per channel, and allow configuration
/// for both clients and servers. To set a custom EventEngine for a client
/// channel, you can do something like the following:
///
/// ChannelArguments args;
/// std::shared_ptr<EventEngine> engine = std::make_shared<MyEngine>(...);
/// args.SetEventEngine(engine);
/// MyAppClient client(grpc::CreateCustomChannel(
/// "localhost:50051", grpc::InsecureChannelCredentials(), args));
///
/// A gRPC server can use a custom EventEngine by calling the
/// ServerBuilder::SetEventEngine method:
///
/// ServerBuilder builder;
/// std::shared_ptr<EventEngine> engine = std::make_shared<MyEngine>(...);
/// builder.SetEventEngine(engine);
/// std::unique_ptr<Server> server(builder.BuildAndStart());
/// server->Wait();
///
///
/// Blocking EventEngine Callbacks
/// ------------------------------
///
/// Doing blocking work in EventEngine callbacks is generally not advisable.
/// While gRPC's default EventEngine implementations have some capacity to scale
/// their thread pools to avoid starvation, this is not an instantaneous
/// process. Further, user-provided EventEngines may not be optimized to handle
/// excessive blocking work at all.
///
/// *Best Practice* : Occasional blocking work may be fine, but we do not
/// recommend running a mostly blocking workload in EventEngine threads.
///
///
/// Thread-safety guarantees
/// ------------------------
///
/// All EventEngine methods are guaranteed to be thread-safe, no external
/// synchronization is required to call any EventEngine method. Please note that
/// this does not apply to application callbacks, which may be run concurrently;
/// application state synchronization must be managed by the application.
///
////////////////////////////////////////////////////////////////////////////////
class EventEngine : public std::enable_shared_from_this<EventEngine>,
public Extensible {
public:
/// A duration between two events.
///
/// Throughout the EventEngine API durations are used to express how long
/// until an action should be performed.
using Duration = std::chrono::duration<int64_t, std::nano>;
/// A custom closure type for EventEngine task execution.
///
/// Throughout the EventEngine API, \a Closure ownership is retained by the
/// caller - the EventEngine will never delete a Closure, and upon
/// cancellation, the EventEngine will simply forget the Closure exists. The
/// caller is responsible for all necessary cleanup.
class Closure {
public:
Closure() = default;
// Closure's are an interface, and thus non-copyable.
Closure(const Closure&) = delete;
Closure& operator=(const Closure&) = delete;
// Polymorphic type => virtual destructor
virtual ~Closure() = default;
// Run the contained code.
virtual void Run() = 0;
};
/// Represents a scheduled task.
///
/// \a TaskHandles are returned by \a Run* methods, and can be given to the
/// \a Cancel method.
struct TaskHandle {
intptr_t keys[2];
static const GRPC_DLL TaskHandle kInvalid;
friend bool operator==(const TaskHandle& lhs, const TaskHandle& rhs);
friend bool operator!=(const TaskHandle& lhs, const TaskHandle& rhs);
};
/// A handle to a cancellable connection attempt.
///
/// Returned by \a Connect, and can be passed to \a CancelConnect.
struct ConnectionHandle {
intptr_t keys[2];
static const GRPC_DLL ConnectionHandle kInvalid;
friend bool operator==(const ConnectionHandle& lhs,
const ConnectionHandle& rhs);
friend bool operator!=(const ConnectionHandle& lhs,
const ConnectionHandle& rhs);
};
/// Thin wrapper around a platform-specific sockaddr type. A sockaddr struct
/// exists on all platforms that gRPC supports.
///
/// Platforms are expected to provide definitions for:
/// * sockaddr
/// * sockaddr_in
/// * sockaddr_in6
class ResolvedAddress {
public:
static constexpr socklen_t MAX_SIZE_BYTES = 128;
ResolvedAddress(const sockaddr* address, socklen_t size);
ResolvedAddress() = default;
ResolvedAddress(const ResolvedAddress&) = default;
const struct sockaddr* address() const;
socklen_t size() const;
private:
char address_[MAX_SIZE_BYTES] = {};
socklen_t size_ = 0;
};
/// One end of a connection between a gRPC client and server. Endpoints are
/// created when connections are established, and Endpoint operations are
/// gRPC's primary means of communication.
///
/// Endpoints must use the provided MemoryAllocator for all data buffer memory
/// allocations. gRPC allows applications to set memory constraints per
/// Channel or Server, and the implementation depends on all dynamic memory
/// allocation being handled by the quota system.
class Endpoint : public Extensible {
public:
/// Shuts down all connections and invokes all pending read or write
/// callbacks with an error status.
virtual ~Endpoint() = default;
/// A struct representing optional arguments that may be provided to an
/// EventEngine Endpoint Read API call.
///
/// Passed as argument to an Endpoint \a Read
struct ReadArgs {
// A suggestion to the endpoint implementation to read at-least the
// specified number of bytes over the network connection before marking
// the endpoint read operation as complete. gRPC may use this argument
// to minimize the number of endpoint read API calls over the lifetime
// of a connection.
int64_t read_hint_bytes;
};
/// Reads data from the Endpoint.
///
/// When data is available on the connection, that data is moved into the
/// \a buffer. If the read succeeds immediately, it returns true and the \a
/// on_read callback is not executed. Otherwise it returns false and the \a
/// on_read callback executes asynchronously when the read completes. The
/// caller must ensure that the callback has access to the buffer when it
/// executes. Ownership of the buffer is not transferred. Valid slices *may*
/// be placed into the buffer even if the callback is invoked with a non-OK
/// Status.
///
/// There can be at most one outstanding read per Endpoint at any given
/// time. An outstanding read is one in which the \a on_read callback has
/// not yet been executed for some previous call to \a Read. If an attempt
/// is made to call \a Read while a previous read is still outstanding, the
/// \a EventEngine must abort.
///
/// For failed read operations, implementations should pass the appropriate
/// statuses to \a on_read. For example, callbacks might expect to receive
/// CANCELLED on endpoint shutdown.
virtual bool Read(absl::AnyInvocable<void(absl::Status)> on_read,
SliceBuffer* buffer, const ReadArgs* args) = 0;
/// A struct representing optional arguments that may be provided to an
/// EventEngine Endpoint Write API call.
///
/// Passed as argument to an Endpoint \a Write
struct WriteArgs {
// Represents private information that may be passed by gRPC for
// select endpoints expected to be used only within google.
void* google_specific = nullptr;
// A suggestion to the endpoint implementation to group data to be written
// into frames of the specified max_frame_size. gRPC may use this
// argument to dynamically control the max sizes of frames sent to a
// receiver in response to high receiver memory pressure.
int64_t max_frame_size;
};
/// Writes data out on the connection.
///
/// If the write succeeds immediately, it returns true and the
/// \a on_writable callback is not executed. Otherwise it returns false and
/// the \a on_writable callback is called asynchronously when the connection
/// is ready for more data. The Slices within the \a data buffer may be
/// mutated at will by the Endpoint until \a on_writable is called. The \a
/// data SliceBuffer will remain valid after calling \a Write, but its state
/// is otherwise undefined. All bytes in \a data must have been written
/// before calling \a on_writable unless an error has occurred.
///
/// There can be at most one outstanding write per Endpoint at any given
/// time. An outstanding write is one in which the \a on_writable callback
/// has not yet been executed for some previous call to \a Write. If an
/// attempt is made to call \a Write while a previous write is still
/// outstanding, the \a EventEngine must abort.
///
/// For failed write operations, implementations should pass the appropriate
/// statuses to \a on_writable. For example, callbacks might expect to
/// receive CANCELLED on endpoint shutdown.
virtual bool Write(absl::AnyInvocable<void(absl::Status)> on_writable,
SliceBuffer* data, const WriteArgs* args) = 0;
/// Returns an address in the format described in DNSResolver. The returned
/// values are expected to remain valid for the life of the Endpoint.
virtual const ResolvedAddress& GetPeerAddress() const = 0;
virtual const ResolvedAddress& GetLocalAddress() const = 0;
};
/// Called when a new connection is established.
///
/// If the connection attempt was not successful, implementations should pass
/// the appropriate statuses to this callback. For example, callbacks might
/// expect to receive DEADLINE_EXCEEDED statuses when appropriate, or
/// CANCELLED statuses on EventEngine shutdown.
using OnConnectCallback =
absl::AnyInvocable<void(absl::StatusOr<std::unique_ptr<Endpoint>>)>;
/// Listens for incoming connection requests from gRPC clients and initiates
/// request processing once connections are established.
class Listener : public Extensible {
public:
/// Called when the listener has accepted a new client connection.
using AcceptCallback = absl::AnyInvocable<void(
std::unique_ptr<Endpoint>, MemoryAllocator memory_allocator)>;
virtual ~Listener() = default;
/// Bind an address/port to this Listener.
///
/// It is expected that multiple addresses/ports can be bound to this
/// Listener before Listener::Start has been called. Returns either the
/// bound port or an appropriate error status.
virtual absl::StatusOr<int> Bind(const ResolvedAddress& addr) = 0;
virtual absl::Status Start() = 0;
};
/// Factory method to create a network listener / server.
///
/// Once a \a Listener is created and started, the \a on_accept callback will
/// be called once asynchronously for each established connection. This method
/// may return a non-OK status immediately if an error was encountered in any
/// synchronous steps required to create the Listener. In this case,
/// \a on_shutdown will never be called.
///
/// If this method returns a Listener, then \a on_shutdown will be invoked
/// exactly once when the Listener is shut down, and only after all
/// \a on_accept callbacks have finished executing. The status passed to it
/// will indicate if there was a problem during shutdown.
///
/// The provided \a MemoryAllocatorFactory is used to create \a
/// MemoryAllocators for Endpoint construction.
virtual absl::StatusOr<std::unique_ptr<Listener>> CreateListener(
Listener::AcceptCallback on_accept,
absl::AnyInvocable<void(absl::Status)> on_shutdown,
const EndpointConfig& config,
std::unique_ptr<MemoryAllocatorFactory> memory_allocator_factory) = 0;
/// Creates a client network connection to a remote network listener.
///
/// Even in the event of an error, it is expected that the \a on_connect
/// callback will be asynchronously executed exactly once by the EventEngine.
/// A connection attempt can be cancelled using the \a CancelConnect method.
///
/// Implementation Note: it is important that the \a memory_allocator be used
/// for all read/write buffer allocations in the EventEngine implementation.
/// This allows gRPC's \a ResourceQuota system to monitor and control memory
/// usage with graceful degradation mechanisms. Please see the \a
/// MemoryAllocator API for more information.
virtual ConnectionHandle Connect(OnConnectCallback on_connect,
const ResolvedAddress& addr,
const EndpointConfig& args,
MemoryAllocator memory_allocator,
Duration timeout) = 0;
/// Request cancellation of a connection attempt.
///
/// If the associated connection has already been completed, it will not be
/// cancelled, and this method will return false.
///
/// If the associated connection has not been completed, it will be cancelled,
/// and this method will return true. The \a OnConnectCallback will not be
/// called, and \a on_connect will be destroyed before this method returns.
virtual bool CancelConnect(ConnectionHandle handle) = 0;
/// Provides asynchronous resolution.
///
/// This object has a destruction-is-cancellation semantic.
/// Implementations should make sure that all pending requests are cancelled
/// when the object is destroyed and all pending callbacks will be called
/// shortly. If cancellation races with request completion, implementations
/// may choose to either cancel or satisfy the request.
class DNSResolver {
public:
/// Optional configuration for DNSResolvers.
struct ResolverOptions {
/// If empty, default DNS servers will be used.
/// Must be in the "IP:port" format as described in naming.md.
std::string dns_server;
};
/// DNS SRV record type.
struct SRVRecord {
std::string host;
int port = 0;
int priority = 0;
int weight = 0;
};
/// Called with the collection of sockaddrs that were resolved from a given
/// target address.
using LookupHostnameCallback =
absl::AnyInvocable<void(absl::StatusOr<std::vector<ResolvedAddress>>)>;
/// Called with a collection of SRV records.
using LookupSRVCallback =
absl::AnyInvocable<void(absl::StatusOr<std::vector<SRVRecord>>)>;
/// Called with the result of a TXT record lookup
using LookupTXTCallback =
absl::AnyInvocable<void(absl::StatusOr<std::vector<std::string>>)>;
virtual ~DNSResolver() = default;
/// Asynchronously resolve an address.
///
/// \a default_port may be a non-numeric named service port, and will only
/// be used if \a address does not already contain a port component.
///
/// When the lookup is complete or cancelled, the \a on_resolve callback
/// will be invoked with a status indicating the success or failure of the
/// lookup. Implementations should pass the appropriate statuses to the
/// callback. For example, callbacks might expect to receive CANCELLED or
/// NOT_FOUND.
virtual void LookupHostname(LookupHostnameCallback on_resolve,
absl::string_view name,
absl::string_view default_port) = 0;
/// Asynchronously perform an SRV record lookup.
///
/// \a on_resolve has the same meaning and expectations as \a
/// LookupHostname's \a on_resolve callback.
virtual void LookupSRV(LookupSRVCallback on_resolve,
absl::string_view name) = 0;
/// Asynchronously perform a TXT record lookup.
///
/// \a on_resolve has the same meaning and expectations as \a
/// LookupHostname's \a on_resolve callback.
virtual void LookupTXT(LookupTXTCallback on_resolve,
absl::string_view name) = 0;
};
/// At time of destruction, the EventEngine must have no active
/// responsibilities. EventEngine users (applications) are responsible for
/// cancelling all tasks and DNS lookups, shutting down listeners and
/// endpoints, prior to EventEngine destruction. If there are any outstanding
/// tasks, any running listeners, etc. at time of EventEngine destruction,
/// that is an invalid use of the API, and it will result in undefined
/// behavior.
virtual ~EventEngine() = default;
// TODO(nnoble): consider whether we can remove this method before we
// de-experimentalize this API.
virtual bool IsWorkerThread() = 0;
/// Creates and returns an instance of a DNSResolver, optionally configured by
/// the \a options struct. This method may return a non-OK status if an error
/// occurred when creating the DNSResolver. If the caller requests a custom
/// DNS server, and the EventEngine implementation does not support it, this
/// must return an error.
virtual absl::StatusOr<std::unique_ptr<DNSResolver>> GetDNSResolver(
const DNSResolver::ResolverOptions& options) = 0;
/// Asynchronously executes a task as soon as possible.
///
/// \a Closures passed to \a Run cannot be cancelled. The \a closure will not
/// be deleted after it has been run, ownership remains with the caller.
///
/// Implementations must not execute the closure in the calling thread before
/// \a Run returns. For example, if the caller must release a lock before the
/// closure can proceed, running the closure immediately would cause a
/// deadlock.
virtual void Run(Closure* closure) = 0;
/// Asynchronously executes a task as soon as possible.
///
/// \a Closures passed to \a Run cannot be cancelled. Unlike the overloaded \a
/// Closure alternative, the absl::AnyInvocable version's \a closure will be
/// deleted by the EventEngine after the closure has been run.
///
/// This version of \a Run may be less performant than the \a Closure version
/// in some scenarios. This overload is useful in situations where performance
/// is not a critical concern.
///
/// Implementations must not execute the closure in the calling thread before
/// \a Run returns.
virtual void Run(absl::AnyInvocable<void()> closure) = 0;
/// Synonymous with scheduling an alarm to run after duration \a when.
///
/// The \a closure will execute when time \a when arrives unless it has been
/// cancelled via the \a Cancel method. If cancelled, the closure will not be
/// run, nor will it be deleted. Ownership remains with the caller.
///
/// Implementations must not execute the closure in the calling thread before
/// \a RunAfter returns.
virtual TaskHandle RunAfter(Duration when, Closure* closure) = 0;
/// Synonymous with scheduling an alarm to run after duration \a when.
///
/// The \a closure will execute when time \a when arrives unless it has been
/// cancelled via the \a Cancel method. If cancelled, the closure will not be
/// run. Unlike the overloaded \a Closure alternative, the absl::AnyInvocable
/// version's \a closure will be deleted by the EventEngine after the closure
/// has been run, or upon cancellation.
///
/// This version of \a RunAfter may be less performant than the \a Closure
/// version in some scenarios. This overload is useful in situations where
/// performance is not a critical concern.
///
/// Implementations must not execute the closure in the calling thread before
/// \a RunAfter returns.
virtual TaskHandle RunAfter(Duration when,
absl::AnyInvocable<void()> closure) = 0;
/// Request cancellation of a task.
///
/// If the associated closure cannot be cancelled for any reason, this
/// function will return false.
///
/// If the associated closure can be cancelled, the associated callback will
/// never be run, and this method will return true. If the callback type was
/// an absl::AnyInvocable, it will be destroyed before the method returns.
virtual bool Cancel(TaskHandle handle) = 0;
};
/// Replace gRPC's default EventEngine factory.
///
/// Applications may call \a SetEventEngineFactory at any time to replace the
/// default factory used within gRPC. EventEngines will be created when
/// necessary, when they are otherwise not provided by the application.
///
/// To be certain that none of the gRPC-provided built-in EventEngines are
/// created, applications must set a custom EventEngine factory method *before*
/// grpc is initialized.
void SetEventEngineFactory(
absl::AnyInvocable<std::unique_ptr<EventEngine>()> factory);
/// Reset gRPC's EventEngine factory to the built-in default.
///
/// Applications that have called \a SetEventEngineFactory can remove their
/// custom factory using this method. The built-in EventEngine factories will be
/// used going forward. This has no affect on any EventEngines that were created
/// using the previous factories.
void EventEngineFactoryReset();
/// Create an EventEngine using the default factory.
std::unique_ptr<EventEngine> CreateEventEngine();
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_EVENT_ENGINE_H

View File

@@ -0,0 +1,68 @@
// Copyright 2024 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.
#ifndef GRPC_EVENT_ENGINE_EXTENSIBLE_H
#define GRPC_EVENT_ENGINE_EXTENSIBLE_H
#include <grpc/support/port_platform.h>
#include "absl/strings/string_view.h"
namespace grpc_event_engine {
namespace experimental {
class Extensible {
public:
/// A method which allows users to query whether an implementation supports a
/// specified extension. The name of the extension is provided as an input.
///
/// An extension could be any type with a unique string id. Each extension may
/// support additional capabilities and if the implementation supports the
/// queried extension, it should return a valid pointer to the extension type.
///
/// E.g., use case of an EventEngine::Endpoint supporting a custom extension.
///
/// class CustomEndpointExtension {
/// public:
/// static std::string EndpointExtensionName() {
/// return "my.namespace.extension_name";
/// }
/// virtual void Process() = 0;
/// }
///
/// class CustomEndpoint :
/// public EventEngine::Endpoint, public CustomEndpointExtension {
/// public:
/// void* QueryExtension(absl::string_view id) override {
/// if (id == CustomEndpointExtension::EndpointExtensionName()) {
/// return static_cast<CustomEndpointExtension*>(this);
/// }
/// return nullptr;
/// }
/// void Process() override { ... }
/// ...
/// }
///
/// auto endpoint =
/// static_cast<CustomEndpointExtension*>(endpoint->QueryExtension(
/// CustomEndpointExtension::EndpointExtensionName()));
/// if (endpoint != nullptr) endpoint->Process();
///
virtual void* QueryExtension(absl::string_view /*id*/) { return nullptr; }
};
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_EXTENSIBLE_H

View File

@@ -0,0 +1,74 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H
#define GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H
#include <grpc/support/port_platform.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
#include <grpc/event_engine/memory_request.h>
#include <grpc/slice.h>
namespace grpc_event_engine {
namespace experimental {
namespace internal {
/// Underlying memory allocation interface.
/// This is an internal interface, not intended to be used by users.
/// Its interface is subject to change at any time.
class MemoryAllocatorImpl
: public std::enable_shared_from_this<MemoryAllocatorImpl> {
public:
MemoryAllocatorImpl() {}
virtual ~MemoryAllocatorImpl() {}
MemoryAllocatorImpl(const MemoryAllocatorImpl&) = delete;
MemoryAllocatorImpl& operator=(const MemoryAllocatorImpl&) = delete;
/// Reserve bytes from the quota.
/// If we enter overcommit, reclamation will begin concurrently.
/// Returns the number of bytes reserved.
/// If MemoryRequest is invalid, this function will abort.
/// If MemoryRequest is valid, this function is infallible, and will always
/// succeed at reserving the some number of bytes between request.min() and
/// request.max() inclusively.
virtual size_t Reserve(MemoryRequest request) = 0;
/// Allocate a slice, using MemoryRequest to size the number of returned
/// bytes. For a variable length request, check the returned slice length to
/// verify how much memory was allocated. Takes care of reserving memory for
/// any relevant control structures also.
virtual grpc_slice MakeSlice(MemoryRequest request) = 0;
/// Release some bytes that were previously reserved.
/// If more bytes are released than were reserved, we will have undefined
/// behavior.
virtual void Release(size_t n) = 0;
/// Shutdown this allocator.
/// Further usage of Reserve() is undefined behavior.
virtual void Shutdown() = 0;
};
} // namespace internal
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_INTERNAL_MEMORY_ALLOCATOR_IMPL_H

View File

@@ -0,0 +1,79 @@
// 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_INTERNAL_SLICE_CAST_H
#define GRPC_EVENT_ENGINE_INTERNAL_SLICE_CAST_H
namespace grpc_event_engine {
namespace experimental {
namespace internal {
// Opt-in trait class for slice conversions.
// Declare a specialization of this class for any types that are compatible
// with `SliceCast`. Both ways need to be declared (i.e. if
// SliceCastable<A,B> exists, you should declare
// SliceCastable<B,A> too).
// The type has no members, it's just the existance of the specialization that
// unlocks SliceCast usage for a type pair.
template <typename Result, typename T>
struct SliceCastable;
// This is strictly too wide, but consider all types to be SliceCast-able to
// themselves.
// Unfortunately this allows `const int& x = SliceCast<int>(x);` which is kind
// of bogus.
template <typename A>
struct SliceCastable<A, A> {};
// Cast to `const Result&` from `const T&` without any runtime checks.
// This is only valid if `sizeof(Result) == sizeof(T)`, and if `Result`, `T` are
// opted in as compatible via `SliceCastable`.
template <typename Result, typename T>
const Result& SliceCast(const T& value, SliceCastable<Result, T> = {}) {
// Insist upon sizes being equal to catch mismatches.
// We assume if sizes are opted in and sizes are equal then yes, these two
// types are expected to be layout compatible and actually appear to be.
static_assert(sizeof(Result) == sizeof(T), "size mismatch");
return reinterpret_cast<const Result&>(value);
}
// Cast to `Result&` from `T&` without any runtime checks.
// This is only valid if `sizeof(Result) == sizeof(T)`, and if `Result`, `T` are
// opted in as compatible via `SliceCastable`.
template <typename Result, typename T>
Result& SliceCast(T& value, SliceCastable<Result, T> = {}) {
// Insist upon sizes being equal to catch mismatches.
// We assume if sizes are opted in and sizes are equal then yes, these two
// types are expected to be layout compatible and actually appear to be.
static_assert(sizeof(Result) == sizeof(T), "size mismatch");
return reinterpret_cast<Result&>(value);
}
// Cast to `Result&&` from `T&&` without any runtime checks.
// This is only valid if `sizeof(Result) == sizeof(T)`, and if `Result`, `T` are
// opted in as compatible via `SliceCastable`.
template <typename Result, typename T>
Result&& SliceCast(T&& value, SliceCastable<Result, T> = {}) {
// Insist upon sizes being equal to catch mismatches.
// We assume if sizes are opted in and sizes are equal then yes, these two
// types are expected to be layout compatible and actually appear to be.
static_assert(sizeof(Result) == sizeof(T), "size mismatch");
return reinterpret_cast<Result&&>(value);
}
} // namespace internal
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_INTERNAL_SLICE_CAST_H

View File

@@ -0,0 +1,213 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H
#define GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H
#include <grpc/support/port_platform.h>
#include <stdlib.h> // for abort()
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
#include <grpc/event_engine/internal/memory_allocator_impl.h>
#include <grpc/slice.h>
namespace grpc_event_engine {
namespace experimental {
// Tracks memory allocated by one system.
// Is effectively a thin wrapper/smart pointer for a MemoryAllocatorImpl,
// providing a convenient and stable API.
class MemoryAllocator {
public:
/// Construct a MemoryAllocator given an internal::MemoryAllocatorImpl
/// implementation. The constructed MemoryAllocator will call
/// MemoryAllocatorImpl::Shutdown() upon destruction.
explicit MemoryAllocator(
std::shared_ptr<internal::MemoryAllocatorImpl> allocator)
: allocator_(std::move(allocator)) {}
// Construct an invalid MemoryAllocator.
MemoryAllocator() : allocator_(nullptr) {}
~MemoryAllocator() {
if (allocator_ != nullptr) allocator_->Shutdown();
}
MemoryAllocator(const MemoryAllocator&) = delete;
MemoryAllocator& operator=(const MemoryAllocator&) = delete;
MemoryAllocator(MemoryAllocator&&) = default;
MemoryAllocator& operator=(MemoryAllocator&&) = default;
/// Drop the underlying allocator and make this an empty object.
/// The object will not be usable after this call unless it's a valid
/// allocator is moved into it.
void Reset() {
auto a = std::move(allocator_);
if (a != nullptr) a->Shutdown();
}
/// Reserve bytes from the quota.
/// If we enter overcommit, reclamation will begin concurrently.
/// Returns the number of bytes reserved.
size_t Reserve(MemoryRequest request) { return allocator_->Reserve(request); }
/// Release some bytes that were previously reserved.
void Release(size_t n) { return allocator_->Release(n); }
//
// The remainder of this type are helper functions implemented in terms of
// Reserve/Release.
//
/// An automatic releasing reservation of memory.
class Reservation {
public:
Reservation() = default;
Reservation(const Reservation&) = delete;
Reservation& operator=(const Reservation&) = delete;
Reservation(Reservation&&) = default;
Reservation& operator=(Reservation&&) = default;
~Reservation() {
if (allocator_ != nullptr) allocator_->Release(size_);
}
private:
friend class MemoryAllocator;
Reservation(std::shared_ptr<internal::MemoryAllocatorImpl> allocator,
size_t size)
: allocator_(std::move(allocator)), size_(size) {}
std::shared_ptr<internal::MemoryAllocatorImpl> allocator_;
size_t size_ = 0;
};
/// Reserve bytes from the quota and automatically release them when
/// Reservation is destroyed.
Reservation MakeReservation(MemoryRequest request) {
return Reservation(allocator_, Reserve(request));
}
/// Allocate a new object of type T, with constructor arguments.
/// The returned type is wrapped, and upon destruction the reserved memory
/// will be released to the allocator automatically. As such, T must have a
/// virtual destructor so we can insert the necessary hook.
template <typename T, typename... Args>
typename std::enable_if<std::has_virtual_destructor<T>::value, T*>::type New(
Args&&... args) {
// Wrap T such that when it's destroyed, we can release memory back to the
// allocator.
class Wrapper final : public T {
public:
explicit Wrapper(std::shared_ptr<internal::MemoryAllocatorImpl> allocator,
Args&&... args)
: T(std::forward<Args>(args)...), allocator_(std::move(allocator)) {}
~Wrapper() override { allocator_->Release(sizeof(*this)); }
private:
const std::shared_ptr<internal::MemoryAllocatorImpl> allocator_;
};
Reserve(sizeof(Wrapper));
return new Wrapper(allocator_, std::forward<Args>(args)...);
}
/// Construct a unique_ptr immediately.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args&&... args) {
return std::unique_ptr<T>(New<T>(std::forward<Args>(args)...));
}
/// Allocate a slice, using MemoryRequest to size the number of returned
/// bytes. For a variable length request, check the returned slice length to
/// verify how much memory was allocated. Takes care of reserving memory for
/// any relevant control structures also.
grpc_slice MakeSlice(MemoryRequest request) {
return allocator_->MakeSlice(request);
}
/// A C++ allocator for containers of T.
template <typename T>
class Container {
public:
using value_type = T;
/// Construct the allocator: \a underlying_allocator is borrowed, and must
/// outlive this object.
explicit Container(MemoryAllocator* underlying_allocator)
: underlying_allocator_(underlying_allocator) {}
template <typename U>
explicit Container(const Container<U>& other)
: underlying_allocator_(other.underlying_allocator()) {}
MemoryAllocator* underlying_allocator() const {
return underlying_allocator_;
}
T* allocate(size_t n) {
underlying_allocator_->Reserve(n * sizeof(T));
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, size_t n) {
::operator delete(p);
underlying_allocator_->Release(n * sizeof(T));
}
private:
MemoryAllocator* underlying_allocator_;
};
protected:
/// Return a pointer to the underlying implementation.
/// Note that the interface of said implementation is unstable and likely to
/// change at any time.
internal::MemoryAllocatorImpl* get_internal_impl_ptr() {
return allocator_.get();
}
const internal::MemoryAllocatorImpl* get_internal_impl_ptr() const {
return allocator_.get();
}
private:
std::shared_ptr<internal::MemoryAllocatorImpl> allocator_;
};
// Wrapper type around std::vector to make initialization against a
// MemoryAllocator based container allocator easy.
template <typename T>
class Vector : public std::vector<T, MemoryAllocator::Container<T>> {
public:
explicit Vector(MemoryAllocator* allocator)
: std::vector<T, MemoryAllocator::Container<T>>(
MemoryAllocator::Container<T>(allocator)) {}
};
class MemoryAllocatorFactory {
public:
virtual ~MemoryAllocatorFactory() = default;
/// On Endpoint creation, call \a CreateMemoryAllocator to create a new
/// allocator for the endpoint.
/// \a name is used to label the memory allocator in debug logs.
/// Typically we'll want to:
/// auto allocator = factory->CreateMemoryAllocator(peer_address_string);
/// auto* endpoint = allocator->New<MyEndpoint>(std::move(allocator), ...);
virtual MemoryAllocator CreateMemoryAllocator(absl::string_view name) = 0;
};
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_MEMORY_ALLOCATOR_H

View File

@@ -0,0 +1,57 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_MEMORY_REQUEST_H
#define GRPC_EVENT_ENGINE_MEMORY_REQUEST_H
#include <grpc/support/port_platform.h>
#include <stddef.h>
#include "absl/strings/string_view.h"
namespace grpc_event_engine {
namespace experimental {
/// Reservation request - how much memory do we want to allocate?
class MemoryRequest {
public:
/// Request a fixed amount of memory.
// NOLINTNEXTLINE(google-explicit-constructor)
MemoryRequest(size_t n) : min_(n), max_(n) {}
/// Request a range of memory.
/// Requires: \a min <= \a max.
/// Requires: \a max <= max_size()
MemoryRequest(size_t min, size_t max) : min_(min), max_(max) {}
/// Maximum allowable request size - hard coded to 1GB.
static constexpr size_t max_allowed_size() { return 1024 * 1024 * 1024; }
/// Increase the size by \a amount.
/// Undefined behavior if min() + amount or max() + amount overflows.
MemoryRequest Increase(size_t amount) const {
return MemoryRequest(min_ + amount, max_ + amount);
}
size_t min() const { return min_; }
size_t max() const { return max_; }
private:
size_t min_;
size_t max_;
};
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_MEMORY_REQUEST_H

View File

@@ -0,0 +1,39 @@
// Copyright 2021 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.
#ifndef GRPC_EVENT_ENGINE_PORT_H
#define GRPC_EVENT_ENGINE_PORT_H
#include <grpc/support/port_platform.h>
// Platform-specific sockaddr includes
#if defined(GPR_ANDROID) || defined(GPR_LINUX) || defined(GPR_APPLE) || \
defined(GPR_FREEBSD) || defined(GPR_OPENBSD) || defined(GPR_SOLARIS) || \
defined(GPR_AIX) || defined(GPR_NACL) || defined(GPR_FUCHSIA) || \
defined(GRPC_POSIX_SOCKET) || defined(GPR_NETBSD)
#define GRPC_EVENT_ENGINE_POSIX
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#elif defined(GPR_WINDOWS)
#include <winsock2.h>
#include <ws2tcpip.h>
// must be included after the above
#include <mswsock.h>
#else
#error UNKNOWN PLATFORM
#endif
#endif // GRPC_EVENT_ENGINE_PORT_H

View File

@@ -0,0 +1,311 @@
// 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 <grpc/support/port_platform.h>
#include <string.h>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include <grpc/event_engine/internal/slice_cast.h>
#include <grpc/slice.h>
#include <grpc/support/log.h>
// 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<const char*>(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 <typename Out>
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));
}
static Out FromCopiedBuffer(const uint8_t* p, size_t len) {
return Out(
grpc_slice_from_copied_buffer(reinterpret_cast<const char*>(p), len));
}
template <typename Buffer>
static Out FromCopiedBuffer(const Buffer& buffer) {
return FromCopiedBuffer(reinterpret_cast<const char*>(buffer.data()),
buffer.size());
}
};
} // namespace slice_detail
class GPR_MSVC_EMPTY_BASE_CLASS_WORKAROUND MutableSlice
: public slice_detail::BaseSlice,
public slice_detail::CopyConstructors<MutableSlice> {
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 GPR_MSVC_EMPTY_BASE_CLASS_WORKAROUND Slice
: public slice_detail::BaseSlice,
public slice_detail::CopyConstructors<Slice> {
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 internal {
template <>
struct SliceCastable<Slice, grpc_slice> {};
template <>
struct SliceCastable<grpc_slice, Slice> {};
template <>
struct SliceCastable<MutableSlice, grpc_slice> {};
template <>
struct SliceCastable<grpc_slice, MutableSlice> {};
template <>
struct SliceCastable<MutableSlice, Slice> {};
template <>
struct SliceCastable<Slice, MutableSlice> {};
} // namespace internal
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_SLICE_H

View File

@@ -0,0 +1,159 @@
// 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 <grpc/support/port_platform.h>
#include <string.h>
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/utility/utility.h"
#include <grpc/event_engine/internal/slice_cast.h>
#include <grpc/event_engine/slice.h>
#include <grpc/impl/codegen/slice.h>
#include <grpc/slice.h>
#include <grpc/slice_buffer.h>
#include <grpc/support/log.h>
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 EventEngine's API is no longer
/// an experimental API.
class SliceBuffer {
public:
SliceBuffer() { grpc_slice_buffer_init(&slice_buffer_); }
SliceBuffer(const SliceBuffer& other) = delete;
SliceBuffer(SliceBuffer&& other) noexcept
: slice_buffer_(other.slice_buffer_) {
grpc_slice_buffer_init(&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_); }
SliceBuffer& operator=(const SliceBuffer&) = delete;
SliceBuffer& operator=(SliceBuffer&& other) noexcept {
grpc_slice_buffer_swap(&slice_buffer_, &other.slice_buffer_);
return *this;
}
/// Swap the contents of this SliceBuffer with the contents of another.
void Swap(SliceBuffer& other) {
grpc_slice_buffer_swap(&slice_buffer_, &other.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/deletes the last n bytes in the SliceBuffer and add it to the
/// other SliceBuffer
void MoveLastNBytesIntoSliceBuffer(size_t n, SliceBuffer& other) {
grpc_slice_buffer_trim_end(&slice_buffer_, n, &other.slice_buffer_);
}
/// Move the first n bytes of the SliceBuffer into the other SliceBuffer
void MoveFirstNBytesIntoSliceBuffer(size_t n, SliceBuffer& other) {
grpc_slice_buffer_move_first(&slice_buffer_, n, &other.slice_buffer_);
}
/// 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);
/// Array access into the SliceBuffer. It returns a non mutable reference to
/// the slice at the specified index
const Slice& operator[](size_t index) const {
return internal::SliceCast<Slice>(slice_buffer_.slices[index]);
}
/// Return mutable reference to the slice at the specified index
Slice& MutableSliceAt(size_t index) const {
return internal::SliceCast<Slice>(slice_buffer_.slices[index]);
}
/// The total number of bytes held by the SliceBuffer
size_t Length() const { return slice_buffer_.length; }
/// Return a pointer to the back raw grpc_slice_buffer
grpc_slice_buffer* c_slice_buffer() { return &slice_buffer_; }
// Returns a SliceBuffer that transfers slices into this new SliceBuffer,
// leaving the input parameter empty.
static SliceBuffer TakeCSliceBuffer(grpc_slice_buffer& slice_buffer) {
return SliceBuffer(&slice_buffer);
}
private:
// Transfers slices into this new SliceBuffer, leaving the parameter empty.
// Does not take ownership of the slice_buffer argument.
explicit SliceBuffer(grpc_slice_buffer* slice_buffer) {
grpc_slice_buffer_init(&slice_buffer_);
grpc_slice_buffer_swap(&slice_buffer_, slice_buffer);
}
/// The backing raw slice buffer.
grpc_slice_buffer slice_buffer_;
};
} // namespace experimental
} // namespace grpc_event_engine
#endif // GRPC_EVENT_ENGINE_SLICE_BUFFER_H