View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.buffer.api;
17  
18  import io.netty5.buffer.api.internal.ArcDrop;
19  import io.netty5.buffer.api.internal.LeakDetection;
20  import io.netty5.buffer.api.internal.MemoryManagerLoader;
21  import io.netty5.buffer.api.internal.MemoryManagerOverride;
22  import io.netty5.buffer.api.internal.WrappingAllocation;
23  import io.netty5.util.Resource;
24  import io.netty5.util.SafeCloseable;
25  import io.netty5.util.internal.UnstableApi;
26  import io.netty5.util.internal.logging.InternalLogger;
27  import io.netty5.util.internal.logging.InternalLoggerFactory;
28  
29  import java.util.Optional;
30  import java.util.ServiceConfigurationError;
31  import java.util.ServiceLoader.Provider;
32  import java.util.function.Consumer;
33  import java.util.function.Function;
34  import java.util.function.Supplier;
35  import java.util.stream.Stream;
36  
37  /**
38   * The choice of {@code MemoryManager} implementation also determines the choice of {@link Buffer} implementation.
39   * It is the MemoryManager that implement memory allocation, and how to wrap the allocated memory in a {@link Buffer}
40   * interface.
41   *
42   * @apiNote This is a low-level, {@linkplain UnstableApi unstable}, API that is used for
43   * {@link BufferAllocator BufferAllocator} implementations to build upon.
44   * The methods in this interface are unsafe, because they can be used to violate the safety guarantees of the
45   * {@link Buffer} API, and potentially also the safety guarantees of the JVM.
46   */
47  @UnstableApi
48  public interface MemoryManager {
49      /**
50       * Get the default, or currently configured, memory managers instance.
51       * @return A MemoryManagers instance.
52       */
53      static MemoryManager instance() {
54          return MemoryManagerOverride.configuredOrDefaultManager();
55      }
56  
57      /**
58       * Register a callback that will be called whenever a {@link Buffer} instance is leaked.
59       * <p>
60       * Be mindful that the callback must be fast, and not take any locks or call any blocking methods,
61       * as this might interfere with the garbage collectors reference processing and cleaning.
62       * <p>
63       * This also applies to callbacks that perform logging.
64       * In these cases, asynchronous logging should be preferred, for the avoidance of blocking calls and IO.
65       * <p>
66       * If the same callback object is registered multiple times, it will only be informed once for each leak,
67       * but each of the associated {@link SafeCloseable} objects will need to be closed before the callback is removed.
68       *
69       * @param callback The callback that will be called when a buffer leak is detected.
70       * @return An {@link SafeCloseable} instance that, when closed, removes the given callback again.
71       */
72      @UnstableApi
73      static SafeCloseable onLeakDetected(Consumer<LeakInfo> callback) {
74          return LeakDetection.onLeakDetected(callback);
75      }
76  
77      /**
78       * Temporarily override the default configured memory managers instance.
79       * <p>
80       * Calls to {@link #instance()} from within the given supplier will get the given managers instance.
81       *
82       * @param manager Override the default configured managers instance with this instance.
83       * @param supplier The supplier function to be called while the override is in place.
84       * @param <T> The result type from the supplier.
85       * @return The result from the supplier.
86       */
87      static <T> T using(MemoryManager manager, Supplier<T> supplier) {
88          return MemoryManagerOverride.using(manager, supplier);
89      }
90  
91      /**
92       * Get a stream of all available memory managers.
93       *
94       * @return A stream of providers of memory managers instances.
95       */
96      static Stream<Provider<MemoryManager>> availableManagers() {
97          return MemoryManagerLoader.stream();
98      }
99  
100     /**
101      * Find a {@link MemoryManager} implementation by its {@linkplain #implementationName() implementation name}.
102      *
103      * @param implementationName The named implementation to look for.
104      * @return A {@link MemoryManager} implementation, if any was found.
105      */
106     static Optional<MemoryManager> lookupImplementation(String implementationName) {
107         return availableManagers()
108                 .flatMap(provider -> {
109                     try {
110                         return Stream.ofNullable(provider.get());
111                     } catch (ServiceConfigurationError | Exception e) {
112                         InternalLogger logger = InternalLoggerFactory.getInstance(MemoryManager.class);
113                         if (logger.isTraceEnabled()) {
114                             logger.debug("Failed to load a MemoryManager implementation.", e);
115                         } else {
116                             logger.debug("Failed to load a MemoryManager implementation: " + e.getMessage());
117                         }
118                         return Stream.empty();
119                     }
120                 })
121                 .filter(impl -> implementationName.equals(impl.implementationName()))
122                 .findFirst();
123     }
124 
125     /**
126      * Create a new on-heap {@link Buffer} instance that directly wraps the given array.
127      * <p>
128      * This is <em>unsafe</em> because it allows the memory (the array) to be aliased (multiple references to the same
129      * memory) in an uncontrolled way.
130      * <p>
131      * The returned buffer will be {@linkplain Buffer#readOnly() read-only}, but changes to the byte array will be
132      * reflected in the buffers contents.
133      * <p>
134      * <strong>Note:</strong> Wrapping buffers created with this method are not subject to leak detection, and if they
135      * are garbage collected without being {@linkplain Buffer#close() closed} first, then no callback will be issued
136      * to any {@linkplain #onLeakDetected(Consumer) on-leak callback handler}.
137      *
138      * @param array The byte array that will be embedded in the created buffer.
139      * @return A buffer that wraps the given byte array
140      */
141     @UnstableApi
142     static Buffer unsafeWrap(byte[] array) {
143         MemoryManager manager = instance();
144         ManagedBufferAllocator allocator = new ManagedBufferAllocator(manager, false);
145         WrappingAllocation allocationType = new WrappingAllocation(array);
146         Buffer buffer = manager.allocateShared(allocator, array.length, ArcDrop::wrap, allocationType);
147         buffer.skipWritableBytes(array.length);
148         return buffer.makeReadOnly();
149     }
150 
151     /**
152      * Allocates a shared buffer. "Shared" is the normal type of buffer, and means the buffer permit concurrent access
153      * from multiple threads, within the limited thread-safety guarantees of the {@link Buffer} interface.
154      *
155      * @param allocatorControl Call-back interface for controlling the {@linkplain BufferAllocator allocator} that
156      *                        requested the allocation of this buffer.
157      * @param size The size of the buffer to allocate. This size is assumed to be valid for the implementation.
158      * @param dropDecorator A function to decorate the memory managers {@link Drop} instance.
159      *                     The {@link Drop} instance returned by this function will be used when the buffer is
160      *                     {@linkplain Resource#close() closed}.
161      * @param allocationType The type of allocation to perform.
162      *                      Typically, one of the {@linkplain StandardAllocationTypes}.
163      * @return A {@link Buffer} instance with the given configuration.
164      * @throws IllegalArgumentException For unknown {@link AllocationType}s.
165      */
166     Buffer allocateShared(AllocatorControl allocatorControl, long size,
167                           Function<Drop<Buffer>, Drop<Buffer>> dropDecorator,
168                           AllocationType allocationType);
169 
170     /**
171      * Allocates a constant buffer based on the given parent. A "constant" buffer is conceptually similar to a read-only
172      * buffer, but the implementation may share the underlying memory across multiple buffer instance - something that
173      * is normally not allowed by the API. This allows efficient implementation of the
174      * {@link BufferAllocator#constBufferSupplier(byte[])} method.
175      * <p>
176      * <strong>Note:</strong> The const-parent buffer must be allocated by this memory manager.
177      *
178      * @param readOnlyConstParent The read-only parent buffer for which a const buffer should be created. The parent
179      *                            buffer is allocated in the usual way, with
180      *                            {@link #allocateShared(AllocatorControl, long, Function, AllocationType)},
181      *                            initialised with contents, and then made {@linkplain Buffer#makeReadOnly() read-only}.
182      * @return A const buffer with the same size, contents, and read-only state of the given parent buffer.
183      */
184     Buffer allocateConstChild(Buffer readOnlyConstParent);
185 
186     /**
187      * Create an object that represents the internal memory of the given buffer.
188      *
189      * @param buf The buffer to unwrap.
190      * @return The internal memory of the given buffer, as an opaque object.
191      */
192     Object unwrapRecoverableMemory(Buffer buf);
193 
194     /**
195      * Recover the memory from a prior {@link #unwrapRecoverableMemory(Buffer)} call, and wrap it in a {@link Buffer}
196      * instance.
197      *
198      * @param allocatorControl The allocator control to attach to the buffer.
199      * @param recoverableMemory The opaque memory to use for the buffer.
200      * @param drop The {@link Drop} instance to use when the buffer is {@linkplain Resource#close() closed}.
201      * @return A {@link Buffer} instance backed by the given recovered memory.
202      */
203     Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop);
204 
205     /**
206      * Produces a slice of the given internal memory representation object.
207      *
208      * @param memory The opaque memory to slice.
209      * @param offset The offset into the memory to slice from.
210      * @param length The length of the slice.
211      * @return A new opaque memory instance that represents the given slice of the original.
212      */
213     Object sliceMemory(Object memory, int offset, int length);
214 
215     /**
216      * Overwrite the given recoverable memory object with zeroes, erasing all data that it contains.
217      * <p>
218      * This is used by the {@link SensitiveBufferAllocator} to erase data on deallocation.
219      *
220      * @param memory The memory that should be overwritten.
221      */
222     void clearMemory(Object memory);
223 
224     /**
225      * Get the name for this implementation, which can be used for finding this particular implementation via the
226      * {@link #lookupImplementation(String)} method.
227      *
228      * @return The name of this memory managers implementation.
229      */
230     String implementationName();
231 }