Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default to non static library loading for JNI #6911

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class AprilTagJNI {
static boolean libraryLoaded = false;

/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
public static final class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(false);

/**
* Returns true if the JNI should be loaded in the static block.
Expand Down Expand Up @@ -55,6 +55,19 @@ private Helper() {}
}
}

/**
* Force load the library.
*
* @throws IOException if library load failed
*/
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) {
return;
}
RuntimeLoader.loadLibrary("apriltagjni");
libraryLoaded = true;
}

/**
* Constructs an AprilTag detector engine.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package edu.wpi.first.apriltag;

import edu.wpi.first.apriltag.jni.AprilTagJNI;
import edu.wpi.first.util.WPIUtilJNI;
import java.io.IOException;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

public final class AprilTagJniTestExtension implements BeforeAllCallback {
private static ExtensionContext getRoot(ExtensionContext context) {
return context.getParent().map(AprilTagJniTestExtension::getRoot).orElse(context);
}

@Override
public void beforeAll(ExtensionContext context) throws Exception {
getRoot(context)
.getStore(Namespace.GLOBAL)
.getOrComputeIfAbsent(
"April Tag Initialized",
key -> {
initializeNatives();
return true;
},
Boolean.class);
}

private void initializeNatives() {
try {
WPIUtilJNI.forceLoad();
AprilTagJNI.forceLoad();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
edu.wpi.first.apriltag.AprilTagJniTestExtension
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class CameraServerJNI {
static boolean libraryLoaded = false;

/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
public static final class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(false);

/**
* Returns true if the JNI should be loaded in the static block.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public final class OpenCvLoader {

/** Sets whether JNI should be loaded in the static block. */
public static final class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(false);

/**
* Returns true if the JNI should be loaded in the static block.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package edu.wpi.first.cscore;

import edu.wpi.first.util.WPIUtilJNI;

import java.io.IOException;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

public final class CameraServerJniTestExtension implements BeforeAllCallback {
private static ExtensionContext getRoot(ExtensionContext context) {
return context.getParent().map(CameraServerJniTestExtension::getRoot).orElse(context);
}

@Override
public void beforeAll(ExtensionContext context) throws Exception {
getRoot(context)
.getStore(Namespace.GLOBAL)
.getOrComputeIfAbsent(
"CsCore Initialized",
key -> {
initializeNatives();
return true;
},
Boolean.class);
}

private void initializeNatives() {
try {
WPIUtilJNI.forceLoad();
CameraServerJNI.forceLoad();
OpenCvLoader.forceLoad();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
edu.wpi.first.cscore.CameraServerJniTestExtension
4 changes: 2 additions & 2 deletions hal/src/main/java/edu/wpi/first/hal/JNIWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public class JNIWrapper {
static boolean libraryLoaded = false;

/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
public static final class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(false);

/**
* Returns true if the JNI should be loaded in the static block.
Expand Down
39 changes: 39 additions & 0 deletions hal/src/test/java/edu/wpi/first/hal/HalJniTestExtension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package edu.wpi.first.hal;

import edu.wpi.first.util.WPIUtilJNI;
import java.io.IOException;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

public final class HalJniTestExtension implements BeforeAllCallback {
private static ExtensionContext getRoot(ExtensionContext context) {
return context.getParent().map(HalJniTestExtension::getRoot).orElse(context);
}

@Override
public void beforeAll(ExtensionContext context) throws Exception {
getRoot(context)
.getStore(Namespace.GLOBAL)
.getOrComputeIfAbsent(
"HAL Initialized",
key -> {
initializeNatives();
return true;
},
Boolean.class);
}

private void initializeNatives() {
try {
WPIUtilJNI.forceLoad();
JNIWrapper.forceLoad();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
edu.wpi.first.hal.HalJniTestExtension
4 changes: 2 additions & 2 deletions ntcore/src/generate/main/java/NetworkTablesJNI.java.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public final class NetworkTablesJNI {
static boolean libraryLoaded = false;

/** Sets whether JNI should be loaded in the static block. */
public static class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
public static final class Helper {
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(false);

/**
* Returns true if the JNI should be loaded in the static block.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package edu.wpi.first.networktables;

import edu.wpi.first.util.WPIUtilJNI;

import java.io.IOException;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

public final class NetworkTablesJniTestExtension implements BeforeAllCallback {
private static ExtensionContext getRoot(ExtensionContext context) {
return context.getParent().map(NetworkTablesJniTestExtension::getRoot).orElse(context);
}

@Override
public void beforeAll(ExtensionContext context) throws Exception {
getRoot(context)
.getStore(Namespace.GLOBAL)
.getOrComputeIfAbsent(
"Ntcore Initialized",
key -> {
initializeNatives();
return true;
},
Boolean.class);
}

private void initializeNatives() {
try {
WPIUtilJNI.forceLoad();
NetworkTablesJNI.forceLoad();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
edu.wpi.first.networktables.NetworkTablesJniTestExtension
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

package edu.wpi.first.wpilibj2;

import edu.wpi.first.hal.HAL;
import edu.wpi.first.wpilibj.RobotBase;
import edu.wpi.first.wpilibj.simulation.DriverStationSim;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
Expand All @@ -29,7 +30,8 @@ public void beforeAll(ExtensionContext context) {
}

private void initializeHardware() {
HAL.initialize(500, 0);
RobotBase.loadLibrariesAndInitializeHal();

DriverStationSim.setDsAttached(true);
DriverStationSim.setAutonomous(false);
DriverStationSim.setEnabled(true);
Expand Down
2 changes: 2 additions & 0 deletions wpilibj/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ if(WITH_JAVA)
${OPENCV_JAR_FILE}
cscore_jar
cameraserver_jar
apriltag_jar
wpimath_jar
wpiunits_jar
wpinet_jar
wpiutil_jar
OUTPUT_NAME wpilibj
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
Expand Down
1 change: 1 addition & 0 deletions wpilibj/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {
implementation project(':ntcore')
implementation project(':cscore')
implementation project(':cameraserver')
implementation project(':apriltag')
testImplementation 'org.mockito:mockito-core:4.1.0'
devImplementation sourceSets.main.output
}
Expand Down
53 changes: 52 additions & 1 deletion wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@

package edu.wpi.first.wpilibj;

import edu.wpi.first.apriltag.jni.AprilTagJNI;
import edu.wpi.first.cameraserver.CameraServerShared;
import edu.wpi.first.cameraserver.CameraServerSharedStore;
import edu.wpi.first.cscore.CameraServerJNI;
import edu.wpi.first.cscore.OpenCvLoader;
import edu.wpi.first.hal.FRCNetComm.tInstances;
import edu.wpi.first.hal.FRCNetComm.tResourceType;
import edu.wpi.first.hal.HAL;
import edu.wpi.first.hal.HALUtil;
import edu.wpi.first.hal.JNIWrapper;
import edu.wpi.first.math.MathShared;
import edu.wpi.first.math.MathSharedStore;
import edu.wpi.first.math.MathUsageId;
import edu.wpi.first.math.jni.WPIMathJNI;
import edu.wpi.first.net.WPINetJNI;
import edu.wpi.first.networktables.MultiSubscriber;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.NetworkTablesJNI;
import edu.wpi.first.util.WPIUtilJNI;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
Expand Down Expand Up @@ -390,14 +397,58 @@ public static void suppressExitWarning(boolean value) {
m_runMutex.unlock();
}

/**
* Loads base native libraries needed by WPILib in all robot projects.
*
* @throws IOException If any library was not found
*/
public static void loadBaseNativeLibraries() throws IOException {
WPIUtilJNI.forceLoad();
WPINetJNI.forceLoad();
WPIMathJNI.forceLoad();
JNIWrapper.forceLoad();
NetworkTablesJNI.forceLoad();
}

/**
* Sets the static load behavior for all of the vision libraries
*
* @param loadOnStaticInitialization true to load on static initialization, false to disable (default)
*/
public static void setVisionNativeLibrariesStaticLoadBehavior(boolean loadOnStaticInitialization) {
CameraServerJNI.Helper.setExtractOnStaticLoad(true);
OpenCvLoader.Helper.setExtractOnStaticLoad(true);
AprilTagJNI.Helper.setExtractOnStaticLoad(true);
}

/**
* Configure all native libraries to match how a robot program works.
*
* <p>This will load all the base JNI libraries, set the vision libaries
* to load on static initialization, and initialize the HAL.
*
* @return HAL initialization result, true for success.
*/
public static boolean loadLibrariesAndInitializeHal() {
try {
loadBaseNativeLibraries();
} catch (IOException e) {
throw new RuntimeException(e);
}

setVisionNativeLibrariesStaticLoadBehavior(true);

return HAL.initialize(500, 0);
}

/**
* Starting point for the applications.
*
* @param <T> Robot subclass.
* @param robotSupplier Function that returns an instance of the robot subclass.
*/
public static <T extends RobotBase> void startRobot(Supplier<T> robotSupplier) {
if (!HAL.initialize(500, 0)) {
if (!loadLibrariesAndInitializeHal()) {
throw new IllegalStateException("Failed to initialize. Terminating");
}

Expand Down
Loading
Loading