2022-05-12 16:17:45 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
|
|
*
|
|
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
|
|
* accompanied this code).
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License version
|
|
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
|
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
|
|
* questions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import org.testng.annotations.Test;
|
|
|
|
|
2022-12-05 13:49:53 +00:00
|
|
|
import java.lang.foreign.Arena;
|
|
|
|
import java.lang.foreign.SegmentScope;
|
2022-05-12 16:17:45 +00:00
|
|
|
import java.lang.foreign.Linker;
|
|
|
|
import java.lang.foreign.FunctionDescriptor;
|
|
|
|
import java.lang.foreign.MemorySegment;
|
|
|
|
import java.lang.foreign.SymbolLookup;
|
|
|
|
import java.lang.invoke.MethodHandle;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
import static org.testng.Assert.*;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @test
|
|
|
|
* @enablePreview
|
2023-01-19 01:33:35 +00:00
|
|
|
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
2022-05-12 16:17:45 +00:00
|
|
|
* @run testng/othervm --enable-native-access=ALL-UNNAMED LibraryLookupTest
|
|
|
|
*/
|
|
|
|
public class LibraryLookupTest {
|
|
|
|
|
|
|
|
static final Path JAVA_LIBRARY_PATH = Path.of(System.getProperty("java.library.path"));
|
|
|
|
static final MethodHandle INC = Linker.nativeLinker().downcallHandle(FunctionDescriptor.ofVoid());
|
|
|
|
static final Path LIB_PATH = JAVA_LIBRARY_PATH.resolve(System.mapLibraryName("LibraryLookup"));
|
|
|
|
|
|
|
|
@Test
|
|
|
|
void testLoadLibraryConfined() {
|
2022-12-05 13:49:53 +00:00
|
|
|
try (Arena arena0 = Arena.openConfined()) {
|
|
|
|
callFunc(loadLibrary(arena0.scope()));
|
|
|
|
try (Arena arena1 = Arena.openConfined()) {
|
|
|
|
callFunc(loadLibrary(arena1.scope()));
|
|
|
|
try (Arena arena2 = Arena.openConfined()) {
|
|
|
|
callFunc(loadLibrary(arena2.scope()));
|
2022-05-12 16:17:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expectedExceptions = IllegalStateException.class)
|
|
|
|
void testLoadLibraryConfinedClosed() {
|
2022-12-05 13:49:53 +00:00
|
|
|
MemorySegment addr;
|
|
|
|
try (Arena arena = Arena.openConfined()) {
|
|
|
|
addr = loadLibrary(arena.scope());
|
2022-05-12 16:17:45 +00:00
|
|
|
}
|
|
|
|
callFunc(addr);
|
|
|
|
}
|
|
|
|
|
2022-12-05 13:49:53 +00:00
|
|
|
private static MemorySegment loadLibrary(SegmentScope session) {
|
2022-05-12 16:17:45 +00:00
|
|
|
SymbolLookup lib = SymbolLookup.libraryLookup(LIB_PATH, session);
|
2022-12-05 13:49:53 +00:00
|
|
|
MemorySegment addr = lib.find("inc").get();
|
|
|
|
assertEquals(addr.scope(), session);
|
2022-05-12 16:17:45 +00:00
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2022-12-05 13:49:53 +00:00
|
|
|
private static void callFunc(MemorySegment addr) {
|
2022-05-12 16:17:45 +00:00
|
|
|
try {
|
|
|
|
INC.invokeExact(addr);
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
throw ex;
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
throw new AssertionError(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final int ITERATIONS = 100;
|
|
|
|
static final int MAX_EXECUTOR_WAIT_SECONDS = 20;
|
|
|
|
static final int NUM_ACCESSORS = Math.min(10, Runtime.getRuntime().availableProcessors());
|
|
|
|
|
|
|
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
|
|
|
void testBadLibraryLookupName() {
|
2022-12-05 13:49:53 +00:00
|
|
|
SymbolLookup.libraryLookup("nonExistent", SegmentScope.global());
|
2022-05-12 16:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
|
|
|
void testBadLibraryLookupPath() {
|
2022-12-05 13:49:53 +00:00
|
|
|
SymbolLookup.libraryLookup(Path.of("nonExistent"), SegmentScope.global());
|
2022-05-12 16:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
void testLoadLibraryShared() throws Throwable {
|
|
|
|
ExecutorService accessExecutor = Executors.newCachedThreadPool();
|
|
|
|
for (int i = 0; i < NUM_ACCESSORS ; i++) {
|
|
|
|
accessExecutor.execute(new LibraryLoadAndAccess());
|
|
|
|
}
|
|
|
|
accessExecutor.shutdown();
|
|
|
|
assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static class LibraryLoadAndAccess implements Runnable {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
for (int i = 0 ; i < ITERATIONS ; i++) {
|
2022-12-05 13:49:53 +00:00
|
|
|
try (Arena arena = Arena.openConfined()) {
|
|
|
|
callFunc(loadLibrary(arena.scope()));
|
2022-05-12 16:17:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
void testLoadLibrarySharedClosed() throws Throwable {
|
2022-12-05 13:49:53 +00:00
|
|
|
Arena arena = Arena.openShared();
|
|
|
|
MemorySegment addr = loadLibrary(arena.scope());
|
2022-05-12 16:17:45 +00:00
|
|
|
ExecutorService accessExecutor = Executors.newCachedThreadPool();
|
|
|
|
for (int i = 0; i < NUM_ACCESSORS ; i++) {
|
|
|
|
accessExecutor.execute(new LibraryAccess(addr));
|
|
|
|
}
|
|
|
|
while (true) {
|
|
|
|
try {
|
2022-12-05 13:49:53 +00:00
|
|
|
arena.close();
|
2022-05-12 16:17:45 +00:00
|
|
|
break;
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
// wait for addressable parameter to be released
|
|
|
|
Thread.onSpinWait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
accessExecutor.shutdown();
|
|
|
|
assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static class LibraryAccess implements Runnable {
|
|
|
|
|
2022-12-05 13:49:53 +00:00
|
|
|
final MemorySegment addr;
|
2022-05-12 16:17:45 +00:00
|
|
|
|
2022-12-05 13:49:53 +00:00
|
|
|
LibraryAccess(MemorySegment addr) {
|
2022-05-12 16:17:45 +00:00
|
|
|
this.addr = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
for (int i = 0 ; i < ITERATIONS ; i++) {
|
|
|
|
try {
|
|
|
|
callFunc(addr);
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
// library closed
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|