< prev index next >

src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/SegmentAllocator.java

Print this page

  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.foreign;
 27 
 28 import jdk.internal.foreign.ArenaAllocator;
 29 import jdk.internal.foreign.AbstractMemorySegmentImpl;

 30 import jdk.internal.foreign.ResourceScopeImpl;
 31 import jdk.internal.foreign.Utils;
 32 
 33 import java.lang.invoke.VarHandle;
 34 import java.lang.reflect.Array;
 35 import java.nio.ByteOrder;

 36 import java.util.Objects;
 37 import java.util.function.Function;
 38 import java.util.stream.Stream;
 39 
 40 /**
 41  * This interface models a memory allocator. Clients implementing this interface
 42  * must implement the {@link #allocate(long, long)} method. This interface defines several default methods
 43  * which can be useful to create segments from several kinds of Java values such as primitives and arrays.
 44  * This interface can be seen as a thin wrapper around the basic capabilities for creating native segments
 45  * (e.g. {@link MemorySegment#allocateNative(long, long, ResourceScope)}); since {@link SegmentAllocator} is a <em>functional interface</em>,

 46  * clients can easily obtain a native allocator by using either a lambda expression or a method reference.
 47  * <p>
 48  * This interface provides a factory, namely {@link SegmentAllocator#ofScope(ResourceScope)} which can be used to obtain
 49  * a <em>scoped</em> allocator, that is, an allocator which creates segment bound by a given scope. This can be useful
 50  * when working inside a <em>try-with-resources</em> construct:
 51  *
 52  * <blockquote><pre>{@code
 53 try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 54    SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
 55    ...
 56 }
 57  * }</pre></blockquote>
 58  *
 59  * In addition, this interface also defines factories for commonly used allocators; for instance {@link #arenaAllocator(ResourceScope)}
 60  * and {@link #arenaAllocator(long, ResourceScope)} are arena-style native allocators. Finally {@link #ofSegment(MemorySegment)}
 61  * returns an allocator which wraps a segment (either on-heap or off-heap) and recycles its content upon each new allocation request.

 62  */
 63 @FunctionalInterface
 64 public interface SegmentAllocator {
 65 
 66     /**
 67      * Allocate a block of memory with given layout and initialize it with given byte value.

















 68      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
 69      * @param layout the layout of the block of memory to be allocated.
 70      * @param value the value to be set on the newly allocated memory block.
 71      * @return a segment for the newly allocated memory block.
 72      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a byte value.
 73      */
 74     default MemorySegment allocate(ValueLayout layout, byte value) {
 75         Objects.requireNonNull(layout);
 76         VarHandle handle = layout.varHandle(byte.class);
 77         MemorySegment addr = allocate(layout);
 78         handle.set(addr, value);
 79         return addr;
 80     }
 81 
 82     /**
 83      * Allocate a block of memory with given layout and initialize it with given char value.
 84      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
 85      * @param layout the layout of the block of memory to be allocated.
 86      * @param value the value to be set on the newly allocated memory block.
 87      * @return a segment for the newly allocated memory block.
 88      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a char value.
 89      */
 90     default MemorySegment allocate(ValueLayout layout, char value) {
 91         Objects.requireNonNull(layout);
 92         VarHandle handle = layout.varHandle(char.class);
 93         MemorySegment addr = allocate(layout);
 94         handle.set(addr, value);
 95         return addr;
 96     }
 97 
 98     /**
 99      * Allocate a block of memory with given layout and initialize it with given short value.
100      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
101      * @param layout the layout of the block of memory to be allocated.
102      * @param value the value to be set on the newly allocated memory block.
103      * @return a segment for the newly allocated memory block.
104      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a short value.
105      */
106     default MemorySegment allocate(ValueLayout layout, short value) {
107         Objects.requireNonNull(layout);
108         VarHandle handle = layout.varHandle(short.class);
109         MemorySegment addr = allocate(layout);
110         handle.set(addr, value);
111         return addr;
112     }
113 
114     /**
115      * Allocate a block of memory with given layout and initialize it with given int value.
116      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
117      * @param layout the layout of the block of memory to be allocated.
118      * @param value the value to be set on the newly allocated memory block.
119      * @return a segment for the newly allocated memory block.
120      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a int value.
121      */
122     default MemorySegment allocate(ValueLayout layout, int value) {
123         Objects.requireNonNull(layout);
124         VarHandle handle = layout.varHandle(int.class);
125         MemorySegment addr = allocate(layout);
126         handle.set(addr, value);
127         return addr;
128     }
129 
130     /**
131      * Allocate a block of memory with given layout and initialize it with given float value.
132      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
133      * @param layout the layout of the block of memory to be allocated.
134      * @param value the value to be set on the newly allocated memory block.
135      * @return a segment for the newly allocated memory block.
136      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a float value.
137      */
138     default MemorySegment allocate(ValueLayout layout, float value) {
139         Objects.requireNonNull(layout);
140         VarHandle handle = layout.varHandle(float.class);
141         MemorySegment addr = allocate(layout);
142         handle.set(addr, value);
143         return addr;
144     }
145 
146     /**
147      * Allocate a block of memory with given layout and initialize it with given long value.
148      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
149      * @param layout the layout of the block of memory to be allocated.
150      * @param value the value to be set on the newly allocated memory block.
151      * @return a segment for the newly allocated memory block.
152      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a long value.
153      */
154     default MemorySegment allocate(ValueLayout layout, long value) {
155         Objects.requireNonNull(layout);
156         VarHandle handle = layout.varHandle(long.class);
157         MemorySegment addr = allocate(layout);
158         handle.set(addr, value);
159         return addr;
160     }
161 
162     /**
163      * Allocate a block of memory with given layout and initialize it with given double value.
164      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
165      * @param layout the layout of the block of memory to be allocated.
166      * @param value the value to be set on the newly allocated memory block.
167      * @return a segment for the newly allocated memory block.
168      * @throws IllegalArgumentException if {@code layout.byteSize()} does not conform to the size of a double value.
169      */
170     default MemorySegment allocate(ValueLayout layout, double value) {
171         Objects.requireNonNull(layout);
172         VarHandle handle = layout.varHandle(double.class);
173         MemorySegment addr = allocate(layout);
174         handle.set(addr, value);
175         return addr;
176     }
177 
178     /**
179      * Allocate a block of memory with given layout and initialize it with given address value
180      * (expressed as an {@link Addressable} instance).
181      * The address value might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}).
182      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
183      * @param layout the layout of the block of memory to be allocated.
184      * @param value the value to be set on the newly allocated memory block.
185      * @return a segment for the newly allocated memory block.
186      * @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}.
187      */
188     default MemorySegment allocate(ValueLayout layout, Addressable value) {
189         Objects.requireNonNull(value);
190         Objects.requireNonNull(layout);
191         if (MemoryLayouts.ADDRESS.byteSize() != layout.byteSize()) {
192             throw new IllegalArgumentException("Layout size mismatch - " + layout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize());
193         }
194         return switch ((int)layout.byteSize()) {
195             case 4 -> allocate(layout, (int)value.address().toRawLongValue());
196             case 8 -> allocate(layout, value.address().toRawLongValue());
197             default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here
198         };
199     }
200 
201     /**
202      * Allocate a block of memory with given layout and initialize it with given byte array.
203      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
204      * @param elementLayout the element layout of the array to be allocated.
205      * @param array the array to be copied on the newly allocated memory block.
206      * @return a segment for the newly allocated memory block.
207      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a byte value.
208      */
209     default MemorySegment allocateArray(ValueLayout elementLayout, byte[] array) {
210         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
211     }
212 
213     /**
214      * Allocate a block of memory with given layout and initialize it with given short array.
215      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
216      * @param elementLayout the element layout of the array to be allocated.
217      * @param array the array to be copied on the newly allocated memory block.
218      * @return a segment for the newly allocated memory block.
219      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a short value.
220      */
221     default MemorySegment allocateArray(ValueLayout elementLayout, short[] array) {
222         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
223     }
224 
225     /**
226      * Allocate a block of memory with given layout and initialize it with given char array.
227      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
228      * @param elementLayout the element layout of the array to be allocated.
229      * @param array the array to be copied on the newly allocated memory block.
230      * @return a segment for the newly allocated memory block.
231      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a char value.
232      */
233     default MemorySegment allocateArray(ValueLayout elementLayout, char[] array) {
234         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
235     }
236 
237     /**
238      * Allocate a block of memory with given layout and initialize it with given int array.
239      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
240      * @param elementLayout the element layout of the array to be allocated.
241      * @param array the array to be copied on the newly allocated memory block.
242      * @return a segment for the newly allocated memory block.
243      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a int value.
244      */
245     default MemorySegment allocateArray(ValueLayout elementLayout, int[] array) {
246         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
247     }
248 
249     /**
250      * Allocate a block of memory with given layout and initialize it with given float array.
251      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
252      * @param elementLayout the element layout of the array to be allocated.
253      * @param array the array to be copied on the newly allocated memory block.
254      * @return a segment for the newly allocated memory block.
255      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a float value.
256      */
257     default MemorySegment allocateArray(ValueLayout elementLayout, float[] array) {
258         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
259     }
260 
261     /**
262      * Allocate a block of memory with given layout and initialize it with given long array.
263      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
264      * @param elementLayout the element layout of the array to be allocated.
265      * @param array the array to be copied on the newly allocated memory block.
266      * @return a segment for the newly allocated memory block.
267      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a long value.
268      */
269     default MemorySegment allocateArray(ValueLayout elementLayout, long[] array) {
270         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
271     }
272 
273     /**
274      * Allocate a block of memory with given layout and initialize it with given double array.
275      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
276      * @param elementLayout the element layout of the array to be allocated.
277      * @param array the array to be copied on the newly allocated memory block.
278      * @return a segment for the newly allocated memory block.
279      * @throws IllegalArgumentException if {@code elementLayout.byteSize()} does not conform to the size of a double value.
280      */
281     default MemorySegment allocateArray(ValueLayout elementLayout, double[] array) {
282         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
283     }
284 
285     /**
286      * Allocate a block of memory with given layout and initialize it with given address array.
287      * The address value of each array element might be narrowed according to the platform address size (see {@link MemoryLayouts#ADDRESS}).
288      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
289      * @param elementLayout the element layout of the array to be allocated.
290      * @param array the array to be copied on the newly allocated memory block.
291      * @return a segment for the newly allocated memory block.
292      * @throws IllegalArgumentException if {@code layout.byteSize() != MemoryLayouts.ADDRESS.byteSize()}.
293      */
294     default MemorySegment allocateArray(ValueLayout elementLayout, Addressable[] array) {
295         Objects.requireNonNull(elementLayout);
296         Objects.requireNonNull(array);
297         Stream.of(array).forEach(Objects::requireNonNull);
298         if (MemoryLayouts.ADDRESS.byteSize() != elementLayout.byteSize()) {
299             throw new IllegalArgumentException("Layout size mismatch - " + elementLayout.byteSize() + " != " + MemoryLayouts.ADDRESS.byteSize());
300         }
301         return switch ((int)elementLayout.byteSize()) {
302             case 4 -> copyArrayWithSwapIfNeeded(Stream.of(array)
303                             .mapToInt(a -> (int)a.address().toRawLongValue()).toArray(),
304                     elementLayout, MemorySegment::ofArray);
305             case 8 -> copyArrayWithSwapIfNeeded(Stream.of(array)
306                             .mapToLong(a -> a.address().toRawLongValue()).toArray(),
307                     elementLayout, MemorySegment::ofArray);
308             default -> throw new UnsupportedOperationException("Unsupported pointer size"); // should not get here
309         };
310     }
311 
312     private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
313                                                         Function<Z, MemorySegment> heapSegmentFactory) {
314         Objects.requireNonNull(array);
315         Objects.requireNonNull(elementLayout);
316         Utils.checkPrimitiveCarrierCompat(array.getClass().componentType(), elementLayout);
317         MemorySegment addr = allocate(MemoryLayout.sequenceLayout(Array.getLength(array), elementLayout));
318         if (elementLayout.byteSize() == 1 || (elementLayout.order() == ByteOrder.nativeOrder())) {
319             addr.copyFrom(heapSegmentFactory.apply(array));
320         } else {
321             ((AbstractMemorySegmentImpl)addr).copyFromSwap(heapSegmentFactory.apply(array), elementLayout.byteSize());
322         }
323         return addr;
324     }
325 
326     /**
327      * Allocate a block of memory  with given layout.
328      * @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}.
329      * @param layout the layout of the block of memory to be allocated.
330      * @return a segment for the newly allocated memory block.
331      */
332     default MemorySegment allocate(MemoryLayout layout) {
333         Objects.requireNonNull(layout);
334         return allocate(layout.byteSize(), layout.byteAlignment());
335     }
336 
337     /**
338      * Allocate a block of memory corresponding to an array with given element layout and size.
339      * @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}.
340      * @param elementLayout the array element layout.
341      * @param count the array element count.
342      * @return a segment for the newly allocated memory block.
343      */
344     default MemorySegment allocateArray(MemoryLayout elementLayout, long count) {
345         Objects.requireNonNull(elementLayout);
346         return allocate(MemoryLayout.sequenceLayout(count, elementLayout));
347     }
348 
349     /**
350      * Allocate a block of memory with given size, with default alignment (1-byte aligned).

351      * @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}.
352      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
353      * @return a segment for the newly allocated memory block.
354      */
355     default MemorySegment allocate(long bytesSize) {
356         return allocate(bytesSize, 1);
357     }
358 
359     /**
360      * Allocate a block of memory  with given size and alignment constraint.
361      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
362      * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
363      * @return a segment for the newly allocated memory block.
364      */
365     MemorySegment allocate(long bytesSize, long bytesAlignment);
366 
367     /**
368      * Returns a native arena-based allocator which allocates a single memory segment, of given size (using malloc),
369      * and then responds to allocation request by returning different slices of that same segment
370      * (until no further allocation is possible).
371      * This can be useful when clients want to perform multiple allocation requests while avoiding the cost associated
372      * with allocating a new off-heap memory region upon each allocation request.
373      * <p>
374      * An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be
375      * performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope,
376      * allocation requests can only occur from the thread owning the allocator's resource scope.
377      * <p>
378      * The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds
379      * the allocator capacity.
380      *
381      * @param size the size (in bytes) of the allocation arena.
382      * @param scope the scope associated with the segments returned by this allocator.
383      * @return a new bounded arena-based allocator
384      * @throws IllegalArgumentException if {@code size <= 0}.
385      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
386      * than the thread owning {@code scope}.
387      */
388     static SegmentAllocator arenaAllocator(long size, ResourceScope scope) {
389         Objects.requireNonNull(scope);
390         return scope.ownerThread() == null ?
391                 new ArenaAllocator.BoundedSharedArenaAllocator(scope, size) :
392                 new ArenaAllocator.BoundedArenaAllocator(scope, size);















393     }
394 
395     /**
396      * Returns a native unbounded arena-based allocator.
397      * <p>
398      * The returned allocator allocates a memory segment {@code S} of a certain fixed size (using malloc) and then
399      * responds to allocation requests in one of the following ways:
400      * <ul>
401      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a <em>free</em>
402      *     slice {@code S'} which fits that allocation request, return that {@code S'}.
403      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no <em>free</em>
404      *     slices which fits that allocation request, allocate a new segment {@code S'} (using malloc), which has same size as {@code S}
405      *     and set {@code S = S'}; the allocator then tries to respond to the same allocation request again.
406      *     <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'}
407      *     (using malloc), which has a sufficient size to satisfy the allocation request, and return {@code S'}.
408      * </ul>
409      * <p>
410      * This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
411      * cost associated with allocating a new off-heap memory region upon each allocation request.
412      * <p>
413      * An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be
414      * performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope,
415      * allocation requests can only occur from the thread owning the allocator's resource scope.
416      * <p>
417      * The returned allocator might throw an {@link OutOfMemoryError} if an incoming allocation request exceeds
418      * the system capacity.

419      *
420      * @param scope the scope associated with the segments returned by this allocator.


421      * @return a new unbounded arena-based allocator

422      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
423      * than the thread owning {@code scope}.
424      */
425     static SegmentAllocator arenaAllocator(ResourceScope scope) {
426         Objects.requireNonNull(scope);
427         return scope.ownerThread() == null ?
428                 new ArenaAllocator.UnboundedSharedArenaAllocator(scope) :
429                 new ArenaAllocator.UnboundedArenaAllocator(scope);




430     }
431 
432     /**
433      * Returns a segment allocator which responds to allocation requests by recycling a single segment; that is,
434      * each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
435      * constraints are ignored by this allocator). This can be useful to limit allocation requests in case a client







436      * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request
437      * takes place.
438      * <p>
439      * While the allocator returned by this method is <em>thread-safe</em>, concurrent access on the same recycling
440      * allocator might cause a thread to overwrite contents written to the underlying segment by a different thread.
441      *
442      * @param segment the memory segment to be recycled by the returned allocator.
443      * @return an allocator which recycles an existing segment upon each new allocation request.
444      */
445     static SegmentAllocator ofSegment(MemorySegment segment) {
446         Objects.requireNonNull(segment);
447         return (size, align) -> segment.asSlice(0, size);
448     }
449 
450     /**
451      * Returns a native allocator which responds to allocation requests by allocating new segments
452      * bound by the given resource scope, using the {@link MemorySegment#allocateNative(long, long, ResourceScope)}
453      * factory. This code is equivalent (but likely more efficient) to the following:
454      * <blockquote><pre>{@code
455     Resource scope = ...
456     SegmentAllocator scoped = (size, align) -> MemorySegment.allocateNative(size, align, scope);
457      * }</pre></blockquote>
458      *
459      * @param scope the resource scope associated with the segments created by the returned allocator.
460      * @return an allocator which allocates new memory segment bound by the provided resource scope.
461      */
462     static SegmentAllocator ofScope(ResourceScope scope) {
463         Objects.requireNonNull(scope);
464         return (ResourceScopeImpl)scope;
465     }


















466 }

  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.foreign;
 27 

 28 import jdk.internal.foreign.AbstractMemorySegmentImpl;
 29 import jdk.internal.foreign.ArenaAllocator;
 30 import jdk.internal.foreign.ResourceScopeImpl;
 31 import jdk.internal.foreign.Utils;
 32 
 33 import java.lang.invoke.VarHandle;
 34 import java.lang.reflect.Array;
 35 import java.nio.ByteOrder;
 36 import java.nio.charset.StandardCharsets;
 37 import java.util.Objects;
 38 import java.util.function.Function;

 39 
 40 /**
 41  * This interface models a memory allocator. Clients implementing this interface
 42  * must implement the {@link #allocate(long, long)} method. This interface defines several default methods
 43  * which can be useful to create segments from several kinds of Java values such as primitives and arrays.
 44  * This interface can be seen as a thin wrapper around the basic capabilities for
 45  * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) creating} native segments;
 46  * since {@link SegmentAllocator} is a <em>functional interface</em>,
 47  * clients can easily obtain a native allocator by using either a lambda expression or a method reference.
 48  * <p>
 49  * This interface also defines factories for commonly used allocators:
 50  * <ul>
 51  *     <li>{@link #nativeAllocator(ResourceScope)} creates an allocator which
 52  *     {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) allocates} native segments, backed by a given scope;</li>
 53  *     <li>{@link #newNativeArena(ResourceScope)} creates a more efficient arena-style native allocator, where memory
 54  *     is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;</li>
 55  *     <li>{@link #prefixAllocator(MemorySegment)} creates an allocator which wraps a segment (either on-heap or off-heap)
 56  *     and recycles its content upon each new allocation request.</li>
 57  * </ul>
 58  * <p>
 59  * Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate <em>where</em>
 60  * the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
 61  * {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
 62  * {@link SegmentAllocator} parameter if the underlying native function is known to return a struct by-value. Effectively,
 63  * the allocator parameter tells the linker runtime where to store the return value of the native function.
 64  */
 65 @FunctionalInterface
 66 public interface SegmentAllocator {
 67 
 68     /**
 69      * Converts a Java string into a UTF-8 encoded, null-terminated C string,
 70      * storing the result into a memory segment.
 71      * <p>
 72      * This method always replaces malformed-input and unmappable-character
 73      * sequences with this charset's default replacement byte array.  The
 74      * {@link java.nio.charset.CharsetEncoder} class should be used when more
 75      * control over the encoding process is required.
 76      *
 77      * @param str the Java string to be converted into a C string.
 78      * @return a new native memory segment containing the converted C string.
 79      */
 80     default MemorySegment allocateUtf8String(String str) {
 81         Objects.requireNonNull(str);
 82         return Utils.toCString(str.getBytes(StandardCharsets.UTF_8), this);
 83     }
 84 
 85     /**
 86      * Allocate a memory segment with given layout and initialize it with given byte value.
 87      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
 88      * @param layout the layout of the block of memory to be allocated.
 89      * @param value the value to be set on the newly allocated memory block.
 90      * @return a segment for the newly allocated memory block.

 91      */
 92     default MemorySegment allocate(ValueLayout.OfByte layout, byte value) {
 93         Objects.requireNonNull(layout);
 94         VarHandle handle = layout.varHandle();
 95         MemorySegment addr = allocate(layout);
 96         handle.set(addr, value);
 97         return addr;
 98     }
 99 
100     /**
101      * Allocate a memory segment with given layout and initialize it with given char value.
102      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
103      * @param layout the layout of the block of memory to be allocated.
104      * @param value the value to be set on the newly allocated memory block.
105      * @return a segment for the newly allocated memory block.

106      */
107     default MemorySegment allocate(ValueLayout.OfChar layout, char value) {
108         Objects.requireNonNull(layout);
109         VarHandle handle = layout.varHandle();
110         MemorySegment addr = allocate(layout);
111         handle.set(addr, value);
112         return addr;
113     }
114 
115     /**
116      * Allocate a memory segment with given layout and initialize it with given short value.
117      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
118      * @param layout the layout of the block of memory to be allocated.
119      * @param value the value to be set on the newly allocated memory block.
120      * @return a segment for the newly allocated memory block.

121      */
122     default MemorySegment allocate(ValueLayout.OfShort layout, short value) {
123         Objects.requireNonNull(layout);
124         VarHandle handle = layout.varHandle();
125         MemorySegment addr = allocate(layout);
126         handle.set(addr, value);
127         return addr;
128     }
129 
130     /**
131      * Allocate a memory segment with given layout and initialize it with given int value.
132      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
133      * @param layout the layout of the block of memory to be allocated.
134      * @param value the value to be set on the newly allocated memory block.
135      * @return a segment for the newly allocated memory block.

136      */
137     default MemorySegment allocate(ValueLayout.OfInt layout, int value) {
138         Objects.requireNonNull(layout);
139         VarHandle handle = layout.varHandle();
140         MemorySegment addr = allocate(layout);
141         handle.set(addr, value);
142         return addr;
143     }
144 
145     /**
146      * Allocate a memory segment with given layout and initialize it with given float value.
147      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
148      * @param layout the layout of the block of memory to be allocated.
149      * @param value the value to be set on the newly allocated memory block.
150      * @return a segment for the newly allocated memory block.

151      */
152     default MemorySegment allocate(ValueLayout.OfFloat layout, float value) {
153         Objects.requireNonNull(layout);
154         VarHandle handle = layout.varHandle();
155         MemorySegment addr = allocate(layout);
156         handle.set(addr, value);
157         return addr;
158     }
159 
160     /**
161      * Allocate a memory segment with given layout and initialize it with given long value.
162      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
163      * @param layout the layout of the block of memory to be allocated.
164      * @param value the value to be set on the newly allocated memory block.
165      * @return a segment for the newly allocated memory block.

166      */
167     default MemorySegment allocate(ValueLayout.OfLong layout, long value) {
168         Objects.requireNonNull(layout);
169         VarHandle handle = layout.varHandle();
170         MemorySegment addr = allocate(layout);
171         handle.set(addr, value);
172         return addr;
173     }
174 
175     /**
176      * Allocate a memory segment with given layout and initialize it with given double value.
177      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
178      * @param layout the layout of the block of memory to be allocated.
179      * @param value the value to be set on the newly allocated memory block.
180      * @return a segment for the newly allocated memory block.

181      */
182     default MemorySegment allocate(ValueLayout.OfDouble layout, double value) {
183         Objects.requireNonNull(layout);
184         VarHandle handle = layout.varHandle();
185         MemorySegment addr = allocate(layout);
186         handle.set(addr, value);
187         return addr;
188     }
189 
190     /**
191      * Allocate a memory segment with given layout and initialize it with given address value
192      * (expressed as an {@link Addressable} instance).
193      * The address value might be narrowed according to the platform address size (see {@link ValueLayout#ADDRESS}).
194      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
195      * @param layout the layout of the block of memory to be allocated.
196      * @param value the value to be set on the newly allocated memory block.
197      * @return a segment for the newly allocated memory block.

198      */
199     default MemorySegment allocate(ValueLayout.OfAddress layout, Addressable value) {
200         Objects.requireNonNull(value);
201         Objects.requireNonNull(layout);
202         MemorySegment segment = allocate(layout);
203         layout.varHandle().set(segment, value.address());
204         return segment;





205     }
206 
207     /**
208      * Allocate a memory segment with given layout and initialize it with given byte array.
209      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
210      * @param elementLayout the element layout of the array to be allocated.
211      * @param array the array to be copied on the newly allocated memory block.
212      * @return a segment for the newly allocated memory block.

213      */
214     default MemorySegment allocateArray(ValueLayout.OfByte elementLayout, byte[] array) {
215         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
216     }
217 
218     /**
219      * Allocate a memory segment with given layout and initialize it with given short array.
220      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
221      * @param elementLayout the element layout of the array to be allocated.
222      * @param array the array to be copied on the newly allocated memory block.
223      * @return a segment for the newly allocated memory block.

224      */
225     default MemorySegment allocateArray(ValueLayout.OfShort elementLayout, short[] array) {
226         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
227     }
228 
229     /**
230      * Allocate a memory segment with given layout and initialize it with given char array.
231      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
232      * @param elementLayout the element layout of the array to be allocated.
233      * @param array the array to be copied on the newly allocated memory block.
234      * @return a segment for the newly allocated memory block.

235      */
236     default MemorySegment allocateArray(ValueLayout.OfChar elementLayout, char[] array) {
237         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
238     }
239 
240     /**
241      * Allocate a memory segment with given layout and initialize it with given int array.
242      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
243      * @param elementLayout the element layout of the array to be allocated.
244      * @param array the array to be copied on the newly allocated memory block.
245      * @return a segment for the newly allocated memory block.

246      */
247     default MemorySegment allocateArray(ValueLayout.OfInt elementLayout, int[] array) {
248         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
249     }
250 
251     /**
252      * Allocate a memory segment with given layout and initialize it with given float array.
253      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
254      * @param elementLayout the element layout of the array to be allocated.
255      * @param array the array to be copied on the newly allocated memory block.
256      * @return a segment for the newly allocated memory block.

257      */
258     default MemorySegment allocateArray(ValueLayout.OfFloat elementLayout, float[] array) {
259         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
260     }
261 
262     /**
263      * Allocate a memory segment with given layout and initialize it with given long array.
264      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
265      * @param elementLayout the element layout of the array to be allocated.
266      * @param array the array to be copied on the newly allocated memory block.
267      * @return a segment for the newly allocated memory block.

268      */
269     default MemorySegment allocateArray(ValueLayout.OfLong elementLayout, long[] array) {
270         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
271     }
272 
273     /**
274      * Allocate a memory segment with given layout and initialize it with given double array.
275      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
276      * @param elementLayout the element layout of the array to be allocated.
277      * @param array the array to be copied on the newly allocated memory block.
278      * @return a segment for the newly allocated memory block.

279      */
280     default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double[] array) {
281         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
282     }
283 



























284     private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
285                                                         Function<Z, MemorySegment> heapSegmentFactory) {
286         Objects.requireNonNull(array);
287         Objects.requireNonNull(elementLayout);
288         int size = Array.getLength(array);
289         MemorySegment addr = allocate(MemoryLayout.sequenceLayout(size, elementLayout));
290         MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
291                 addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);



292         return addr;
293     }
294 
295     /**
296      * Allocate a memory segment with given layout.
297      * @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}.
298      * @param layout the layout of the block of memory to be allocated.
299      * @return a segment for the newly allocated memory block.
300      */
301     default MemorySegment allocate(MemoryLayout layout) {
302         Objects.requireNonNull(layout);
303         return allocate(layout.byteSize(), layout.byteAlignment());
304     }
305 
306     /**
307      * Allocate a memory segment with given element layout and size.
308      * @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}.
309      * @param elementLayout the array element layout.
310      * @param count the array element count.
311      * @return a segment for the newly allocated memory block.
312      */
313     default MemorySegment allocateArray(MemoryLayout elementLayout, long count) {
314         Objects.requireNonNull(elementLayout);
315         return allocate(MemoryLayout.sequenceLayout(count, elementLayout));
316     }
317 
318     /**
319      * Allocate a memory segment with given size
320      * and default alignment constraints (1-byte aligned).
321      * @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}.
322      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
323      * @return a segment for the newly allocated memory block.
324      */
325     default MemorySegment allocate(long bytesSize) {
326         return allocate(bytesSize, 1);
327     }
328 
329     /**
330      * Allocate a memory segment with given size and alignment constraints.
331      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
332      * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
333      * @return a segment for the newly allocated memory block.
334      */
335     MemorySegment allocate(long bytesSize, long bytesAlignment);
336 
337     /**
338      * Returns a native unbounded arena-based allocator, with predefined block size and maximum arena size,
339      * associated with the provided scope. Equivalent to the following code:
340      * <blockquote><pre>{@code
341     SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, scope);
342      * }</pre></blockquote>







343      *
344      * @param scope the scope associated with the segments returned by the arena-based allocator.
345      * @return a new unbounded arena-based allocator


346      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
347      * than the thread owning {@code scope}.
348      */
349     static SegmentAllocator newNativeArena(ResourceScope scope) {
350         return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, scope);
351     }
352 
353     /**
354      * Returns a native unbounded arena-based allocator, with block size set to the specified arena size, associated with
355      * the provided scope, with given arena size. Equivalent to the following code:
356      * <blockquote><pre>{@code
357     SegmentAllocator.newNativeArena(arenaSize, arenaSize, scope);
358      * }</pre></blockquote>
359      *
360      * @param arenaSize the size (in bytes) of the allocation arena.
361      * @param scope the scope associated with the segments returned by the arena-based allocator.
362      * @return a new unbounded arena-based allocator
363      * @throws IllegalArgumentException if {@code arenaSize <= 0}.
364      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
365      * than the thread owning {@code scope}.
366      */
367     static SegmentAllocator newNativeArena(long arenaSize, ResourceScope scope) {
368         return newNativeArena(arenaSize, arenaSize, scope);
369     }
370 
371     /**
372      * Returns a native arena-based allocator, associated with the provided scope, with given arena size and block size.
373      * <p>
374      * The returned allocator {@linkplain MemorySegment#allocateNative(long, ResourceScope) allocates} a memory segment
375      * {@code S} of the specified block size and then responds to allocation requests in one of the following ways:
376      * <ul>
377      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a <em>free</em>
378      *     slice {@code S'} which fits that allocation request, return that {@code S'}.
379      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no <em>free</em>
380      *     slices which fits that allocation request, allocate a new segment {@code S'}, which has same size as {@code S}
381      *     and set {@code S = S'}; the allocator then tries to respond to the same allocation request again.
382      *     <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
383      *     which has a sufficient size to satisfy the allocation request, and return {@code S'}.
384      * </ul>
385      * <p>
386      * This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
387      * cost associated with allocating a new off-heap memory region upon each allocation request.
388      * <p>
389      * An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be
390      * performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope,
391      * allocation requests can only occur from the thread owning the allocator's resource scope.
392      * <p>
393      * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
394      * exceeds the arena size, or the system capacity. Furthermore, the returned allocator is not thread safe, and all
395      * allocation requests should occur within a single thread (regardless of the scope associated with the native arena).
396      *
397      * @param arenaSize the size (in bytes) of the allocation arena.
398      * @param blockSize the block size associated with the arena-based allocator.
399      * @param scope the scope associated with the segments returned by the arena-based allocator.
400      * @return a new unbounded arena-based allocator
401      * @throws IllegalArgumentException if {@code blockSize <= 0}, if {@code arenaSize <= 0} or if {@code arenaSize < blockSize}.
402      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
403      * than the thread owning {@code scope}.
404      */
405     static SegmentAllocator newNativeArena(long arenaSize, long blockSize, ResourceScope scope) {
406         Objects.requireNonNull(scope);
407         if (blockSize <= 0) {
408             throw new IllegalArgumentException("Invalid block size: " + blockSize);
409         }
410         if (arenaSize <= 0 || arenaSize < blockSize) {
411             throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
412         }
413         return new ArenaAllocator(blockSize, arenaSize, scope);
414     }
415 
416     /**
417      * Returns a segment allocator which responds to allocation requests by recycling a single segment; that is,
418      * each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
419      * constraints are ignored by this allocator), hence the name <em>prefix allocator</em>.
420      * Equivalent to (but likely more efficient than) the following code:
421      * <blockquote><pre>{@code
422     MemorySegment segment = ...
423     SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size);
424      * }</pre></blockquote>
425      * <p>
426      * This allocator can be useful to limit allocation requests in case a client
427      * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request
428      * takes place.
429      * <p>
430      * While the allocator returned by this method is <em>thread-safe</em>, concurrent access on the same recycling
431      * allocator might cause a thread to overwrite contents written to the underlying segment by a different thread.
432      *
433      * @param segment the memory segment to be recycled by the returned allocator.
434      * @return an allocator which recycles an existing segment upon each new allocation request.
435      */
436     static SegmentAllocator prefixAllocator(MemorySegment segment) {
437         Objects.requireNonNull(segment);
438         return (AbstractMemorySegmentImpl)segment;
439     }
440 
441     /**
442      * Returns a native allocator, associated with the provided scope. Equivalent to (but likely more efficient than)
443      * the following code:

444      * <blockquote><pre>{@code
445     ResourceScope scope = ...
446     SegmentAllocator nativeAllocator = (size, align) -> MemorySegment.allocateNative(size, align, scope);
447      * }</pre></blockquote>
448      *
449      * @param scope the scope associated with the returned allocator.
450      * @return a native allocator, associated with the provided scope.
451      */
452     static SegmentAllocator nativeAllocator(ResourceScope scope) {
453         Objects.requireNonNull(scope);
454         return (ResourceScopeImpl)scope;
455     }
456 
457     /**
458      * Returns a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
459      * Equivalent to (but likely more efficient than) the following code:
460      * <blockquote><pre>{@code
461     ResourceScope scope = ...
462     SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
463      * }</pre></blockquote>
464      *
465      * @return a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
466      */
467     static SegmentAllocator implicitAllocator() {
468         class Holder {
469             static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) ->
470                     MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
471         }
472         return Holder.IMPLICIT_ALLOCATOR;
473     }
474 }
< prev index next >