1 /* 2 * Copyright (c) 2022, 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 import org.testng.annotations.Test; 25 26 import java.lang.foreign.*; 27 import java.lang.foreign.Arena; 28 import java.lang.invoke.MethodHandle; 29 import java.nio.file.Path; 30 import java.util.concurrent.ExecutorService; 31 import java.util.concurrent.Executors; 32 import java.util.concurrent.TimeUnit; 33 34 import static org.testng.Assert.*; 35 36 /* 37 * @test 38 * @enablePreview 39 * @requires jdk.foreign.linker != "UNSUPPORTED" 40 * @run testng/othervm --enable-native-access=ALL-UNNAMED LibraryLookupTest 41 */ 42 public class LibraryLookupTest { 43 44 static final Path JAVA_LIBRARY_PATH = Path.of(System.getProperty("java.library.path")); 45 static final MethodHandle INC = Linker.nativeLinker().downcallHandle(FunctionDescriptor.ofVoid()); 46 static final Path LIB_PATH = JAVA_LIBRARY_PATH.resolve(System.mapLibraryName("LibraryLookup")); 47 48 @Test 49 void testLoadLibraryConfined() { 50 try (Arena arena0 = Arena.ofConfined()) { 51 callFunc(loadLibrary(arena0)); 52 try (Arena arena1 = Arena.ofConfined()) { 53 callFunc(loadLibrary(arena1)); 54 try (Arena arena2 = Arena.ofConfined()) { 55 callFunc(loadLibrary(arena2)); 56 } 57 } 58 } 59 } 60 61 @Test(expectedExceptions = IllegalStateException.class) 62 void testLoadLibraryConfinedClosed() { 63 MemorySegment addr; 64 try (Arena arena = Arena.ofConfined()) { 65 addr = loadLibrary(arena); 66 } 67 callFunc(addr); 68 } 69 70 @Test(expectedExceptions = IllegalArgumentException.class) 71 void testLoadLibraryBadName() { 72 try (Arena arena = Arena.ofConfined()) { 73 SymbolLookup.libraryLookup(LIB_PATH.toString() + "\u0000", arena); 74 } 75 } 76 77 @Test 78 void testLoadLibraryBadLookupName() { 79 try (Arena arena = Arena.ofConfined()) { 80 SymbolLookup lookup = SymbolLookup.libraryLookup(LIB_PATH, arena); 81 assertTrue(lookup.find("inc\u0000foobar").isEmpty()); 82 } 83 } 84 85 private static MemorySegment loadLibrary(Arena session) { 86 SymbolLookup lib = SymbolLookup.libraryLookup(LIB_PATH, session); 87 MemorySegment addr = lib.find("inc").get(); 88 assertEquals(addr.scope(), session.scope()); 89 return addr; 90 } 91 92 private static void callFunc(MemorySegment addr) { 93 try { 94 INC.invokeExact(addr); 95 } catch (IllegalStateException ex) { 96 throw ex; 97 } catch (Throwable ex) { 98 throw new AssertionError(ex); 99 } 100 } 101 102 static final int ITERATIONS = 100; 103 static final int MAX_EXECUTOR_WAIT_SECONDS = 20; 104 static final int NUM_ACCESSORS = Math.min(10, Runtime.getRuntime().availableProcessors()); 105 106 @Test(expectedExceptions = IllegalArgumentException.class) 107 void testBadLibraryLookupName() { 108 SymbolLookup.libraryLookup("nonExistent", Arena.global()); 109 } 110 111 @Test(expectedExceptions = IllegalArgumentException.class) 112 void testBadLibraryLookupPath() { 113 SymbolLookup.libraryLookup(Path.of("nonExistent"), Arena.global()); 114 } 115 116 @Test 117 void testLoadLibraryShared() throws Throwable { 118 ExecutorService accessExecutor = Executors.newCachedThreadPool(); 119 for (int i = 0; i < NUM_ACCESSORS ; i++) { 120 accessExecutor.execute(new LibraryLoadAndAccess()); 121 } 122 accessExecutor.shutdown(); 123 assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS)); 124 } 125 126 static class LibraryLoadAndAccess implements Runnable { 127 @Override 128 public void run() { 129 for (int i = 0 ; i < ITERATIONS ; i++) { 130 try (Arena arena = Arena.ofConfined()) { 131 callFunc(loadLibrary(arena)); 132 } 133 } 134 } 135 } 136 137 @Test 138 void testLoadLibrarySharedClosed() throws Throwable { 139 Arena arena = Arena.ofShared(); 140 MemorySegment addr = loadLibrary(arena); 141 ExecutorService accessExecutor = Executors.newCachedThreadPool(); 142 for (int i = 0; i < NUM_ACCESSORS ; i++) { 143 accessExecutor.execute(new LibraryAccess(addr)); 144 } 145 while (true) { 146 try { 147 arena.close(); 148 break; 149 } catch (IllegalStateException ex) { 150 // wait for addressable parameter to be released 151 Thread.onSpinWait(); 152 } 153 } 154 accessExecutor.shutdown(); 155 assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS)); 156 } 157 158 static class LibraryAccess implements Runnable { 159 160 final MemorySegment addr; 161 162 LibraryAccess(MemorySegment addr) { 163 this.addr = addr; 164 } 165 166 @Override 167 public void run() { 168 for (int i = 0 ; i < ITERATIONS ; i++) { 169 try { 170 callFunc(addr); 171 } catch (IllegalStateException ex) { 172 // library closed 173 break; 174 } 175 } 176 } 177 } 178 }