1 /*
  2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
  3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  *  This code is free software; you can redistribute it and/or modify it
  6  *  under the terms of the GNU General Public License version 2 only, as
  7  *  published by the Free Software Foundation.
  8  *
  9  *  This code is distributed in the hope that it will be useful, but WITHOUT
 10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  *  version 2 for more details (a copy is included in the LICENSE file that
 13  *  accompanied this code).
 14  *
 15  *  You should have received a copy of the GNU General Public License version
 16  *  2 along with this work; if not, write to the Free Software Foundation,
 17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  *  or visit www.oracle.com if you need additional information or have any
 21  *  questions.
 22  *
 23  */
 24 
 25 /*
 26  * @test
 27  * @run testng/othervm TestSegmentAllocators
 28  */
 29 
 30 import jdk.incubator.foreign.*;
 31 
 32 import org.testng.annotations.*;
 33 
 34 import java.lang.invoke.VarHandle;
 35 import java.nio.ByteBuffer;
 36 import java.nio.ByteOrder;
 37 import java.nio.CharBuffer;
 38 import java.nio.DoubleBuffer;
 39 import java.nio.FloatBuffer;
 40 import java.nio.IntBuffer;
 41 import java.nio.LongBuffer;
 42 import java.nio.ShortBuffer;
 43 import java.util.ArrayList;
 44 import java.util.List;
 45 import java.util.function.BiFunction;
 46 import java.util.function.Function;
 47 import java.util.stream.IntStream;
 48 import java.util.stream.LongStream;
 49 
 50 import static org.testng.Assert.*;
 51 
 52 public class TestSegmentAllocators {
 53 
 54     final static int ELEMS = 128;
 55     final static Class<?> ADDRESS_CARRIER = ValueLayout.ADDRESS.bitSize() == 64 ? long.class : int.class;
 56 
 57     @Test(dataProvider = "nativeScopes")
 58     @SuppressWarnings("unchecked")
 59     public <Z, L extends ValueLayout> void testAllocation(Z value, AllocationFactory allocationFactory, L layout, AllocationFunction<Z, L> allocationFunction, Function<MemoryLayout, VarHandle> handleFactory) {
 60         layout = (L)layout.withBitAlignment(layout.bitSize());
 61         L[] layouts = (L[])new ValueLayout[] {
 62                 layout,
 63                 layout.withBitAlignment(layout.bitAlignment() * 2),
 64                 layout.withBitAlignment(layout.bitAlignment() * 4),
 65                 layout.withBitAlignment(layout.bitAlignment() * 8)
 66         };
 67         for (L alignedLayout : layouts) {
 68             List<MemorySegment> addressList = new ArrayList<>();
 69             int elems = ELEMS / ((int)alignedLayout.byteAlignment() / (int)layout.byteAlignment());
 70             ResourceScope[] scopes = {
 71                     ResourceScope.newConfinedScope(),
 72                     ResourceScope.newSharedScope()
 73             };
 74             for (ResourceScope scope : scopes) {
 75                 try (scope) {
 76                     SegmentAllocator allocator = allocationFactory.allocator(alignedLayout.byteSize() * ELEMS, scope);
 77                     for (int i = 0; i < elems; i++) {
 78                         MemorySegment address = allocationFunction.allocate(allocator, alignedLayout, value);
 79                         assertEquals(address.byteSize(), alignedLayout.byteSize());
 80                         addressList.add(address);
 81                         VarHandle handle = handleFactory.apply(alignedLayout);
 82                         assertEquals(value, handle.get(address));
 83                     }
 84                     boolean isBound = allocationFactory.isBound();
 85                     try {
 86                         allocationFunction.allocate(allocator, alignedLayout, value);
 87                         assertFalse(isBound);
 88                     } catch (OutOfMemoryError ex) {
 89                         //failure is expected if bound
 90                         assertTrue(isBound);
 91                     }
 92                 }
 93                 if (allocationFactory != AllocationFactory.IMPLICIT_ALLOCATOR) {
 94                     // addresses should be invalid now
 95                     for (MemorySegment address : addressList) {
 96                         assertFalse(address.scope().isAlive());
 97                     }
 98                 }
 99             }
100         }
101     }
102 
103     static final int SIZE_256M = 1024 * 1024 * 256;
104 
105     @Test
106     public void testBigAllocationInUnboundedScope() {
107         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
108             SegmentAllocator allocator = SegmentAllocator.newNativeArena(scope);
109             for (int i = 8 ; i < SIZE_256M ; i *= 8) {
110                 MemorySegment address = allocator.allocate(i, i);
111                 //check size
112                 assertEquals(address.byteSize(), i);
113                 //check alignment
114                 assertEquals(address.address().toRawLongValue() % i, 0);
115             }
116         }
117     }
118 
119     @Test(expectedExceptions = OutOfMemoryError.class)
120     public void testTooBigForBoundedArena() {
121         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
122             SegmentAllocator allocator = SegmentAllocator.newNativeArena(10, scope);
123             allocator.allocate(12);
124         }
125     }
126 
127     @Test
128     public void testBiggerThanBlockForBoundedArena() {
129         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
130             SegmentAllocator allocator = SegmentAllocator.newNativeArena(4 * 1024 * 2, scope);
131             allocator.allocate(4 * 1024 + 1); // should be ok
132         }
133     }
134 
135     @Test(expectedExceptions = IllegalArgumentException.class)
136     public void testBadUnboundedArenaSize() {
137         SegmentAllocator.newNativeArena( -1, ResourceScope.globalScope());
138     }
139 
140     @Test(dataProvider = "arrayScopes")
141     public <Z> void testArray(AllocationFactory allocationFactory, ValueLayout layout, AllocationFunction<Object, ValueLayout> allocationFunction, ToArrayHelper<Z> arrayHelper) {
142         Z arr = arrayHelper.array();
143         ResourceScope[] scopes = {
144                 ResourceScope.newConfinedScope(),
145                 ResourceScope.newSharedScope()
146         };
147         for (ResourceScope scope : scopes) {
148             try (scope) {
149                 SegmentAllocator allocator = allocationFactory.allocator(100, scope);
150                 MemorySegment address = allocationFunction.allocate(allocator, layout, arr);
151                 Z found = arrayHelper.toArray(address, layout);
152                 assertEquals(found, arr);
153             }
154         }
155     }
156 
157     @DataProvider(name = "nativeScopes")
158     static Object[][] nativeScopes() {
159         List<Object[]> nativeScopes = new ArrayList<>();
160         for (AllocationFactory factory : AllocationFactory.values()) {
161             nativeScopes.add(new Object[] { (byte)42, factory, ValueLayout.JAVA_BYTE,
162                     (AllocationFunction.OfByte) SegmentAllocator::allocate,
163                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
164             nativeScopes.add(new Object[] { (short)42, factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN),
165                     (AllocationFunction.OfShort) SegmentAllocator::allocate,
166                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
167             nativeScopes.add(new Object[] { (char)42, factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN),
168                     (AllocationFunction.OfChar) SegmentAllocator::allocate,
169                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
170             nativeScopes.add(new Object[] { 42, factory,
171                     ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
172                     (AllocationFunction.OfInt) SegmentAllocator::allocate,
173                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
174             nativeScopes.add(new Object[] { 42f, factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN),
175                     (AllocationFunction.OfFloat) SegmentAllocator::allocate,
176                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
177             nativeScopes.add(new Object[] { 42L, factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN),
178                     (AllocationFunction.OfLong) SegmentAllocator::allocate,
179                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
180             nativeScopes.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN),
181                     (AllocationFunction.OfDouble) SegmentAllocator::allocate,
182                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
183             nativeScopes.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN),
184                     (AllocationFunction.OfAddress) SegmentAllocator::allocate,
185                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
186 
187             nativeScopes.add(new Object[] { (short)42, factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN),
188                     (AllocationFunction.OfShort) SegmentAllocator::allocate,
189                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
190             nativeScopes.add(new Object[] { (char)42, factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN),
191                     (AllocationFunction.OfChar) SegmentAllocator::allocate,
192                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
193             nativeScopes.add(new Object[] { 42, factory,
194                     ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
195                     (AllocationFunction.OfInt) SegmentAllocator::allocate,
196                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
197             nativeScopes.add(new Object[] { 42f, factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN),
198                     (AllocationFunction.OfFloat) SegmentAllocator::allocate,
199                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
200             nativeScopes.add(new Object[] { 42L, factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN),
201                     (AllocationFunction.OfLong) SegmentAllocator::allocate,
202                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
203             nativeScopes.add(new Object[] { 42d, factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN),
204                     (AllocationFunction.OfDouble) SegmentAllocator::allocate,
205                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
206             nativeScopes.add(new Object[] { MemoryAddress.ofLong(42), factory, ValueLayout.ADDRESS.withOrder(ByteOrder.BIG_ENDIAN),
207                     (AllocationFunction.OfAddress) SegmentAllocator::allocate,
208                     (Function<MemoryLayout, VarHandle>)l -> l.varHandle() });
209         }
210         return nativeScopes.toArray(Object[][]::new);
211     }
212 
213     @DataProvider(name = "arrayScopes")
214     static Object[][] arrayScopes() {
215         List<Object[]> arrayScopes = new ArrayList<>();
216         for (AllocationFactory factory : AllocationFactory.values()) {
217             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_BYTE,
218                     (AllocationFunction.OfByteArray) SegmentAllocator::allocateArray,
219                     ToArrayHelper.toByteArray });
220             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.LITTLE_ENDIAN),
221                     (AllocationFunction.OfCharArray) SegmentAllocator::allocateArray,
222                     ToArrayHelper.toCharArray });
223             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.LITTLE_ENDIAN),
224                     (AllocationFunction.OfShortArray) SegmentAllocator::allocateArray,
225                     ToArrayHelper.toShortArray });
226             arrayScopes.add(new Object[] { factory,
227                     ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN),
228                     (AllocationFunction.OfIntArray) SegmentAllocator::allocateArray,
229                     ToArrayHelper.toIntArray });
230             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.LITTLE_ENDIAN),
231                     (AllocationFunction.OfFloatArray) SegmentAllocator::allocateArray,
232                     ToArrayHelper.toFloatArray });
233             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.LITTLE_ENDIAN),
234                     (AllocationFunction.OfLongArray) SegmentAllocator::allocateArray,
235                     ToArrayHelper.toLongArray });
236             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.LITTLE_ENDIAN),
237                     (AllocationFunction.OfDoubleArray) SegmentAllocator::allocateArray,
238                     ToArrayHelper.toDoubleArray });
239 
240             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN),
241                     (AllocationFunction.OfCharArray) SegmentAllocator::allocateArray,
242                     ToArrayHelper.toCharArray });
243             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN),
244                     (AllocationFunction.OfShortArray) SegmentAllocator::allocateArray,
245                     ToArrayHelper.toShortArray });
246             arrayScopes.add(new Object[] { factory,
247                     ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN),
248                     (AllocationFunction.OfIntArray) SegmentAllocator::allocateArray,
249                     ToArrayHelper.toIntArray });
250             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN),
251                     (AllocationFunction.OfFloatArray) SegmentAllocator::allocateArray,
252                     ToArrayHelper.toFloatArray });
253             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN),
254                     (AllocationFunction.OfLongArray) SegmentAllocator::allocateArray,
255                     ToArrayHelper.toLongArray });
256             arrayScopes.add(new Object[] { factory, ValueLayout.JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN),
257                     (AllocationFunction.OfDoubleArray) SegmentAllocator::allocateArray,
258                     ToArrayHelper.toDoubleArray });
259         };
260         return arrayScopes.toArray(Object[][]::new);
261     }
262 
263     interface AllocationFunction<X, L extends ValueLayout> {
264         MemorySegment allocate(SegmentAllocator allocator, L layout, X value);
265 
266         interface OfByte extends AllocationFunction<Byte, ValueLayout.OfByte> { }
267         interface OfBoolean extends AllocationFunction<Boolean, ValueLayout.OfBoolean> { }
268         interface OfChar extends AllocationFunction<Character, ValueLayout.OfChar> { }
269         interface OfShort extends AllocationFunction<Short, ValueLayout.OfShort> { }
270         interface OfInt extends AllocationFunction<Integer, ValueLayout.OfInt> { }
271         interface OfFloat extends AllocationFunction<Float, ValueLayout.OfFloat> { }
272         interface OfLong extends AllocationFunction<Long, ValueLayout.OfLong> { }
273         interface OfDouble extends AllocationFunction<Double, ValueLayout.OfDouble> { }
274         interface OfAddress extends AllocationFunction<MemoryAddress, ValueLayout.OfAddress> { }
275 
276         interface OfByteArray extends AllocationFunction<byte[], ValueLayout.OfByte> { }
277         interface OfCharArray extends AllocationFunction<char[], ValueLayout.OfChar> { }
278         interface OfShortArray extends AllocationFunction<short[], ValueLayout.OfShort> { }
279         interface OfIntArray extends AllocationFunction<int[], ValueLayout.OfInt> { }
280         interface OfFloatArray extends AllocationFunction<float[], ValueLayout.OfFloat> { }
281         interface OfLongArray extends AllocationFunction<long[], ValueLayout.OfLong> { }
282         interface OfDoubleArray extends AllocationFunction<double[], ValueLayout.OfDouble> { }
283     }
284 
285     enum AllocationFactory {
286         ARENA_BOUNDED(true, SegmentAllocator::newNativeArena),
287         ARENA_UNBOUNDED(false, (size, scope) -> SegmentAllocator.newNativeArena(scope)),
288         NATIVE_ALLOCATOR(false, (size, scope) -> SegmentAllocator.nativeAllocator(scope)),
289         IMPLICIT_ALLOCATOR(false, (size, scope) -> SegmentAllocator.implicitAllocator());
290 
291         private final boolean isBound;
292         private final BiFunction<Long, ResourceScope, SegmentAllocator> factory;
293 
294         AllocationFactory(boolean isBound, BiFunction<Long, ResourceScope, SegmentAllocator> factory) {
295             this.isBound = isBound;
296             this.factory = factory;
297         }
298 
299         SegmentAllocator allocator(long size, ResourceScope scope) {
300             return factory.apply(size, scope);
301         }
302 
303         public boolean isBound() {
304             return isBound;
305         }
306     }
307 
308     interface ToArrayHelper<T> {
309         T array();
310         T toArray(MemorySegment segment, ValueLayout layout);
311 
312         ToArrayHelper<byte[]> toByteArray = new ToArrayHelper<>() {
313             @Override
314             public byte[] array() {
315                 return new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
316             }
317 
318             @Override
319             public byte[] toArray(MemorySegment segment, ValueLayout layout) {
320                 ByteBuffer buffer = segment.asByteBuffer().order(layout.order());
321                 byte[] found = new byte[buffer.limit()];
322                 buffer.get(found);
323                 return found;
324             }
325         };
326 
327         ToArrayHelper<char[]> toCharArray = new ToArrayHelper<>() {
328             @Override
329             public char[] array() {
330                 return new char[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
331             }
332 
333             @Override
334             public char[] toArray(MemorySegment segment, ValueLayout layout) {
335                 CharBuffer buffer = segment.asByteBuffer().order(layout.order()).asCharBuffer();
336                 char[] found = new char[buffer.limit()];
337                 buffer.get(found);
338                 return found;
339             }
340         };
341 
342         ToArrayHelper<short[]> toShortArray = new ToArrayHelper<>() {
343             @Override
344             public short[] array() {
345                 return new short[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
346             }
347 
348             @Override
349             public short[] toArray(MemorySegment segment, ValueLayout layout) {
350                 ShortBuffer buffer = segment.asByteBuffer().order(layout.order()).asShortBuffer();
351                 short[] found = new short[buffer.limit()];
352                 buffer.get(found);
353                 return found;
354             }
355         };
356 
357         ToArrayHelper<int[]> toIntArray = new ToArrayHelper<>() {
358             @Override
359             public int[] array() {
360                 return new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
361             }
362 
363             @Override
364             public int[] toArray(MemorySegment segment, ValueLayout layout) {
365                 IntBuffer buffer = segment.asByteBuffer().order(layout.order()).asIntBuffer();
366                 int[] found = new int[buffer.limit()];
367                 buffer.get(found);
368                 return found;
369             }
370         };
371 
372         ToArrayHelper<float[]> toFloatArray = new ToArrayHelper<>() {
373             @Override
374             public float[] array() {
375                 return new float[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
376             }
377 
378             @Override
379             public float[] toArray(MemorySegment segment, ValueLayout layout) {
380                 FloatBuffer buffer = segment.asByteBuffer().order(layout.order()).asFloatBuffer();
381                 float[] found = new float[buffer.limit()];
382                 buffer.get(found);
383                 return found;
384             }
385         };
386 
387         ToArrayHelper<long[]> toLongArray = new ToArrayHelper<>() {
388             @Override
389             public long[] array() {
390                 return new long[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
391             }
392 
393             @Override
394             public long[] toArray(MemorySegment segment, ValueLayout layout) {
395                 LongBuffer buffer = segment.asByteBuffer().order(layout.order()).asLongBuffer();
396                 long[] found = new long[buffer.limit()];
397                 buffer.get(found);
398                 return found;
399             }
400         };
401 
402         ToArrayHelper<double[]> toDoubleArray = new ToArrayHelper<>() {
403             @Override
404             public double[] array() {
405                 return new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
406             }
407 
408             @Override
409             public double[] toArray(MemorySegment segment, ValueLayout layout) {
410                 DoubleBuffer buffer = segment.asByteBuffer().order(layout.order()).asDoubleBuffer();
411                 double[] found = new double[buffer.limit()];
412                 buffer.get(found);
413                 return found;
414             }
415         };
416 
417         ToArrayHelper<MemoryAddress[]> toAddressArray = new ToArrayHelper<>() {
418             @Override
419             public MemoryAddress[] array() {
420                 return switch ((int) ValueLayout.ADDRESS.byteSize()) {
421                     case 4 -> wrap(toIntArray.array());
422                     case 8 -> wrap(toLongArray.array());
423                     default -> throw new IllegalStateException("Cannot get here");
424                 };
425             }
426 
427             @Override
428             public MemoryAddress[] toArray(MemorySegment segment, ValueLayout layout) {
429                 return switch ((int)layout.byteSize()) {
430                     case 4 -> wrap(toIntArray.toArray(segment, layout));
431                     case 8 -> wrap(toLongArray.toArray(segment, layout));
432                     default -> throw new IllegalStateException("Cannot get here");
433                 };
434             }
435 
436             private MemoryAddress[] wrap(int[] ints) {
437                 return IntStream.of(ints).mapToObj(MemoryAddress::ofLong).toArray(MemoryAddress[]::new);
438             }
439 
440             private MemoryAddress[] wrap(long[] ints) {
441                 return LongStream.of(ints).mapToObj(MemoryAddress::ofLong).toArray(MemoryAddress[]::new);
442             }
443         };
444     }
445 }