BufferAllocator.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package org.apache.arrow.memory;

import java.util.Collection;
import org.apache.arrow.memory.rounding.DefaultRoundingPolicy;
import org.apache.arrow.memory.rounding.RoundingPolicy;
import org.checkerframework.checker.nullness.qual.Nullable;

/** Wrapper class to deal with byte buffer allocation. Ensures users only use designated methods. */
public interface BufferAllocator extends AutoCloseable {

  /**
   * Allocate a new or reused buffer of the provided size. Note that the buffer may technically be
   * larger than the requested size for rounding purposes. However, the buffer's capacity will be
   * set to the configured size.
   *
   * @param size The size in bytes.
   * @return a new ArrowBuf, or null if the request can't be satisfied
   * @throws OutOfMemoryException if buffer cannot be allocated
   */
  ArrowBuf buffer(long size);

  /**
   * Allocate a new or reused buffer of the provided size. Note that the buffer may technically be
   * larger than the requested size for rounding purposes. However, the buffer's capacity will be
   * set to the configured size.
   *
   * @param size The size in bytes.
   * @param manager A buffer manager to manage reallocation.
   * @return a new ArrowBuf, or null if the request can't be satisfied
   * @throws OutOfMemoryException if buffer cannot be allocated
   */
  ArrowBuf buffer(long size, BufferManager manager);

  /**
   * Get the root allocator of this allocator. If this allocator is already a root, return this
   * directly.
   *
   * @return The root allocator
   */
  BufferAllocator getRoot();

  /**
   * Create a new child allocator.
   *
   * @param name the name of the allocator.
   * @param initReservation the initial space reservation (obtained from this allocator)
   * @param maxAllocation maximum amount of space the new allocator can allocate
   * @return the new allocator, or null if it can't be created
   */
  BufferAllocator newChildAllocator(String name, long initReservation, long maxAllocation);

  /**
   * Create a new child allocator.
   *
   * @param name the name of the allocator.
   * @param listener allocation listener for the newly created child
   * @param initReservation the initial space reservation (obtained from this allocator)
   * @param maxAllocation maximum amount of space the new allocator can allocate
   * @return the new allocator, or null if it can't be created
   */
  BufferAllocator newChildAllocator(
      String name, AllocationListener listener, long initReservation, long maxAllocation);

  /**
   * Close and release all buffers generated from this buffer pool.
   *
   * <p>When assertions are on, complains if there are any outstanding buffers; to avoid that,
   * release all buffers before the allocator is closed.
   */
  @Override
  void close();

  /**
   * Returns the amount of memory currently allocated from this allocator.
   *
   * @return the amount of memory currently allocated
   */
  long getAllocatedMemory();

  /**
   * Return the current maximum limit this allocator imposes.
   *
   * @return Limit in number of bytes.
   */
  long getLimit();

  /**
   * Return the initial reservation.
   *
   * @return reservation in bytes.
   */
  long getInitReservation();

  /**
   * Set the maximum amount of memory this allocator is allowed to allocate.
   *
   * @param newLimit The new Limit to apply to allocations
   */
  void setLimit(long newLimit);

  /**
   * Returns the peak amount of memory allocated from this allocator.
   *
   * @return the peak amount of memory allocated
   */
  long getPeakMemoryAllocation();

  /**
   * Returns the amount of memory that can probably be allocated at this moment without exceeding
   * this or any parents allocation maximum.
   *
   * @return Headroom in bytes
   */
  long getHeadroom();

  /**
   * Forcibly allocate bytes. Returns whether the allocation fit within limits.
   *
   * @param size to increase
   * @return Whether the allocation fit within limits.
   */
  boolean forceAllocate(long size);

  /**
   * Release bytes from this allocator.
   *
   * @param size to release
   */
  void releaseBytes(long size);

  /**
   * Returns the allocation listener used by this allocator.
   *
   * @return the {@link AllocationListener} instance. Or {@link AllocationListener#NOOP} by default
   *     if no listener is configured when this allocator was created.
   */
  AllocationListener getListener();

  /**
   * Returns the parent allocator.
   *
   * @return parent allocator
   */
  @Nullable
  BufferAllocator getParentAllocator();

  /**
   * Returns the set of child allocators.
   *
   * @return set of child allocators
   */
  Collection<BufferAllocator> getChildAllocators();

  /**
   * Create an allocation reservation. A reservation is a way of building up a request for a buffer
   * whose size is not known in advance. See
   *
   * @return the newly created reservation
   * @see AllocationReservation
   */
  AllocationReservation newReservation();

  /**
   * Get a reference to the empty buffer associated with this allocator. Empty buffers are special
   * because we don't worry about them leaking or managing reference counts on them since they don't
   * actually point to any memory.
   *
   * @return the empty buffer
   */
  ArrowBuf getEmpty();

  /**
   * Return the name of this allocator. This is a human readable name that can help debugging.
   * Typically provides coordinates about where this allocator was created
   *
   * @return the name of the allocator
   */
  String getName();

  /**
   * Return whether or not this allocator (or one if its parents) is over its limits. In the case
   * that an allocator is over its limit, all consumers of that allocator should aggressively try to
   * address the overlimit situation.
   *
   * @return whether or not this allocator (or one if its parents) is over its limits
   */
  boolean isOverLimit();

  /**
   * Return a verbose string describing this allocator. If in DEBUG mode, this will also include
   * relevant stacktraces and historical logs for underlying objects
   *
   * @return A very verbose description of the allocator hierarchy.
   */
  String toVerboseString();

  /**
   * Asserts (using java assertions) that the provided allocator is currently open. If assertions
   * are disabled, this is a no-op.
   */
  void assertOpen();

  /** Gets the rounding policy of the allocator. */
  default RoundingPolicy getRoundingPolicy() {
    return DefaultRoundingPolicy.DEFAULT_ROUNDING_POLICY;
  }

  /**
   * EXPERIMENTAL: Wrap an allocation created outside this BufferAllocator.
   *
   * <p>This is useful to integrate allocations from native code into the same memory management
   * framework as Java-allocated buffers, presenting users a consistent API. The created buffer will
   * be tracked by this allocator and can be transferred like Java-allocated buffers.
   *
   * <p>The underlying allocation will be closed when all references to the buffer are released. If
   * this method throws, the underlying allocation will also be closed.
   *
   * @param allocation The underlying allocation.
   */
  default ArrowBuf wrapForeignAllocation(ForeignAllocation allocation) {
    throw new UnsupportedOperationException();
  }
}