diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt index 03c2696efa..c29f770c9a 100644 --- a/hal/CMakeLists.txt +++ b/hal/CMakeLists.txt @@ -25,6 +25,7 @@ string(REPLACE ";" "\n" usage_reporting_types_cpp "${usage_reporting_types_cpp}" string(REPLACE ";" "\n" usage_reporting_instances_cpp "${usage_reporting_instances_cpp}") file(GLOB + hal_shared_native_src src/main/native/cpp/*.cpp hal_shared_native_src src/main/native/cpp/cpp/*.cpp hal_shared_native_src src/main/native/cpp/handles/*.cpp hal_sim_native_src src/main/native/sim/*.cpp diff --git a/hal/src/main/java/edu/wpi/first/hal/HAL.java b/hal/src/main/java/edu/wpi/first/hal/HAL.java index a298ff65f6..8f09a0a863 100644 --- a/hal/src/main/java/edu/wpi/first/hal/HAL.java +++ b/hal/src/main/java/edu/wpi/first/hal/HAL.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -18,6 +18,12 @@ public final class HAL extends JNIWrapper { public static native boolean initialize(int timeout, int mode); + public static native boolean hasMain(); + + public static native void runMain(); + + public static native void exitMain(); + public static native void observeUserProgramStarting(); public static native void observeUserProgramDisabled(); diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index 0b1e5354f7..a80a96ce29 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -61,6 +61,7 @@ void InitializeHAL() { InitializeFRCDriverStation(); InitializeI2C(); InitialzeInterrupts(); + InitializeMain(); InitializeNotifier(); InitializePCMInternal(); InitializePDP(); diff --git a/hal/src/main/native/athena/HALInitializer.h b/hal/src/main/native/athena/HALInitializer.h index 384fe583d6..fc38038669 100644 --- a/hal/src/main/native/athena/HALInitializer.h +++ b/hal/src/main/native/athena/HALInitializer.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -38,6 +38,7 @@ extern void InitializeFRCDriverStation(); extern void InitializeHAL(); extern void InitializeI2C(); extern void InitialzeInterrupts(); +extern void InitializeMain(); extern void InitializeNotifier(); extern void InitializePCMInternal(); extern void InitializePDP(); diff --git a/hal/src/main/native/cpp/Main.cpp b/hal/src/main/native/cpp/Main.cpp new file mode 100644 index 0000000000..a37c2b098c --- /dev/null +++ b/hal/src/main/native/cpp/Main.cpp @@ -0,0 +1,64 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "hal/Main.h" + +#include +#include + +static void DefaultMain(void*); +static void DefaultExit(void*); + +static bool gHasMain = false; +static void* gMainParam = nullptr; +static void (*gMainFunc)(void*) = DefaultMain; +static void (*gExitFunc)(void*) = DefaultExit; +static bool gExited = false; +struct MainObj { + wpi::mutex gExitMutex; + wpi::condition_variable gExitCv; +}; + +static MainObj* mainObj; + +static void DefaultMain(void*) { + std::unique_lock lock{mainObj->gExitMutex}; + mainObj->gExitCv.wait(lock, [] { return gExited; }); +} + +static void DefaultExit(void*) { + std::lock_guard lock{mainObj->gExitMutex}; + gExited = true; + mainObj->gExitCv.notify_all(); +} + +namespace hal { +namespace init { +void InitializeMain() { + static MainObj mO; + mainObj = &mO; +} +} // namespace init +} // namespace hal + +extern "C" { + +void HAL_SetMain(void* param, void (*mainFunc)(void*), + void (*exitFunc)(void*)) { + gHasMain = true; + gMainParam = param; + gMainFunc = mainFunc; + gExitFunc = exitFunc; +} + +HAL_Bool HAL_HasMain(void) { return gHasMain; } + +void HAL_RunMain(void) { gMainFunc(gMainParam); } + +void HAL_ExitMain(void) { gExitFunc(gMainParam); } + +} // extern "C" diff --git a/hal/src/main/native/cpp/jni/HAL.cpp b/hal/src/main/native/cpp/jni/HAL.cpp index 867f655bd9..393b0b4d17 100644 --- a/hal/src/main/native/cpp/jni/HAL.cpp +++ b/hal/src/main/native/cpp/jni/HAL.cpp @@ -17,6 +17,7 @@ #include "HALUtil.h" #include "edu_wpi_first_hal_HAL.h" #include "hal/DriverStation.h" +#include "hal/Main.h" using namespace frc; using namespace wpi::java; @@ -35,6 +36,42 @@ Java_edu_wpi_first_hal_HAL_initialize return HAL_Initialize(timeout, mode); } +/* + * Class: edu_wpi_first_hal_HAL + * Method: hasMain + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_edu_wpi_first_hal_HAL_hasMain + (JNIEnv*, jclass) +{ + return HAL_HasMain(); +} + +/* + * Class: edu_wpi_first_hal_HAL + * Method: runMain + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_HAL_runMain + (JNIEnv*, jclass) +{ + HAL_RunMain(); +} + +/* + * Class: edu_wpi_first_hal_HAL + * Method: exitMain + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_HAL_exitMain + (JNIEnv*, jclass) +{ + HAL_ExitMain(); +} + /* * Class: edu_wpi_first_hal_HAL * Method: observeUserProgramStarting diff --git a/hal/src/main/native/include/hal/HAL.h b/hal/src/main/native/include/hal/HAL.h index 2cb358dadf..992a2c28d8 100644 --- a/hal/src/main/native/include/hal/HAL.h +++ b/hal/src/main/native/include/hal/HAL.h @@ -27,6 +27,7 @@ #include "hal/HALBase.h" #include "hal/I2C.h" #include "hal/Interrupts.h" +#include "hal/Main.h" #include "hal/Notifier.h" #include "hal/PDP.h" #include "hal/PWM.h" diff --git a/hal/src/main/native/include/hal/Main.h b/hal/src/main/native/include/hal/Main.h new file mode 100644 index 0000000000..866e274d42 --- /dev/null +++ b/hal/src/main/native/include/hal/Main.h @@ -0,0 +1,67 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "hal/Types.h" + +/** + * @defgroup hal_relay Main loop functions + * @ingroup hal_capi + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Sets up the system to run the provided main loop in the main thread (e.g. + * the thread in which main() starts execution) and run the robot code in a + * separate thread. + * + * Normally the robot code runs in the main thread, but some GUI systems + * require the GUI be run in the main thread. + * + * To be effective, this function must be called before the robot code starts + * the main loop (e.g. by frc::StartRobot()). + * + * @param param parameter data to pass to mainFunc and exitFunc + * @param mainFunc the function to be run when HAL_RunMain() is called. + * @param exitFunc the function to be run when HAL_ExitMain() is called. + */ +void HAL_SetMain(void* param, void (*mainFunc)(void*), void (*exitFunc)(void*)); + +/** + * Returns true if HAL_SetMain() has been called. + * + * @return True if HAL_SetMain() has been called, false otherwise. + */ +HAL_Bool HAL_HasMain(void); + +/** + * Runs the main function provided to HAL_SetMain(). + * + * If HAL_SetMain() has not been called, simply sleeps until HAL_ExitMain() + * is called. + */ +void HAL_RunMain(void); + +/** + * Causes HAL_RunMain() to exit. + * + * If HAL_SetMain() has been called, this calls the exit function provided + * to that function. + */ +void HAL_ExitMain(void); + +#ifdef __cplusplus +} // extern "C" +#endif +/** @} */ diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index b82560e2cd..191cfdcbb0 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -60,6 +60,7 @@ void InitializeHAL() { InitializeExtensions(); InitializeI2C(); InitializeInterrupts(); + InitializeMain(); InitializeMockHooks(); InitializeNotifier(); InitializePDP(); diff --git a/hal/src/main/native/sim/HALInitializer.h b/hal/src/main/native/sim/HALInitializer.h index d369a3be31..c765042f3f 100644 --- a/hal/src/main/native/sim/HALInitializer.h +++ b/hal/src/main/native/sim/HALInitializer.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -55,6 +55,7 @@ extern void InitializeExtensions(); extern void InitializeHAL(); extern void InitializeI2C(); extern void InitializeInterrupts(); +extern void InitializeMain(); extern void InitializeMockHooks(); extern void InitializeNotifier(); extern void InitializePDP(); diff --git a/wpilibc/src/main/native/include/frc/RobotBase.h b/wpilibc/src/main/native/include/frc/RobotBase.h index 185f00e958..85a9d12eea 100644 --- a/wpilibc/src/main/native/include/frc/RobotBase.h +++ b/wpilibc/src/main/native/include/frc/RobotBase.h @@ -9,6 +9,7 @@ #include +#include #include #include "frc/Base.h" @@ -19,14 +20,37 @@ class DriverStation; int RunHALInitialization(); +namespace impl { + +template +void RunRobot() { + static Robot robot; + robot.StartCompetition(); +} + +} // namespace impl + template int StartRobot() { int halInit = RunHALInitialization(); if (halInit != 0) { return halInit; } - static Robot robot; - robot.StartCompetition(); + if (HAL_HasMain()) { + std::thread([] { + try { + impl::RunRobot(); + } catch (...) { + HAL_ExitMain(); + throw; + } + HAL_ExitMain(); + }) + .detach(); + HAL_RunMain(); + } else { + impl::RunRobot(); + } return 0; } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java index 48de26fc2d..c541f9a578 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotBase.java @@ -204,21 +204,11 @@ public abstract class RobotBase implements AutoCloseable { } /** - * Starting point for the applications. + * Run the robot main loop. */ @SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidCatchingThrowable", "PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) - public static void startRobot(Supplier robotSupplier) { - if (!HAL.initialize(500, 0)) { - throw new IllegalStateException("Failed to initialize. Terminating"); - } - - // Call a CameraServer JNI function to force OpenCV native library loading - // Needed because all the OpenCV JNI functions don't have built in loading - CameraServerJNI.enumerateSinks(); - - HAL.report(tResourceType.kResourceType_Language, tInstances.kLanguage_Java); - + private static void runRobot(Supplier robotSupplier) { System.out.println("********** Robot program starting **********"); T robot; @@ -238,7 +228,6 @@ public abstract class RobotBase implements AutoCloseable { + throwable.toString(), elements); DriverStation.reportWarning("Robots should not quit, but yours did!", false); DriverStation.reportError("Could not instantiate robot " + robotName + "!", false); - System.exit(1); return; } @@ -285,6 +274,34 @@ public abstract class RobotBase implements AutoCloseable { DriverStation.reportError("Unexpected return from startCompetition() method.", false); } } + } + + /** + * Starting point for the applications. + */ + public static void startRobot(Supplier robotSupplier) { + if (!HAL.initialize(500, 0)) { + throw new IllegalStateException("Failed to initialize. Terminating"); + } + + // Call a CameraServer JNI function to force OpenCV native library loading + // Needed because all the OpenCV JNI functions don't have built in loading + CameraServerJNI.enumerateSinks(); + + HAL.report(tResourceType.kResourceType_Language, tInstances.kLanguage_Java); + + if (HAL.hasMain()) { + Thread thread = new Thread(() -> { + runRobot(robotSupplier); + HAL.exitMain(); + }, "robot main"); + thread.setDaemon(true); + thread.start(); + HAL.runMain(); + } else { + runRobot(robotSupplier); + } + System.exit(1); } }