#ifndef AWS_S3_BUFFER_POOL_H #define AWS_S3_BUFFER_POOL_H #include #include #include /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ /** * Generic memory pool interface. * Allows consumers of aws-c-s3 to override how buffer allocation for part buffers is done. * Refer to docs/memory_aware_request_execution.md for details on how default implementation works. * WARNING: this is currently experimental feature and does not provide API stability guarantees so should be used with * caution. At highlevel the flow is as follows: * - crt scheduler queues up requests to be prepared * - requests being prepared will try to reserve mem (i.e. obtain a ticket) and wait until they get it before proceeding * - once mem is reserved requests will proceed with the pipeline * - request will acquire buffer from the ticket when needed * - ticket is released when request is done with the buffer * Note: in some cases pipeline can stall if new buffer cannot be allocated (ex. async writes flow). * In this case reserve request will indicate that not granting the ticket can block and buffer pool should try to * allocate ticket right away (or wait and call waker when mem is allocated for the case of async writes). * Note for custom pool implementations: Scheduler keeps track of all outstanding futures and will error them out when * request is paused or cancelled. Its still fine for memory pool implementation to deliver ticket (it will just be * released by future right away with no side effects) or just ignore the future if its already in error state. */ AWS_PUSH_SANE_WARNING_LEVEL AWS_EXTERN_C_BEGIN struct aws_s3_buffer_ticket; /** * aws_future * Buffer ticket future used for reservations. */ AWS_FUTURE_T_POINTER_WITH_RELEASE_DECLARATION(aws_future_s3_buffer_ticket, struct aws_s3_buffer_ticket, AWS_S3_API) /** * Meta information about ticket reservation request. */ struct aws_s3_buffer_pool_reserve_meta { /* client reserving the ticket. accounts for buffer pool being shared between clients. */ struct aws_s3_client *client; /* meta request ticket is being reserved for. */ struct aws_s3_meta_request *meta_request; /* size of the buffer to reserve. */ size_t size; /* Whether withholding the reservation can block this meta request's progress (currently only the * async-write flow). The pool can't hold such a reservation indefinitely (that can deadlock the * client); it must resolve it, either synchronously (the default pool hands out a "forced" buffer * that may exceed the memory limit) or by deferring. To defer, the pool returns a not-yet-done * future and resolves it later as memory frees up; the caller waits on the future and resumes * once it completes. A pool that defers must guarantee every deferred reservation is eventually * resolved. */ bool can_block; }; struct aws_s3_buffer_ticket; struct aws_s3_buffer_ticket_vtable { /** * Get buffer associated with the ticket. * Note: can be called multiple times and the same buffer should be returned. In some cases ticket might not be * claimed at all. */ struct aws_byte_buf (*claim)(struct aws_s3_buffer_ticket *ticket); /* Implement below for custom ref count behavior. Alternatively set those to null and init the ref count. */ struct aws_s3_buffer_ticket *(*acquire)(struct aws_s3_buffer_ticket *ticket); struct aws_s3_buffer_ticket *(*release)(struct aws_s3_buffer_ticket *ticket); }; /** * Polymorphic ticket. */ struct aws_s3_buffer_ticket { struct aws_s3_buffer_ticket_vtable *vtable; struct aws_ref_count ref_count; void *impl; }; AWS_S3_API struct aws_byte_buf aws_s3_buffer_ticket_claim(struct aws_s3_buffer_ticket *ticket); AWS_S3_API struct aws_s3_buffer_ticket *aws_s3_buffer_ticket_acquire(struct aws_s3_buffer_ticket *ticket); AWS_S3_API struct aws_s3_buffer_ticket *aws_s3_buffer_ticket_release(struct aws_s3_buffer_ticket *ticket); struct aws_s3_buffer_pool; struct aws_s3_buffer_pool_vtable { /* Reserve a ticket. Returns a future that is granted whenever reservation can be made. */ struct aws_future_s3_buffer_ticket *( *reserve)(struct aws_s3_buffer_pool *pool, struct aws_s3_buffer_pool_reserve_meta meta); /** * Trim the pool. This is mostly a suggestion, which pool can decide to ignore. Triggered by CRT when * client has been idle for some time. **/ void (*trim)(struct aws_s3_buffer_pool *pool); /** * Optimize the buffer pool for allocations of a specific size. * Creates a separate list of blocks dedicated to this size for better memory efficiency. */ int (*add_special_size)(struct aws_s3_buffer_pool *pool, size_t buffer_size); /** * Release all special-sized blocks from the buffer pool. * This frees all memory allocated for the special size optimization. */ void (*release_special_size)(struct aws_s3_buffer_pool *pool, size_t buffer_size); /** * Align a range size to the buffer pool's allocation strategy. * Returns the optimal aligned size based on the buffer pool's configuration. */ uint64_t (*derive_aligned_buffer_size)(struct aws_s3_buffer_pool *pool, uint64_t size); /* Implement below for custom ref count behavior. Alternatively set those to null and init the ref count. */ struct aws_s3_buffer_pool *(*acquire)(struct aws_s3_buffer_pool *pool); struct aws_s3_buffer_pool *(*release)(struct aws_s3_buffer_pool *pool); }; /** * Polymorphic buffer pool. */ struct aws_s3_buffer_pool { struct aws_s3_buffer_pool_vtable *vtable; struct aws_ref_count ref_count; void *impl; }; AWS_S3_API struct aws_future_s3_buffer_ticket *aws_s3_buffer_pool_reserve( struct aws_s3_buffer_pool *buffer_pool, struct aws_s3_buffer_pool_reserve_meta meta); AWS_S3_API void aws_s3_buffer_pool_trim(struct aws_s3_buffer_pool *buffer_pool); AWS_S3_API struct aws_s3_buffer_pool *aws_s3_buffer_pool_acquire(struct aws_s3_buffer_pool *buffer_pool); AWS_S3_API struct aws_s3_buffer_pool *aws_s3_buffer_pool_release(struct aws_s3_buffer_pool *buffer_pool); /** * Buffer pool configuration options. */ struct aws_s3_buffer_pool_config { struct aws_s3_client *client; /* Client creating the pool. */ size_t part_size; /* Default part size of the client. */ size_t max_part_size; /* Max part size configured on the client. */ size_t memory_limit; /* Memory limit set on the client. */ }; /** * Factory to construct the pool for the given config. Passes along buffer related info configured on the client, which * factory may ignore when considering how to construct pool. * This implementation should fail if pool cannot be constructed for some reason (ex. if config params cannot be met), * by logging failure reason, returning null and raising aws_error. */ typedef struct aws_s3_buffer_pool *(aws_s3_buffer_pool_factory_fn)(struct aws_allocator *allocator, struct aws_s3_buffer_pool_config config, void *user_data); /** * Optimize the buffer pool for allocations of a specific size. * Creates a separate list of blocks dedicated to this size for better memory efficiency. * Allocations of exactly this size will use these special blocks instead of the regular primary/secondary storage. * * @param buffer_pool The buffer pool to optimize * @param buffer_size The size to optimize for (must be > 0) * @return AWS_OP_SUCCESS on success, AWS_OP_ERR on failure */ AWS_S3_API int aws_s3_buffer_pool_add_special_size(struct aws_s3_buffer_pool *buffer_pool, size_t buffer_size); /** * Release the special-sized blocks from the buffer pool. * Should be called when done with the special-sized allocations. * * @param buffer_pool The buffer pool * @param buffer_size The special size to release blocks for */ AWS_S3_API void aws_s3_buffer_pool_release_special_size(struct aws_s3_buffer_pool *buffer_pool, size_t buffer_size); /** * Align a range size to the buffer pool's allocation strategy. * This function determines the optimal aligned size based on the buffer pool's configuration. * For sizes within the primary allocation range, it aligns to chunk boundaries. * For larger sizes that go to secondary storage, it returns the size as-is. * * @param buffer_pool The buffer pool to use for alignment (can be NULL, in which case size is returned unchanged) * @param size The size to align * @return The aligned size that's optimal for the buffer pool's allocation strategy */ AWS_S3_API uint64_t aws_s3_buffer_pool_derive_aligned_buffer_size(struct aws_s3_buffer_pool *buffer_pool, uint64_t size); AWS_EXTERN_C_END AWS_POP_SANE_WARNING_LEVEL #endif /* AWS_S3_BUFFER_POOL_H */