1 /* 2 * Copyright (c) 2019, 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 * @test 26 * @run testng TestSegments 27 */ 28 29 import jdk.incubator.foreign.MemoryAddress; 30 import jdk.incubator.foreign.MemoryLayout; 31 import jdk.incubator.foreign.MemoryLayouts; 32 import jdk.incubator.foreign.MemorySegment; 33 import org.testng.annotations.DataProvider; 34 import org.testng.annotations.Test; 35 import java.lang.invoke.VarHandle; 36 import java.lang.reflect.Method; 37 import java.lang.reflect.Modifier; 38 import java.nio.ByteBuffer; 39 import java.nio.ByteOrder; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Spliterator; 43 import java.util.concurrent.atomic.AtomicBoolean; 44 import java.util.concurrent.atomic.AtomicReference; 45 import java.util.function.LongFunction; 46 import java.util.function.Supplier; 47 import java.util.stream.Stream; 48 import static jdk.incubator.foreign.MemorySegment.WRITE; 49 import static org.testng.Assert.*; 50 51 public class TestSegments { 52 53 @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) 54 public void testBadAllocateAlign(long size, long align) { 55 MemorySegment.allocateNative(size, align); 56 } 57 58 @Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class) 59 public void testBadAllocateLayout(MemoryLayout layout) { 60 MemorySegment.allocateNative(layout); 61 } 62 63 @Test(expectedExceptions = { OutOfMemoryError.class, 64 IllegalArgumentException.class }) 65 public void testAllocateTooBig() { 66 MemorySegment.allocateNative(Long.MAX_VALUE); 67 } 68 69 @Test(dataProvider = "segmentOperations") 70 public void testOpOutsideConfinement(SegmentMember member) throws Throwable { 71 try (MemorySegment segment = MemorySegment.allocateNative(4)) { 72 AtomicBoolean failed = new AtomicBoolean(false); 73 Thread t = new Thread(() -> { 74 try { 75 Object o = member.method.invoke(segment, member.params); 76 if (member.method.getName().equals("acquire")) { 77 ((MemorySegment)o).close(); 78 } 79 } catch (ReflectiveOperationException ex) { 80 throw new IllegalStateException(ex); 81 } 82 }); 83 t.setUncaughtExceptionHandler((thread, ex) -> failed.set(true)); 84 t.start(); 85 t.join(); 86 assertEquals(failed.get(), member.isConfined()); 87 } 88 } 89 90 @Test 91 public void testNativeSegmentIsZeroed() { 92 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) 93 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); 94 try (MemorySegment segment = MemorySegment.allocateNative(1000)) { 95 for (long i = 0 ; i < segment.byteSize() ; i++) { 96 assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i)); 97 } 98 } 99 } 100 101 @Test 102 public void testNothingSegmentAccess() { 103 VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); 104 long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 }; 105 for (long value : values) { 106 MemoryAddress addr = MemoryAddress.ofLong(value); 107 try { 108 longHandle.get(addr); 109 } catch (UnsupportedOperationException ex) { 110 assertTrue(ex.getMessage().contains("Required access mode")); 111 } 112 } 113 } 114 115 @Test(expectedExceptions = UnsupportedOperationException.class) 116 public void testNothingSegmentOffset() { 117 MemoryAddress addr = MemoryAddress.ofLong(42); 118 assertNull(addr.segment()); 119 addr.segmentOffset(); 120 } 121 122 @Test 123 public void testSlices() { 124 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) 125 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); 126 try (MemorySegment segment = MemorySegment.allocateNative(10)) { 127 //init 128 for (byte i = 0 ; i < segment.byteSize() ; i++) { 129 byteHandle.set(segment.baseAddress(), (long)i, i); 130 } 131 long start = 0; 132 MemoryAddress base = segment.baseAddress(); 133 MemoryAddress last = base.addOffset(10); 134 while (!base.equals(last)) { 135 MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start); 136 for (long i = start ; i < 10 ; i++) { 137 assertEquals( 138 byteHandle.get(segment.baseAddress(), i), 139 byteHandle.get(slice.baseAddress(), i - start) 140 ); 141 } 142 base = base.addOffset(1); 143 start++; 144 } 145 } 146 } 147 148 @Test(dataProvider = "accessModes") 149 public void testAccessModes(int accessModes) { 150 int[] arr = new int[1]; 151 for (AccessActions action : AccessActions.values()) { 152 MemorySegment segment = MemorySegment.ofArray(arr); 153 MemorySegment restrictedSegment = segment.withAccessModes(accessModes); 154 assertEquals(restrictedSegment.accessModes(), accessModes); 155 boolean shouldFail = !restrictedSegment.hasAccessModes(action.accessMode); 156 try { 157 action.run(restrictedSegment); 158 assertFalse(shouldFail); 159 } catch (UnsupportedOperationException ex) { 160 assertTrue(shouldFail); 161 } 162 } 163 } 164 165 @DataProvider(name = "segmentFactories") 166 public Object[][] segmentFactories() { 167 List<Supplier<MemorySegment>> l = List.of( 168 () -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 }), 169 () -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' }), 170 () -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ), 171 () -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f }), 172 () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), 173 () -> MemorySegment.ofArray(new long[] { 1l, 2l, 3l, 4l } ), 174 () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), 175 () -> MemorySegment.allocateNative(4), 176 () -> MemorySegment.allocateNative(4, 8), 177 () -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder())) 178 ); 179 return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); 180 } 181 182 @Test(dataProvider = "segmentFactories") 183 public void testFill(Supplier<MemorySegment> memorySegmentSupplier) { 184 VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) 185 .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); 186 187 for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) { 188 try (MemorySegment segment = memorySegmentSupplier.get()) { 189 segment.fill(value); 190 for (long l = 0; l < segment.byteSize(); l++) { 191 assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value); 192 } 193 194 // fill a slice 195 var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value); 196 for (long l = 0; l < sliceSegment.byteSize(); l++) { 197 assertEquals((byte) byteHandle.get(sliceSegment.baseAddress(), l), ~value); 198 } 199 // assert enclosing slice 200 assertEquals((byte) byteHandle.get(segment.baseAddress(), 0L), value); 201 for (long l = 1; l < segment.byteSize() - 2; l++) { 202 assertEquals((byte) byteHandle.get(segment.baseAddress(), l), (byte) ~value); 203 } 204 assertEquals((byte) byteHandle.get(segment.baseAddress(), segment.byteSize() - 1L), value); 205 } 206 } 207 } 208 209 @Test(dataProvider = "segmentFactories", expectedExceptions = IllegalStateException.class) 210 public void testFillClosed(Supplier<MemorySegment> memorySegmentSupplier) { 211 MemorySegment segment = memorySegmentSupplier.get(); 212 segment.close(); 213 segment.fill((byte) 0xFF); 214 } 215 216 @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) 217 public void testFillIllegalAccessMode(Supplier<MemorySegment> memorySegmentSupplier) { 218 try (MemorySegment segment = memorySegmentSupplier.get()) { 219 segment.withAccessModes(segment.accessModes() & ~WRITE).fill((byte) 0xFF); 220 } 221 } 222 223 @Test(dataProvider = "segmentFactories") 224 public void testFillThread(Supplier<MemorySegment> memorySegmentSupplier) throws Exception { 225 try (MemorySegment segment = memorySegmentSupplier.get()) { 226 AtomicReference<RuntimeException> exception = new AtomicReference<>(); 227 Runnable action = () -> { 228 try { 229 segment.fill((byte) 0xBA); 230 } catch (RuntimeException e) { 231 exception.set(e); 232 } 233 }; 234 Thread thread = new Thread(action); 235 thread.start(); 236 thread.join(); 237 238 RuntimeException e = exception.get(); 239 if (!(e instanceof IllegalStateException)) { 240 throw e; 241 } 242 } 243 } 244 245 @Test 246 public void testFillEmpty() { 247 MemorySegment.ofArray(new byte[] { }).fill((byte) 0xFF); 248 MemorySegment.ofArray(new byte[2]).asSlice(0, 0).fill((byte) 0xFF); 249 MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0)).fill((byte) 0xFF); 250 } 251 252 @Test(expectedExceptions = IllegalArgumentException.class) 253 public void testBadWithAccessModes() { 254 int[] arr = new int[1]; 255 MemorySegment segment = MemorySegment.ofArray(arr); 256 segment.withAccessModes((1 << AccessActions.values().length) + 1); 257 } 258 259 @Test(expectedExceptions = IllegalArgumentException.class) 260 public void testBadHasAccessModes() { 261 int[] arr = new int[1]; 262 MemorySegment segment = MemorySegment.ofArray(arr); 263 segment.hasAccessModes((1 << AccessActions.values().length) + 1); 264 } 265 266 @DataProvider(name = "badSizeAndAlignments") 267 public Object[][] sizesAndAlignments() { 268 return new Object[][] { 269 { -1, 8 }, 270 { 1, 15 }, 271 { 1, -15 } 272 }; 273 } 274 275 @DataProvider(name = "badLayouts") 276 public Object[][] layouts() { 277 SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values(); 278 Object[][] values = new Object[layoutFactories.length * 2][2]; 279 for (int i = 0; i < layoutFactories.length ; i++) { 280 values[i * 2] = new Object[] { MemoryLayout.ofStruct(layoutFactories[i].make(7), MemoryLayout.ofPaddingBits(9)) }; // good size, bad align 281 values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align 282 } 283 return values; 284 } 285 286 enum SizedLayoutFactory { 287 VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)), 288 VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)), 289 PADDING(MemoryLayout::ofPaddingBits); 290 291 private final LongFunction<MemoryLayout> factory; 292 293 SizedLayoutFactory(LongFunction<MemoryLayout> factory) { 294 this.factory = factory; 295 } 296 297 MemoryLayout make(long size) { 298 return factory.apply(size); 299 } 300 } 301 302 @DataProvider(name = "segmentOperations") 303 static Object[][] segmentMembers() { 304 List<SegmentMember> members = new ArrayList<>(); 305 for (Method m : MemorySegment.class.getDeclaredMethods()) { 306 //skip statics and method declared in j.l.Object 307 if (m.getDeclaringClass().equals(Object.class) || 308 (m.getModifiers() & Modifier.STATIC) != 0) continue; 309 Object[] args = Stream.of(m.getParameterTypes()) 310 .map(TestSegments::defaultValue) 311 .toArray(); 312 members.add(new SegmentMember(m, args)); 313 } 314 return members.stream().map(ms -> new Object[] { ms }).toArray(Object[][]::new); 315 } 316 317 static class SegmentMember { 318 final Method method; 319 final Object[] params; 320 321 final static List<String> CONFINED_NAMES = List.of( 322 "close", 323 "fill", 324 "toByteArray", 325 "withOwnerThread" 326 ); 327 328 public SegmentMember(Method method, Object[] params) { 329 this.method = method; 330 this.params = params; 331 } 332 333 boolean isConfined() { 334 return CONFINED_NAMES.contains(method.getName()); 335 } 336 337 @Override 338 public String toString() { 339 return method.getName(); 340 } 341 } 342 343 static Object defaultValue(Class<?> c) { 344 if (c.isPrimitive()) { 345 if (c == char.class) { 346 return (char)0; 347 } else if (c == boolean.class) { 348 return false; 349 } else if (c == byte.class) { 350 return (byte)0; 351 } else if (c == short.class) { 352 return (short)0; 353 } else if (c == int.class) { 354 return 0; 355 } else if (c == long.class) { 356 return 0L; 357 } else if (c == float.class) { 358 return 0f; 359 } else if (c == double.class) { 360 return 0d; 361 } else { 362 throw new IllegalStateException(); 363 } 364 } else { 365 return null; 366 } 367 } 368 369 @DataProvider(name = "accessModes") 370 public Object[][] accessModes() { 371 int nActions = AccessActions.values().length; 372 Object[][] results = new Object[1 << nActions][]; 373 for (int accessModes = 0 ; accessModes < results.length ; accessModes++) { 374 results[accessModes] = new Object[] { accessModes }; 375 } 376 return results; 377 } 378 379 enum AccessActions { 380 ACQUIRE(MemorySegment.ACQUIRE) { 381 @Override 382 void run(MemorySegment segment) { 383 Spliterator<MemorySegment> spliterator = 384 MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE)); 385 AtomicReference<RuntimeException> exception = new AtomicReference<>(); 386 Runnable action = () -> { 387 try { 388 spliterator.tryAdvance(s -> { }); 389 } catch (RuntimeException e) { 390 exception.set(e); 391 } 392 }; 393 Thread thread = new Thread(action); 394 thread.start(); 395 try { 396 thread.join(); 397 } catch (InterruptedException ex) { 398 throw new AssertionError(ex); 399 } 400 RuntimeException e = exception.get(); 401 if (e != null) { 402 throw e; 403 } 404 } 405 }, 406 CLOSE(MemorySegment.CLOSE) { 407 @Override 408 void run(MemorySegment segment) { 409 segment.close(); 410 } 411 }, 412 READ(MemorySegment.READ) { 413 @Override 414 void run(MemorySegment segment) { 415 INT_HANDLE.get(segment.baseAddress()); 416 } 417 }, 418 WRITE(MemorySegment.WRITE) { 419 @Override 420 void run(MemorySegment segment) { 421 INT_HANDLE.set(segment.baseAddress(), 42); 422 } 423 }, 424 HANDOFF(MemorySegment.HANDOFF) { 425 @Override 426 void run(MemorySegment segment) { 427 segment.withOwnerThread(new Thread()); 428 } 429 }; 430 431 final int accessMode; 432 433 static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.varHandle(int.class); 434 435 AccessActions(int accessMode) { 436 this.accessMode = accessMode; 437 } 438 439 abstract void run(MemorySegment segment); 440 } 441 }