[hal] Refactor threads API (#8701)

Since sched_setscheduler() requires non-RT priorities to be 0, we can
use that as a sentinel value for disabling RT and condense the Java API
to just two functions with fewer parameters. The thread priority setter
is deprecated since only experts should use it.

The HAL Notifier thread priority setter was replaced with setting the
priority in the thread itself.

The C++ Notifier non-RT and RT constructors were deduplicated.

The real-time scheduler was changed from SCHED_FIFO to SCHED_RR, which
is SCHED_FIFO with threads allowed to run for a maximum time quantum
before yielding (100 ms by default).
This commit is contained in:
Tyler Veness
2026-04-06 08:49:43 -07:00
committed by GitHub
parent cc56c42d4c
commit 173ecd3d02
27 changed files with 188 additions and 388 deletions

View File

@@ -14,56 +14,20 @@
using namespace wpi;
Notifier::Notifier(std::function<void()> callback) {
if (!callback) {
throw WPILIB_MakeError(err::NullParameter, "callback");
}
m_callback = callback;
int32_t status = 0;
m_notifier = HAL_CreateNotifier(&status);
WPILIB_CheckErrorStatus(status, "CreateNotifier");
m_thread = std::thread([=, this] {
for (;;) {
int32_t status = 0;
HAL_NotifierHandle notifier = m_notifier.load();
if (notifier == 0) {
break;
}
if (WPI_WaitForObject(notifier) == 0) {
break;
}
std::function<void()> callback;
{
std::scoped_lock lock(m_processMutex);
callback = m_callback;
}
// Call callback
if (callback) {
callback();
}
// Ack notifier
HAL_AcknowledgeNotifierAlarm(notifier, &status);
WPILIB_CheckErrorStatus(status, "AcknowledgeNotifier");
}
});
}
Notifier::Notifier(int priority, std::function<void()> callback) {
if (!callback) {
throw WPILIB_MakeError(err::NullParameter, "callback");
}
m_callback = callback;
int32_t status = 0;
HAL_Status status = 0;
m_notifier = HAL_CreateNotifier(&status);
WPILIB_CheckErrorStatus(status, "InitializeNotifier");
m_thread = std::thread([=, this] {
int32_t status = 0;
HAL_SetCurrentThreadPriority(true, priority, &status);
if (priority > 0 && HAL_SetCurrentThreadPriority(priority) != 0) {
WPILIB_ReportWarning("Setting Notifier priority to {} failed\n",
priority);
}
for (;;) {
HAL_NotifierHandle notifier = m_notifier.load();
if (notifier == 0) {
@@ -99,6 +63,7 @@ Notifier::Notifier(int priority, std::function<void()> callback) {
}
// Ack notifier
HAL_Status status = 0;
HAL_AcknowledgeNotifierAlarm(notifier, &status);
WPILIB_CheckErrorStatus(status, "AcknowledgeNotifier");
}
@@ -170,8 +135,3 @@ int32_t Notifier::GetOverrun() const {
WPILIB_CheckErrorStatus(status, "GetNotifierOverrun");
return overrun;
}
bool Notifier::SetHALThreadPriority(bool realTime, int32_t priority) {
int32_t status = 0;
return HAL_SetNotifierThreadPriority(realTime, priority, &status);
}

View File

@@ -9,38 +9,32 @@
namespace wpi {
int GetThreadPriority(std::thread& thread, bool* isRealTime) {
int32_t status = 0;
HAL_Bool rt = false;
int GetThreadPriority(std::thread& thread) {
auto native = thread.native_handle();
auto ret = HAL_GetThreadPriority(&native, &rt, &status);
int32_t priority = 0;
HAL_Status status = HAL_GetThreadPriority(&native, &priority);
WPILIB_CheckErrorStatus(status, "GetThreadPriority");
*isRealTime = rt;
return ret;
return priority;
}
int GetCurrentThreadPriority(bool* isRealTime) {
int32_t status = 0;
HAL_Bool rt = false;
auto ret = HAL_GetCurrentThreadPriority(&rt, &status);
int GetCurrentThreadPriority() {
int32_t priority = 0;
HAL_Status status = HAL_GetCurrentThreadPriority(&priority);
WPILIB_CheckErrorStatus(status, "GetCurrentThreadPriority");
*isRealTime = rt;
return ret;
return priority;
}
bool SetThreadPriority(std::thread& thread, bool realTime, int priority) {
int32_t status = 0;
bool SetThreadPriority(std::thread& thread, int priority) {
auto native = thread.native_handle();
auto ret = HAL_SetThreadPriority(&native, realTime, priority, &status);
HAL_Status status = HAL_SetThreadPriority(&native, priority);
WPILIB_CheckErrorStatus(status, "SetThreadPriority");
return ret;
return status != 0;
}
bool SetCurrentThreadPriority(bool realTime, int priority) {
int32_t status = 0;
auto ret = HAL_SetCurrentThreadPriority(realTime, priority, &status);
bool SetCurrentThreadPriority(int priority) {
HAL_Status status = HAL_SetCurrentThreadPriority(priority);
WPILIB_CheckErrorStatus(status, "SetCurrentThreadPriority");
return ret;
return status != 0;
}
} // namespace wpi

View File

@@ -24,7 +24,6 @@
#include "wpi/nt/NetworkTableInstance.hpp"
#include "wpi/smartdashboard/SmartDashboard.hpp"
#include "wpi/system/Errors.hpp"
#include "wpi/system/Notifier.hpp"
#include "wpi/system/WPILibVersion.hpp"
#include "wpi/util/print.hpp"
#include "wpi/util/timestamp.hpp"
@@ -51,10 +50,6 @@ int wpi::RunHALInitialization() {
HAL_ReportUsage("Language", "C++");
HAL_ReportUsage("WPILibVersion", GetWPILibVersion());
if (!wpi::Notifier::SetHALThreadPriority(true, 40)) {
WPILIB_ReportWarning("Setting HAL Notifier RT priority to 40 failed\n");
}
std::puts("\n********** Robot program starting **********");
return 0;
}

View File

@@ -75,7 +75,8 @@ namespace wpi {
* If the robot periodic functions and the controller periodic functions have a
* lot of scheduling jitter that cause them to occasionally overlap with later
* timeslices, consider giving the main robot thread a real-time priority using
* wpi::SetCurrentThreadPriority(). An RT priority of 15 is a reasonable choice.
* wpi::SetCurrentThreadPriority(int). An RT priority of 15 is a reasonable
* choice.
*
* If you do enable RT though, <i>make sure your periodic functions do not
* block</i>. If they do, the operating system will lock up, and you'll have to

View File

@@ -36,7 +36,8 @@ class Notifier {
*
* @param callback The callback to run.
*/
explicit Notifier(std::function<void()> callback);
explicit Notifier(std::function<void()> callback)
: Notifier(0, std::move(callback)) {}
template <typename Arg, typename... Args>
Notifier(std::invocable<Arg, Args...> auto&& callback, Arg&& arg,
@@ -140,23 +141,6 @@ class Notifier {
*/
int32_t GetOverrun() const;
/**
* Sets the HAL notifier thread priority.
*
* The HAL notifier thread is responsible for managing the FPGA's notifier
* interrupt and waking up user's Notifiers when it's their time to run.
* Giving the HAL notifier thread real-time priority helps ensure the user's
* real-time Notifiers, if any, are notified to run in a timely manner.
*
* @param realTime Set to true to set a real-time priority, false for standard
* priority.
* @param priority Priority to set the thread to. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is forced to
* 0. See "man 7 sched" for more details.
* @return True on success.
*/
static bool SetHALThreadPriority(bool realTime, int32_t priority);
private:
// The thread waiting on the HAL alarm
std::thread m_thread;

View File

@@ -9,49 +9,59 @@
namespace wpi {
/**
* Get the thread priority for the specified thread.
* Gets the specified thread's priority.
*
* @param thread Reference to the thread to get the priority for.
* @param isRealTime Set to true if thread is real-time, otherwise false.
* @return The current thread priority. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is 0. See
* "man 7 sched" for details.
* Priorities range from 0 to 99 where 0 is non-real-time, 1-99 are real-time,
* and 99 is highest priority. See "man 7 sched" for details.
*
* @param thread The thread.
* @return The specified thread's priority.
*/
int GetThreadPriority(std::thread& thread, bool* isRealTime);
int GetThreadPriority(std::thread& thread);
/**
* Get the thread priority for the current thread.
* Gets the current thread's priority.
*
* @param isRealTime Set to true if thread is real-time, otherwise false.
* @return The current thread priority. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is 0. See
* "man 7 sched" for details.
* Priorities range from 0 to 99 where 0 is non-real-time, 1-99 are real-time,
* and 99 is highest priority. See "man 7 sched" for details.
*
* @return The current thread's priority.
*/
int GetCurrentThreadPriority(bool* isRealTime);
int GetCurrentThreadPriority();
/**
* Sets the thread priority for the specified thread.
* Sets the specified thread's priority.
*
* @param thread Reference to the thread to set the priority of.
* @param realTime Set to true to set a real-time priority, false for standard
* priority.
* @param priority Priority to set the thread to. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is forced to
* 0. See "man 7 sched" for more details.
* @return True on success.
* Priorities range from 0 to 99 where 0 is non-real-time, 1-99 are real-time,
* and 99 is highest priority. See "man 7 sched" for details.
*
* @param thread The thread.
* @param priority The priority.
* @return True on success.
* @deprecated Incorrect usage of real-time priority can lead to system lockups.
* Only use this function if you are trained in real-time software
* development.
*/
bool SetThreadPriority(std::thread& thread, bool realTime, int priority);
[[deprecated(
"Incorrect usage of real-time priority can lead to system lockups. Only "
"use this function if you are trained in real-time software development.")]]
bool SetThreadPriority(std::thread& thread, int priority);
/**
* Sets the thread priority for the current thread.
* Sets the current thread's priority.
*
* @param realTime Set to true to set a real-time priority, false for standard
* priority.
* @param priority Priority to set the thread to. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is forced to
* 0. See "man 7 sched" for more details.
* @return True on success.
* Priorities range from 0 to 99 where 0 is non-real-time, 1-99 are real-time,
* and 99 is highest priority. See "man 7 sched" for details.
*
* @param priority The priority.
* @return True on success.
* @deprecated Incorrect usage of real-time priority can lead to system lockups.
* Only use this function if you are trained in real-time software
* development.
*/
bool SetCurrentThreadPriority(bool realTime, int priority);
[[deprecated(
"Incorrect usage of real-time priority can lead to system lockups. Only "
"use this function if you are trained in real-time software development.")]]
bool SetCurrentThreadPriority(int priority);
} // namespace wpi

View File

@@ -19,4 +19,3 @@ classes:
wpi::units::second_t:
Stop:
GetOverrun:
SetHALThreadPriority:

View File

@@ -157,11 +157,6 @@ class RobotStarter:
def _start(self, robot_cls: wpilib.RobotBase) -> bool:
hal.reportUsage("Language", "Python")
if not wpilib.Notifier.setHALThreadPriority(True, 40):
reportErrorInternal(
"Setting HAL Notifier RT priority to 40 failed", isWarning=True
)
isSimulation = wpilib.RobotBase.isSimulation()
# hack: initialize networktables before creating the robot

View File

@@ -155,8 +155,3 @@ int32_t PyNotifier::GetOverrun() const {
WPILIB_CheckErrorStatus(status, "GetNotifierOverrun");
return overrun;
}
bool PyNotifier::SetHALThreadPriority(bool realTime, int32_t priority) {
int32_t status = 0;
return HAL_SetNotifierThreadPriority(realTime, priority, &status);
}

View File

@@ -101,23 +101,6 @@ class PyNotifier {
*/
int32_t GetOverrun() const;
/**
* Sets the HAL notifier thread priority.
*
* The HAL notifier thread is responsible for managing the FPGA's notifier
* interrupt and waking up user's Notifiers when it's their time to run.
* Giving the HAL notifier thread real-time priority helps ensure the user's
* real-time Notifiers, if any, are notified to run in a timely manner.
*
* @param realTime Set to true to set a real-time priority, false for standard
* priority.
* @param priority Priority to set the thread to. For real-time, this is 1-99
* with 99 being highest. For non-real-time, this is forced to
* 0. See "man 7 sched" for more details.
* @return True on success.
*/
static bool SetHALThreadPriority(bool realTime, int32_t priority);
private:
// The thread waiting on the HAL alarm
py::object m_thread;