Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0d429af
feat: add real-time helper functions for windows
srvald Jul 1, 2026
551a81e
docs: add Windows soft real-time configuration guide and helper funct…
srvald Jul 1, 2026
54f25d0
docs: helper functions example
srvald Jul 1, 2026
bcf53e1
test: add unit tests for scheduling and affinity helpers.
srvald Jul 1, 2026
0eb997d
docs: show scheduling helper usage in RTDE example
srvald Jul 1, 2026
7bbf842
fix: adjust wrong argument and file format
srvald Jul 1, 2026
ec350f4
fix: adjust format and wrong test expected return
srvald Jul 1, 2026
85f2896
fix: example back to normal adding the scheduling helper functions
srvald Jul 1, 2026
64e0774
fix: show UAC warning only when REALTIME priority.
srvald Jul 1, 2026
4fc6d35
fix: remove GNU SOURCE
srvald Jul 2, 2026
012ae04
fix: avoid cpu_set_t on apple
srvald Jul 2, 2026
161f4de
fix: return false if cannot get affinity thread ubuntu
srvald Jul 2, 2026
d246b08
fix: add elif linux to avoid function to be used with Apple
srvald Jul 2, 2026
0666c9f
fix: apply old process priority if thread priority fails at FiFo sche…
srvald Jul 2, 2026
633483d
tests: compatible cpu affinity mask for github actions
srvald Jul 2, 2026
e91a534
tests: remove FIFO scheduling tests
srvald Jul 2, 2026
7a48877
test: correct CPUs at thread affinity mask for ubuntu and some tests …
srvald Jul 2, 2026
6c3f64c
fix: fix format
srvald Jul 2, 2026
e62bd2c
tests: resolve unused variable using Apple
srvald Jul 2, 2026
d09ed57
fix: adjust code format
srvald Jul 3, 2026
c3a202a
example: compatible with Apple
srvald Jul 3, 2026
6fe9f2c
docs: Remove whitespace
srvald Jul 3, 2026
37599d1
fix: adjust format and thread missing with Apple
srvald Jul 3, 2026
b56bdb0
refactor: if failure, set previos priority back
srvald Jul 3, 2026
41f371c
fix: adjust format
srvald Jul 3, 2026
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
34 changes: 34 additions & 0 deletions doc/examples/rtde_client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,40 @@ to initialize the RTDE client.
:start-at: const std::string OUTPUT_RECIPE
:end-at: const std::string INPUT_RECIPE

Configuring real-time scheduling
--------------------------------

To reduce scheduling jitter, the example configures CPU affinity and scheduling priorities before
starting RTDE communication.

The Universal Robots Client Library provides helper functions that simplify configuring these
settings on both Linux and Windows.

.. literalinclude:: ../../examples/rtde_client.cpp
:language: c++
:caption: examples/rtde_client.cpp
:linenos:
:lineno-match:
:start-at: pprocess_t process = pprocess_self();
:end-at: URCL_LOG_ERROR("Failed to set FIFO scheduling");

The example assigns dedicated CPU cores and configures the highest available scheduling priority
for the current thread.

On Linux, ``setFiFoScheduling()`` configures ``SCHED_FIFO`` scheduling.

On Windows, ``setFiFoScheduling()`` configures the process to use
``REALTIME_PRIORITY_CLASS`` and the thread to use
``THREAD_PRIORITY_TIME_CRITICAL``.

For a detailed explanation of the available helper functions and platform-specific real-time
configuration recommendations, see :ref:`real time setup`.

.. note::

The selected CPU indices are only examples. Applications should choose CPU affinities according
to the available hardware and system configuration.

Creating an RTDE Client
-----------------------

Expand Down
165 changes: 165 additions & 0 deletions doc/real_time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,168 @@ the ``cpufrequtils`` service. Check with ``cpufreq-info``.

For further information about governors, please see the `kernel
documentation <https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt>`_.

Windows soft real-time configuration
------------------------------------

Unlike Linux systems running a PREEMPT_RT kernel with ``SCHED_FIFO`` scheduling, Windows does not
provide the same level of scheduling behaviour. However, it can be configured for
soft real-time, where scheduling jitter is reduced and timing predictability is improved.

According to Microsoft, hard real-time systems require deterministic execution at precise points in
time, whereas soft real-time systems tolerate a small amount of scheduling jitter and execution
latency. The real-time capabilities available on Windows fall into the latter category.

For additional details, refer to Microsoft's documentation:
`Soft Real-Time <https://learn.microsoft.com/en-us/windows/iot/iot-enterprise/soft-real-time/soft-real-time>`_.

Preparing the system
^^^^^^^^^^^^^^^^^^^^

Microsoft provides guidance for configuring Windows 10 and Windows 11 systems for
soft real-time performance. The recommended device configuration includes techniques such as:

* CPU isolation
* Routing interrupts away from real-time cores
* Disabling selected background services
* Disabling CPU idle states
* Configuring dedicated real-time CPU cores

For detailed device configuration instructions, see `Device Configuration <https://learn.microsoft.com/en-us/windows/iot/iot-enterprise/soft-real-time/soft-real-time-device>`_.

Process and thread configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

After the system has been configured, applications can improve scheduling behaviour by assigning
high priorities and CPU affinities to the processes and threads involved during execution.

Microsoft recommends using:

* ``SetPriorityClass()`` with ``REALTIME_PRIORITY_CLASS``
* ``SetProcessAffinityMask()``
* ``SetThreadPriority()`` with ``THREAD_PRIORITY_TIME_CRITICAL``
* ``SetThreadAffinityMask()``

For detailed information about configuring real-time processes and threads on Windows,
see `Application Development <https://learn.microsoft.com/en-us/windows/iot/iot-enterprise/soft-real-time/soft-real-time-application>`_.

Scheduling priorities
^^^^^^^^^^^^^^^^^^^^^

Windows schedules threads using a combination of process priority class and thread priority level.

The highest priority range is obtained by combining ``REALTIME_PRIORITY_CLASS`` and ``THREAD_PRIORITY_TIME_CRITICAL``
which results in a base thread priority of 31, the highest priority available.

For more information about Windows scheduling priorities, see `Scheduling Priorities <https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities>`_.

.. warning::

Microsoft recommends using ``REALTIME_PRIORITY_CLASS`` with care. Threads running at high
priorities for extended periods can negatively affect other system services and user interaction.

Using the helper functions
^^^^^^^^^^^^^^^^^^^^^^^^^^

The Universal Robots Client Library provides helper functions for configuring CPU affinity and
scheduling priorities.

Current process and thread handles can be obtained using:

.. code-block:: cpp

pprocess_t process = pprocess_self();
pthread_t thread = pthread_self();

Linux
~~~~~

On Linux, the helper functions can be used to assign CPU affinity and configure
``SCHED_FIFO`` scheduling for a thread.

.. code-block:: cpp

pthread_t thread = pthread_self();

cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(7, &cpuset);

setThreadAffinity(thread, cpuset);

int max_prio = sched_get_priority_max(SCHED_FIFO);
setFiFoScheduling(thread, max_prio);

The configured CPU affinity applies to the selected thread. The scheduling
priority is configured using ``SCHED_FIFO``.

Windows
~~~~~~~

On Windows, additional helper functions are available for configuring process
priority, process affinity, thread priority and thread affinity.

.. code-block:: cpp

pprocess_t process = pprocess_self();
pthread_t thread = pthread_self();

DWORD_PTR process_mask = (1ULL << 6) | (1ULL << 7);
setProcessAffinity(process, process_mask);

DWORD_PTR thread_mask = (1ULL << 7);
setThreadAffinity(thread, thread_mask);

int max_prio = sched_get_priority_max(SCHED_FIFO);
setFiFoScheduling(thread, max_prio);

// Equivalent explicit calls
setProcessPriority(process, REALTIME_PRIORITY_CLASS);
setThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);

On Windows, ``setFiFoScheduling()`` configures the process to use
``REALTIME_PRIORITY_CLASS`` and applies the requested thread priority.

Available helper functions
~~~~~~~~~~~~~~~~~~~~~~~~~~

Windows:

.. code-block:: cpp

bool setProcessPriority(pprocess_t& process, DWORD priority);
bool setProcessAffinity(pprocess_t& process, DWORD_PTR cpu_mask);
bool setThreadPriority(pthread_t& thread, int priority);
bool setThreadAffinity(pthread_t& thread, DWORD_PTR cpu_mask);
bool setFiFoScheduling(pthread_t& thread, int priority);

Linux:

.. code-block:: cpp

bool setThreadAffinity(pthread_t& thread, const cpu_set_t& cpuset);
bool setFiFoScheduling(pthread_t& thread, int priority);

Administrative privileges
^^^^^^^^^^^^^^^^^^^^^^^^^

Using ``REALTIME_PRIORITY_CLASS`` may require elevated privileges depending on the system
configuration.

When using the real-time helper functions, it is therefore recommended to run the application with
administrative privileges.

The helper functions verify whether the process is running with elevated privileges and will issue a
warning if this is not the case.

If sufficient privileges are not available, Windows may reject requests to enter the real-time
priority class.

Example usage
^^^^^^^^^^^^^

A complete example demonstrating how to configure process priority, thread priority and CPU affinity
using the helper functions is provided in the RTDE Client example (:ref:`rtde_client_example`).

Refer to this example for recommended usage patterns and platform-specific recommendations.

39 changes: 38 additions & 1 deletion examples/rtde_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,43 @@ void printFraction(const double fraction, const std::string& label, const size_t

int main(int argc, char* argv[])
{
pthread_t thread = pthread_self();
#ifdef _WIN32
pprocess_t process = pprocess_self();

// Assign logical CPUs 6 and 7 to this process
DWORD_PTR process_mask = (1ULL << 6) | (1ULL << 7);
if (!setProcessAffinity(process, process_mask))
{
URCL_LOG_ERROR("Failed to set process affinity");
}

// Assign logical CPU 7 to this thread
DWORD_PTR thread_mask = (1ULL << 7);
if (!setThreadAffinity(thread, thread_mask))
{
URCL_LOG_ERROR("Failed to set thread affinity");
}
#elif __linux__
cpu_set_t cpuset;
CPU_ZERO(&cpuset);

// Assign logical CPU 7 to this thread
CPU_SET(7, &cpuset);

if (!setThreadAffinity(thread, cpuset))
{
URCL_LOG_ERROR("Failed to set thread affinity");
}
#endif

// Set thread and process to maximum priority
const int max_prio = sched_get_priority_max(SCHED_FIFO);
if (!setFiFoScheduling(thread, max_prio))
{
URCL_LOG_ERROR("Failed to set FIFO scheduling");
}

// Parse the ip arguments if given
std::string robot_ip = DEFAULT_ROBOT_IP;
if (argc > 1)
Expand Down Expand Up @@ -143,4 +180,4 @@ int main(int argc, char* argv[])
URCL_LOG_INFO("Exiting RTDE read/write example.");

return 0;
}
}
89 changes: 88 additions & 1 deletion include/ur_client_library/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,18 @@
# define SCHED_FIFO (1)

typedef HANDLE pthread_t;
typedef HANDLE pprocess_t;
Comment on lines 50 to +51

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion use a typedef such as urcl::ThreadT and urcl::ProcessT for both, windows and non-windows. Then, the function signatures can use this typedef-ed type and share their declarations.

Also, consider using void* on Windows and avoid including windows.h


static inline pthread_t pthread_self()
{
return ::GetCurrentThread();
}

static inline pprocess_t pprocess_self()
{
return ::GetCurrentProcess();
}

static inline int sched_get_priority_max(int policy)
{
(void)policy;
Expand Down Expand Up @@ -96,7 +102,88 @@ static inline int sched_get_priority_max(int policy)

namespace urcl
{
bool setFiFoScheduling(pthread_t& thread, const int priority);

#ifdef _WIN32

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if we had only one function definition and do the necessary platform-specific calls inside them.


/*!
* \brief Set the priority class of a process.
*
* Wraps the corresponding operating system API and verifies that the requested
* priority class has been applied successfully.
*
* \param process Process handle.
* \param priority Windows process priority class.
*
* \returns True if the priority class was applied successfully.
*/
bool setProcessPriority(pprocess_t& process, DWORD priority);

/*!
* \brief Restrict a process to a set of CPUs.
*
* Wraps the corresponding operating system API and verifies that the requested
* CPU affinity mask has been applied successfully.
*
* \param process Process handle.
* \param cpu_mask Bit mask describing the CPUs available to the process.
*
* \returns True if the affinity mask was applied successfully.
*/
bool setProcessAffinity(pprocess_t& process, DWORD_PTR cpu_mask);

/*!
* \brief Restrict a thread to a set of CPUs.
*
* \param thread Thread handle.
* \param cpu_mask Bit mask describing the CPUs available to the thread.
*
* \returns True if the affinity mask was applied successfully.
*/
bool setThreadAffinity(pthread_t& thread, DWORD_PTR cpu_mask);

/*!
* \brief Set the scheduling priority of a thread.
*
* Wraps the corresponding operating system API and verifies that the requested
* priority has been applied successfully.
*
* \param thread Thread handle.
* \param priority Windows thread priority value.
*
* \returns True if the priority was applied successfully.
*/
bool setThreadPriority(pthread_t& thread, const int priority);

#elif __linux__

/*!
* \brief Restrict a thread to a set of CPUs.
*
* \param thread Thread handle.
* \param cpuset Set of CPUs available to the thread.
*
* \returns True if the CPU affinity was applied successfully.
*/
bool setThreadAffinity(pthread_t& thread, const cpu_set_t& cpuset);

#endif

/*!
* \brief Configure high-priority scheduling for a thread.
*
* On Linux, this configures ``SCHED_FIFO`` scheduling using the supplied
* priority.
*
* On Windows, this configures the process to use
* ``REALTIME_PRIORITY_CLASS`` and sets the thread to the supplied Windows
* thread priority.
*
* \param thread Thread handle.
* \param priority Scheduling priority to apply.
*
* \returns True if the scheduling configuration was applied successfully.
*/
bool setFiFoScheduling(pthread_t& thread, int priority);

/*!
* \brief Wait for a condition to be true.
Expand Down
Loading
Loading