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