netty5/buffer/src/main/java/io/netty5/buffer/api/MemoryManager.java

175 lines
8.1 KiB
Java

/*
* Copyright 2021 The Netty Project
*
* The Netty Project 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:
*
* https://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 io.netty5.buffer.api;
import io.netty5.buffer.api.internal.MemoryManagerLoader;
import io.netty5.buffer.api.internal.MemoryManagerOverride;
import io.netty5.util.internal.UnstableApi;
import io.netty5.util.internal.logging.InternalLogger;
import io.netty5.util.internal.logging.InternalLoggerFactory;
import java.lang.ref.Cleaner;
import java.util.Optional;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader.Provider;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* The choice of {@code MemoryManager} implementation also determines the choice of {@link Buffer} implementation.
* It is the MemoryManager that implement memory allocation, and how to wrap the allocated memory in a {@link Buffer}
* interface.
*
* @apiNote This is a low-level, {@linkplain UnstableApi unstable}, API that is used for
* {@link BufferAllocator BufferAllocator} implementations to build upon.
* The methods in this interface are unsafe, because they can be used to violate the safety guarantees of the
* {@link Buffer} API, and potentially also the safety guarantees of the JVM.
*/
@UnstableApi
public interface MemoryManager {
/**
* Get the default, or currently configured, memory managers instance.
* @return A MemoryManagers instance.
*/
static MemoryManager instance() {
return MemoryManagerOverride.configuredOrDefaultManager();
}
/**
* Temporarily override the default configured memory managers instance.
* <p>
* Calls to {@link #instance()} from within the given supplier will get the given managers instance.
*
* @param managers Override the default configured managers instance with this instance.
* @param supplier The supplier function to be called while the override is in place.
* @param <T> The result type from the supplier.
* @return The result from the supplier.
*/
static <T> T using(MemoryManager managers, Supplier<T> supplier) {
return MemoryManagerOverride.using(managers, supplier);
}
/**
* Get a lazy-loading stream of all available memory managers.
*
* @return A stream of providers of memory managers instances.
*/
static Stream<Provider<MemoryManager>> availableManagers() {
return MemoryManagerLoader.stream();
}
/**
* Find a {@link MemoryManager} implementation by its {@linkplain #implementationName() implementation name}.
*
* @param implementationName The named implementation to look for.
* @return A {@link MemoryManager} implementation, if any was found.
*/
static Optional<MemoryManager> lookupImplementation(String implementationName) {
return availableManagers()
.flatMap(provider -> {
try {
return Stream.ofNullable(provider.get());
} catch (ServiceConfigurationError | Exception e) {
InternalLogger logger = InternalLoggerFactory.getInstance(MemoryManager.class);
if (logger.isTraceEnabled()) {
logger.debug("Failed to load a MemoryManager implementation.", e);
} else {
logger.debug("Failed to load a MemoryManager implementation: " + e.getMessage());
}
return Stream.empty();
}
})
.filter(impl -> implementationName.equals(impl.implementationName()))
.findFirst();
}
/**
* Allocates a shared buffer. "Shared" is the normal type of buffer, and means the buffer permit concurrent access
* from multiple threads, within the limited thread-safety guarantees of the {@link Buffer} interface.
*
* @param allocatorControl Call-back interface for controlling the {@linkplain BufferAllocator allocator} that
* requested the allocation of this buffer.
* @param size The size of the buffer to allocate. This size is assumed to be valid for the implementation.
* @param drop The {@link Drop} instance to use when the buffer is {@linkplain Resource#close() closed}.
* @param cleaner The {@link Cleaner} that the underlying memory should be attached to. Can be {@code null}.
* @param allocationType The type of allocation to perform.
* Typically, one of the {@linkplain StandardAllocationTypes}.
* @return A {@link Buffer} instance with the given configuration.
* @throws IllegalArgumentException For unknown {@link AllocationType}s.
*/
Buffer allocateShared(AllocatorControl allocatorControl, long size, Drop<Buffer> drop, Cleaner cleaner,
AllocationType allocationType);
/**
* Allocates a constant buffer based on the given parent. A "constant" buffer is conceptually similar to a read-only
* buffer, but the implementation may share the underlying memory across multiple buffer instance - something that
* is normally not allowed by the API. This allows efficient implementation of the
* {@link BufferAllocator#constBufferSupplier(byte[])} method.
* <p>
* <strong>Note:</strong> the const-parent buffer must be allocated by this memory manager.
*
* @param readOnlyConstParent The read-only parent buffer for which a const buffer should be created. The parent
* buffer is allocated in the usual way, with
* {@link #allocateShared(AllocatorControl, long, Drop, Cleaner, AllocationType)},
* initialised with contents, and then made {@linkplain Buffer#makeReadOnly() read-only}.
* @return A const buffer with the same size, contents, and read-only state of the given parent buffer.
*/
Buffer allocateConstChild(Buffer readOnlyConstParent);
/**
* The buffer implementation-specific {@link Drop} implementation that will release the underlying memory.
*
* @return A new drop instance.
*/
Drop<Buffer> drop();
/**
* Create an object that represents the internal memory of the given buffer.
*
* @param buf The buffer to unwrap.
* @return The internal memory of the given buffer, as an opaque object.
*/
Object unwrapRecoverableMemory(Buffer buf);
/**
* Recover the memory from a prior {@link #unwrapRecoverableMemory(Buffer)} call, and wrap it in a {@link Buffer}
* instance.
*
* @param allocatorControl The allocator control to attach to the buffer.
* @param recoverableMemory The opaque memory to use for the buffer.
* @param drop The {@link Drop} instance to use when the buffer is {@linkplain Resource#close() closed}.
* @return A {@link Buffer} instance backed by the given recovered memory.
*/
Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop);
/**
* Produces a slice of the given internal memory representation object.
*
* @param memory The opaque memory to slice.
* @param offset The offset into the memory to slice from.
* @param length The length of the slice.
* @return A new opaque memory instance that represents the given slice of the original.
*/
Object sliceMemory(Object memory, int offset, int length);
/**
* Get the name for this implementation, which can be used for finding this particular implementation via the
* {@link #lookupImplementation(String)} method.
*
* @return The name of this memory managers implementation.
*/
String implementationName();
}