1 /* 2 * Copyright (c) 2021, 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 --enable-native-access=ALL-UNNAMED TestScopedOperations 29 */ 30 31 import java.lang.foreign.Arena; 32 import java.lang.foreign.MemorySegment; 33 import java.lang.foreign.ValueLayout; 34 35 import org.testng.annotations.DataProvider; 36 import org.testng.annotations.Test; 37 38 import java.io.File; 39 import java.io.IOException; 40 import java.nio.channels.FileChannel; 41 import java.nio.file.Path; 42 import java.nio.file.StandardOpenOption; 43 import java.util.ArrayList; 44 import java.util.List; 45 import java.util.concurrent.atomic.AtomicReference; 46 import java.util.function.Consumer; 47 import java.util.function.Function; 48 49 import static java.lang.foreign.ValueLayout.JAVA_BYTE; 50 import static org.testng.Assert.assertEquals; 51 import static org.testng.Assert.assertNotNull; 52 import static org.testng.Assert.assertTrue; 53 import static org.testng.Assert.fail; 54 55 public class TestScopedOperations { 56 57 static Path tempPath; 58 59 static { 60 try { 61 File file = File.createTempFile("scopedBuffer", "txt"); 62 file.deleteOnExit(); 63 tempPath = file.toPath(); 64 } catch (IOException ex) { 65 throw new ExceptionInInitializerError(ex); 66 } 67 } 68 69 @Test(dataProvider = "scopedOperations") 70 public <Z> void testOpAfterClose(String name, ScopedOperation<Z> scopedOperation) { 71 Arena arena = Arena.ofConfined(); 72 Z obj = scopedOperation.apply(arena); 73 arena.close(); 74 try { 75 scopedOperation.accept(obj); 76 fail(); 77 } catch (IllegalStateException ex) { 78 assertTrue(ex.getMessage().contains("closed")); 79 } 80 } 81 82 @Test(dataProvider = "scopedOperations") 83 public <Z> void testOpOutsideConfinement(String name, ScopedOperation<Z> scopedOperation) { 84 try (Arena arena = Arena.ofConfined()) { 85 Z obj = scopedOperation.apply(arena); 86 AtomicReference<Throwable> failed = new AtomicReference<>(); 87 Thread t = new Thread(() -> { 88 try { 89 scopedOperation.accept(obj); 90 } catch (Throwable ex) { 91 failed.set(ex); 92 } 93 }); 94 t.start(); 95 t.join(); 96 assertNotNull(failed.get()); 97 assertEquals(failed.get().getClass(), WrongThreadException.class); 98 assertTrue(failed.get().getMessage().contains("outside")); 99 } catch (InterruptedException ex) { 100 throw new AssertionError(ex); 101 } 102 } 103 104 static List<ScopedOperation> scopedOperations = new ArrayList<>(); 105 106 static { 107 // session operations 108 ScopedOperation.ofScope(session -> session.allocate(100, 1), "MemorySession::allocate"); 109 ScopedOperation.ofScope(session -> { 110 try (FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) { 111 fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 10L, session); 112 } catch (IOException ex) { 113 fail(); 114 } 115 }, "FileChannel::map"); 116 // segment operations 117 ScopedOperation.ofSegment(s -> s.toArray(JAVA_BYTE), "MemorySegment::toArray(BYTE)"); 118 ScopedOperation.ofSegment(s -> s.copyFrom(s), "MemorySegment::copyFrom"); 119 ScopedOperation.ofSegment(s -> s.mismatch(s), "MemorySegment::mismatch"); 120 ScopedOperation.ofSegment(s -> s.fill((byte) 0), "MemorySegment::fill"); 121 // allocator operations 122 ScopedOperation.ofScope(a -> a.allocate(1), "Arena::allocate/size"); 123 ScopedOperation.ofScope(a -> a.allocate(1, 1), "Arena::allocate/size/align"); 124 ScopedOperation.ofScope(a -> a.allocate(JAVA_BYTE), "Arena::allocate/layout"); 125 ScopedOperation.ofScope(a -> a.allocate(JAVA_BYTE, (byte) 0), "Arena::allocate/byte"); 126 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_CHAR, (char) 0), "Arena::allocate/char"); 127 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_SHORT, (short) 0), "Arena::allocate/short"); 128 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_INT, 0), "Arena::allocate/int"); 129 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_FLOAT, 0f), "Arena::allocate/float"); 130 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_LONG, 0L), "Arena::allocate/long"); 131 ScopedOperation.ofScope(a -> a.allocate(ValueLayout.JAVA_DOUBLE, 0d), "Arena::allocate/double"); 132 ScopedOperation.ofScope(a -> a.allocateArray(JAVA_BYTE, 1L), "Arena::allocateArray/size"); 133 ScopedOperation.ofScope(a -> a.allocateArray(JAVA_BYTE, new byte[]{0}), "Arena::allocateArray/byte"); 134 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_CHAR, new char[]{0}), "Arena::allocateArray/char"); 135 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_SHORT, new short[]{0}), "Arena::allocateArray/short"); 136 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_INT, new int[]{0}), "Arena::allocateArray/int"); 137 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_FLOAT, new float[]{0}), "Arena::allocateArray/float"); 138 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_LONG, new long[]{0}), "Arena::allocateArray/long"); 139 ScopedOperation.ofScope(a -> a.allocateArray(ValueLayout.JAVA_DOUBLE, new double[]{0}), "Arena::allocateArray/double"); 140 }; 141 142 @DataProvider(name = "scopedOperations") 143 static Object[][] scopedOperations() { 144 return scopedOperations.stream().map(op -> new Object[] { op.name, op }).toArray(Object[][]::new); 145 } 146 147 static class ScopedOperation<X> implements Consumer<X>, Function<Arena, X> { 148 149 final Function<Arena, X> factory; 150 final Consumer<X> operation; 151 final String name; 152 153 private ScopedOperation(Function<Arena, X> factory, Consumer<X> operation, String name) { 154 this.factory = factory; 155 this.operation = operation; 156 this.name = name; 157 } 158 159 @Override 160 public void accept(X obj) { 161 operation.accept(obj); 162 } 163 164 @Override 165 public X apply(Arena session) { 166 return factory.apply(session); 167 } 168 169 static <Z> void of(Function<Arena, Z> factory, Consumer<Z> consumer, String name) { 170 scopedOperations.add(new ScopedOperation<>(factory, consumer, name)); 171 } 172 173 static void ofScope(Consumer<Arena> scopeConsumer, String name) { 174 scopedOperations.add(new ScopedOperation<>(Function.identity(), scopeConsumer, name)); 175 } 176 177 static void ofSegment(Consumer<MemorySegment> segmentConsumer, String name) { 178 for (SegmentFactory segmentFactory : SegmentFactory.values()) { 179 scopedOperations.add(new ScopedOperation<>( 180 segmentFactory.segmentFactory, 181 segmentConsumer, 182 segmentFactory.name() + "/" + name)); 183 } 184 } 185 186 enum SegmentFactory { 187 188 NATIVE(session -> session.allocate(10, 1)), 189 MAPPED(session -> { 190 try (FileChannel fileChannel = FileChannel.open(Path.of("foo.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE)) { 191 return fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 10L, session); 192 } catch (IOException ex) { 193 throw new AssertionError(ex); 194 } 195 }), 196 UNSAFE(session -> MemorySegment.NULL.reinterpret(10, session, null)); 197 198 static { 199 try { 200 File f = new File("foo.txt"); 201 f.createNewFile(); 202 f.deleteOnExit(); 203 } catch (IOException ex) { 204 throw new ExceptionInInitializerError(ex); 205 } 206 } 207 208 final Function<Arena, MemorySegment> segmentFactory; 209 210 SegmentFactory(Function<Arena, MemorySegment> segmentFactory) { 211 this.segmentFactory = segmentFactory; 212 } 213 } 214 } 215 }