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