1 /* 2 * Copyright (c) 2019, 2023, 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 * @test 26 * @enablePreview 27 * @requires jdk.foreign.linker != "UNSUPPORTED" 28 * @run testng/othervm -Xmx4G -XX:MaxDirectMemorySize=1M --enable-native-access=ALL-UNNAMED TestSegments 29 */ 30 31 import java.lang.foreign.*; 32 33 import org.testng.annotations.DataProvider; 34 import org.testng.annotations.Test; 35 36 import java.lang.invoke.VarHandle; 37 import java.nio.ByteBuffer; 38 import java.util.List; 39 import java.util.concurrent.atomic.AtomicInteger; 40 import java.util.concurrent.atomic.AtomicReference; 41 import java.util.function.IntFunction; 42 import java.util.function.Supplier; 43 44 import static java.lang.foreign.ValueLayout.JAVA_INT; 45 import static org.testng.Assert.*; 46 47 public class TestSegments { 48 49 @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) 50 public void testBadAllocateAlign(long size, long align) { 51 Arena.ofAuto().allocate(size, align); 52 } 53 54 @Test 55 public void testZeroLengthNativeSegment() { 56 try (Arena arena = Arena.ofConfined()) { 57 Arena session = arena; 58 var segment = session.allocate(0, 1); 59 assertEquals(segment.byteSize(), 0); 60 MemoryLayout seq = MemoryLayout.sequenceLayout(0, JAVA_INT); 61 segment = session.allocate(seq); 62 assertEquals(segment.byteSize(), 0); 63 assertEquals(segment.address() % seq.byteAlignment(), 0); 64 segment = session.allocate(0, 4); 65 assertEquals(segment.byteSize(), 0); 66 assertEquals(segment.address() % 4, 0); 67 MemorySegment rawAddress = MemorySegment.ofAddress(segment.address()); 68 assertEquals(rawAddress.byteSize(), 0); 69 assertEquals(rawAddress.address() % 4, 0); 70 } 71 } 72 73 @Test(expectedExceptions = { OutOfMemoryError.class, 74 IllegalArgumentException.class }) 75 public void testAllocateTooBig() { 76 Arena.ofAuto().allocate(Long.MAX_VALUE, 1); 77 } 78 79 @Test(expectedExceptions = OutOfMemoryError.class) 80 public void testNativeAllocationTooBig() { 81 Arena scope = Arena.ofAuto(); 82 MemorySegment segment = scope.allocate(1024L * 1024 * 8 * 2, 1); // 2M 83 } 84 85 @Test 86 public void testNativeSegmentIsZeroed() { 87 VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); 88 try (Arena arena = Arena.ofConfined()) { 89 MemorySegment segment = arena.allocate(1000, 1); 90 for (long i = 0 ; i < segment.byteSize() ; i++) { 91 assertEquals(0, (byte)byteHandle.get(segment, i)); 92 } 93 } 94 } 95 96 @Test 97 public void testSlices() { 98 VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); 99 try (Arena arena = Arena.ofConfined()) { 100 MemorySegment segment = arena.allocate(10, 1); 101 //init 102 for (byte i = 0 ; i < segment.byteSize() ; i++) { 103 byteHandle.set(segment, (long)i, i); 104 } 105 for (int offset = 0 ; offset < 10 ; offset++) { 106 MemorySegment slice = segment.asSlice(offset); 107 for (long i = offset ; i < 10 ; i++) { 108 assertEquals( 109 byteHandle.get(segment, i), 110 byteHandle.get(slice, i - offset) 111 ); 112 } 113 } 114 } 115 } 116 117 @Test(dataProvider = "segmentFactories") 118 public void testDerivedScopes(Supplier<MemorySegment> segmentSupplier) { 119 MemorySegment segment = segmentSupplier.get(); 120 assertEquals(segment.scope(), segment.scope()); 121 // one level 122 assertEquals(segment.asSlice(0).scope(), segment.scope()); 123 assertEquals(segment.asReadOnly().scope(), segment.scope()); 124 // two levels 125 assertEquals(segment.asSlice(0).asReadOnly().scope(), segment.scope()); 126 assertEquals(segment.asReadOnly().asSlice(0).scope(), segment.scope()); 127 // check fresh every time 128 MemorySegment another = segmentSupplier.get(); 129 assertNotEquals(segment.scope(), another.scope()); 130 } 131 132 @Test 133 public void testEqualsOffHeap() { 134 try (Arena arena = Arena.ofConfined()) { 135 Arena scope1 = arena; 136 MemorySegment segment = scope1.allocate(100, 1); 137 assertEquals(segment, segment.asReadOnly()); 138 assertEquals(segment, segment.asSlice(0, 100)); 139 assertNotEquals(segment, segment.asSlice(10, 90)); 140 assertEquals(segment, segment.asSlice(0, 90)); 141 assertEquals(segment, MemorySegment.ofAddress(segment.address())); 142 MemorySegment segment2 = arena.allocate(100, 1); 143 assertNotEquals(segment, segment2); 144 } 145 } 146 147 @Test 148 public void testEqualsOnHeap() { 149 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 150 assertEquals(segment, segment.asReadOnly()); 151 assertEquals(segment, segment.asSlice(0, 100)); 152 assertNotEquals(segment, segment.asSlice(10, 90)); 153 assertEquals(segment, segment.asSlice(0, 90)); 154 MemorySegment segment2 = MemorySegment.ofArray(new byte[100]); 155 assertNotEquals(segment, segment2); 156 } 157 158 @Test 159 public void testHashCodeOffHeap() { 160 try (Arena arena = Arena.ofConfined()) { 161 MemorySegment segment = arena.allocate(100, 1); 162 assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); 163 assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); 164 assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); 165 assertEquals(segment.hashCode(), MemorySegment.ofAddress(segment.address()).hashCode()); 166 } 167 } 168 169 @Test 170 public void testHashCodeOnHeap() { 171 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 172 assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); 173 assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); 174 assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); 175 } 176 177 @Test(expectedExceptions = IndexOutOfBoundsException.class) 178 public void testSmallSegmentMax() { 179 long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when cast to int 180 Arena scope = Arena.ofAuto(); 181 MemorySegment memorySegment = scope.allocate(10, 1); 182 memorySegment.get(JAVA_INT, offset); 183 } 184 185 @Test(expectedExceptions = IndexOutOfBoundsException.class) 186 public void testSmallSegmentMin() { 187 long offset = ((long)Integer.MIN_VALUE * 2L) + 6L; // underflows to 6 when cast to int 188 Arena scope = Arena.ofAuto(); 189 MemorySegment memorySegment = scope.allocate(10L, 1); 190 memorySegment.get(JAVA_INT, offset); 191 } 192 193 @Test 194 public void testSegmentOOBMessage() { 195 try { 196 var segment = Arena.global().allocate(10, 1); 197 segment.getAtIndex(ValueLayout.JAVA_INT, 2); 198 } catch (IndexOutOfBoundsException ex) { 199 assertTrue(ex.getMessage().contains("Out of bound access")); 200 assertTrue(ex.getMessage().contains("offset = 8")); 201 assertTrue(ex.getMessage().contains("length = 4")); 202 } 203 } 204 205 @Test(dataProvider = "segmentFactories") 206 public void testAccessModesOfFactories(Supplier<MemorySegment> segmentSupplier) { 207 MemorySegment segment = segmentSupplier.get(); 208 assertFalse(segment.isReadOnly()); 209 } 210 211 @DataProvider(name = "scopes") 212 public Object[][] scopes() { 213 return new Object[][] { 214 { Arena.ofAuto(), false }, 215 { Arena.global(), false }, 216 { Arena.ofConfined(), true }, 217 { Arena.ofShared(), false } 218 }; 219 } 220 221 @Test(dataProvider = "scopes") 222 public void testIsAccessibleBy(Arena arena, boolean isConfined) { 223 MemorySegment segment = MemorySegment.NULL.reinterpret(arena, null); 224 assertTrue(segment.isAccessibleBy(Thread.currentThread())); 225 assertTrue(segment.isAccessibleBy(new Thread()) != isConfined); 226 } 227 228 @Test(dataProvider = "segmentFactories") 229 public void testToString(Supplier<MemorySegment> segmentSupplier) { 230 var segment = segmentSupplier.get(); 231 String s = segment.toString(); 232 assertTrue(s.startsWith("MemorySegment{")); 233 assertTrue(s.contains("address: 0x")); 234 assertTrue(s.contains("byteSize: ")); 235 if (segment.heapBase().isPresent()) { 236 assertTrue(s.contains("heapBase: [")); 237 } else { 238 assertFalse(s.contains("heapBase: ")); 239 } 240 assertFalse(s.contains("Optional")); 241 } 242 243 @DataProvider(name = "segmentFactories") 244 public Object[][] segmentFactories() { 245 List<Supplier<MemorySegment>> l = List.of( 246 () -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 }), 247 () -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' }), 248 () -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ), 249 () -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f }), 250 () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), 251 () -> MemorySegment.ofArray(new long[] { 1L, 2L, 3L, 4L } ), 252 () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), 253 () -> Arena.ofAuto().allocate(4L, 1), 254 () -> Arena.ofAuto().allocate(4L, 8), 255 () -> Arena.ofAuto().allocate(JAVA_INT), 256 () -> Arena.ofAuto().allocate(4L, 1), 257 () -> Arena.ofAuto().allocate(4L, 8), 258 () -> Arena.ofAuto().allocate(JAVA_INT) 259 260 ); 261 return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); 262 } 263 264 @Test(dataProvider = "segmentFactories") 265 public void testFill(Supplier<MemorySegment> segmentSupplier) { 266 VarHandle byteHandle = ValueLayout.JAVA_BYTE.arrayElementVarHandle(); 267 268 for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) { 269 MemorySegment segment = segmentSupplier.get(); 270 segment.fill(value); 271 for (long l = 0; l < segment.byteSize(); l++) { 272 assertEquals((byte) byteHandle.get(segment, l), value); 273 } 274 275 // fill a slice 276 var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value); 277 for (long l = 0; l < sliceSegment.byteSize(); l++) { 278 assertEquals((byte) byteHandle.get(sliceSegment, l), ~value); 279 } 280 // assert enclosing slice 281 assertEquals((byte) byteHandle.get(segment, 0L), value); 282 for (long l = 1; l < segment.byteSize() - 2; l++) { 283 assertEquals((byte) byteHandle.get(segment, l), (byte) ~value); 284 } 285 assertEquals((byte) byteHandle.get(segment, segment.byteSize() - 1L), value); 286 } 287 } 288 289 @Test(dataProvider = "segmentFactories") 290 public void testHeapBase(Supplier<MemorySegment> segmentSupplier) { 291 MemorySegment segment = segmentSupplier.get(); 292 assertEquals(segment.isNative(), !segment.heapBase().isPresent()); 293 segment = segment.asReadOnly(); 294 assertTrue(segment.heapBase().isEmpty()); 295 } 296 297 @Test 298 public void testScopeConfinedArena() { 299 try (Arena arena = Arena.ofConfined()) { 300 MemorySegment segment = arena.allocate(100); 301 assertEquals(segment.scope(), arena.scope()); 302 } 303 } 304 305 @Test 306 public void testScopeSharedArena() { 307 try (Arena arena = Arena.ofShared()) { 308 MemorySegment segment = arena.allocate(100); 309 assertEquals(segment.scope(), arena.scope()); 310 } 311 } 312 313 @Test 314 public void testScopeAutoArena() { 315 Arena arena = Arena.ofAuto(); 316 MemorySegment segment = arena.allocate(100); 317 assertEquals(segment.scope(), arena.scope()); 318 } 319 320 @Test 321 public void testScopeGlobalArena() { 322 Arena arena = Arena.global(); 323 MemorySegment segment = arena.allocate(100); 324 assertEquals(segment.scope(), arena.scope()); 325 } 326 327 @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) 328 public void testFillIllegalAccessMode(Supplier<MemorySegment> segmentSupplier) { 329 MemorySegment segment = segmentSupplier.get(); 330 segment.asReadOnly().fill((byte) 0xFF); 331 } 332 333 @Test(dataProvider = "segmentFactories") 334 public void testFillThread(Supplier<MemorySegment> segmentSupplier) throws Exception { 335 MemorySegment segment = segmentSupplier.get(); 336 AtomicReference<RuntimeException> exception = new AtomicReference<>(); 337 Runnable action = () -> { 338 try { 339 segment.fill((byte) 0xBA); 340 } catch (RuntimeException e) { 341 exception.set(e); 342 } 343 }; 344 Thread thread = new Thread(action); 345 thread.start(); 346 thread.join(); 347 348 if (!segment.isAccessibleBy(Thread.currentThread())) { 349 RuntimeException e = exception.get(); 350 throw e; 351 } else { 352 assertNull(exception.get()); 353 } 354 } 355 356 @Test 357 public void testFillEmpty() { 358 MemorySegment.ofArray(new byte[] { }).fill((byte) 0xFF); 359 MemorySegment.ofArray(new byte[2]).asSlice(0, 0).fill((byte) 0xFF); 360 MemorySegment.ofBuffer(ByteBuffer.allocateDirect(0)).fill((byte) 0xFF); 361 } 362 363 @Test(dataProvider = "heapFactories") 364 public void testVirtualizedBaseAddress(IntFunction<MemorySegment> heapSegmentFactory, int factor) { 365 MemorySegment segment = heapSegmentFactory.apply(10); 366 assertEquals(segment.address(), 0); // base address should be zero (no leaking of impl details) 367 MemorySegment end = segment.asSlice(segment.byteSize(), 0); 368 assertEquals(end.address(), segment.byteSize()); // end address should be equal to segment byte size 369 } 370 371 @Test 372 void testReinterpret() { 373 AtomicInteger counter = new AtomicInteger(); 374 try (Arena arena = Arena.ofConfined()){ 375 // check size 376 assertEquals(MemorySegment.ofAddress(42).reinterpret(100).byteSize(), 100); 377 assertEquals(MemorySegment.ofAddress(42).reinterpret(100, Arena.ofAuto(), null).byteSize(), 100); 378 // check scope and cleanup 379 assertEquals(MemorySegment.ofAddress(42).reinterpret(100, arena, s -> counter.incrementAndGet()).scope(), arena.scope()); 380 assertEquals(MemorySegment.ofAddress(42).reinterpret(arena, s -> counter.incrementAndGet()).scope(), arena.scope()); 381 } 382 assertEquals(counter.get(), 2); 383 } 384 385 @DataProvider(name = "badSizeAndAlignments") 386 public Object[][] sizesAndAlignments() { 387 return new Object[][] { 388 { -1, 8 }, 389 { 1, 15 }, 390 { 1, -15 } 391 }; 392 } 393 394 @DataProvider(name = "heapFactories") 395 public Object[][] heapFactories() { 396 return new Object[][] { 397 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new byte[size]), 1 }, 398 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new char[size]), 2 }, 399 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new short[size]), 2 }, 400 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new int[size]), 4 }, 401 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new float[size]), 4 }, 402 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new long[size]), 8 }, 403 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new double[size]), 8 } 404 }; 405 } 406 }