I'm looking to create a realtime thread in Nim- Linux-only is fine for now.
In C, according to the LInux foundation, creating a realtime thread looks like this (shortened C-like pseudocode):
function thread_func(){
}
attr = attr_init();
setstacksize(attr, PTHREAD_STACK_MIN);
setschedpolicy(attr, SCHED_FIFO);
setschedparam(attr, priority=80);
setinheritsched(attr, PTHREAD_EXPLICIT_SCHED)
pthread_create(thread, attr, thread_func)
I know I could just use a pthread with C-like init code, but I wanted to see if I can proper Nim threads (at least under linux).
I had a look at the Nim createThread code, and for linux it looks just like the above, minus the real time attribute.
Before I go ahead and create my own patched createThread (or use posix ones- ew), is there someone who has already done work in this area? I couldn't find anything anywhere. Thanks!
Okay so I got myself a a quick-and-dirty interim quick fix that works... I noticed I could just compile the C example into Nim and run it, getting a runRealtime proc that I can just feed a callback to. Primitive! But does work!
I found you can use proper Nim threads for everything that isn't realtime. In my code for now communication is done via global variable flags and locking just means resources are only used by one thread or the other.
// rt.c
/*
* Quick-and-dirty Nim realtime scheduling for Linux
*
* Based on the:
*
* POSIX Real Time Example
* using a single pthread as RT thread
*/
#include <limits.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
int run_realtime_thread(void *(*tf) ())
{
struct sched_param param;
pthread_attr_t attr;
pthread_t thread;
int ret;
/* Lock memory */
if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
printf("mlockall failed: %m\n");
exit(-2);
}
/* Initialize pthread attributes (default values) */
ret = pthread_attr_init(&attr);
if (ret) {
printf("init pthread attributes failed\n");
goto out;
}
/* Set a specific stack size */
ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
if (ret) {
printf("pthread setstacksize failed\n");
goto out;
}
/* Set scheduler policy and priority of pthread */
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
printf("pthread setschedpolicy failed\n");
goto out;
}
param.sched_priority = 80;
ret = pthread_attr_setschedparam(&attr, ¶m);
if (ret) {
printf("pthread setschedparam failed\n");
goto out;
}
/* Use scheduling parameters of attr */
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
printf("pthread setinheritsched failed\n");
goto out;
}
/* Create a pthread with specified attributes */
ret = pthread_create(&thread, &attr, *tf, NULL);
if (ret) {
printf("create pthread failed\n");
goto out;
}
out:
ret = pthread_join(thread, NULL);
if (ret)
printf("join pthread failed: %m\n");
return ret;
}
# rt.nim
import posix
{.compile: "rt.c".}
proc runRealtime*(p: proc () {.cdecl.})
{.importc: "run_realtime_thread".}
# microsecond sleep (max 1s for now)
proc nod*(ns: int) =
var a, b: Timespec
a.tv_sec = Time(0)
a.tv_nsec = ns
discard nanosleep(a, hb)
# main.nim
# realtime thread
proc rt() {.cdecl.} =
while true:
nod(500_000_000) # whoo-hoo precise ns sleep
# main thread, non-realtime
var mainThread = Thread[void]
proc main {.thread.} =
while true:
sleep 1000
createThread(mainThread, main)
runRealtime(rt)
All the functions you use are in std/posix so you can implement it in pure Nim.
You could also use createThread as a starting point which already has a pthread_attr struct in usage : https://github.com/nim-lang/Nim/blob/ae4b47c5bd48d244ee1f93ec6ba5f6bcf55eb973/lib/std/typedthreads.nim#L259
I think if you combine those you can create a more "idiomatic" createRealTimeThread in native Nim :)
Thanks!!! Oh yes I love std/posix- the emphasis here was 'quick'.
A pthread solution is throwaway, any more effort would have to go towards Nim threads with SCHED_FIFO.
Maybe copy/paste createThread into createThreadRealtime, add scheduling and put into Nimble package. But I don't know which intricacies to test.
Maybe copy/paste createThread into createThreadRealtime, add scheduling and put into Nimble package.
That was my idea, yes. All you need to modify from the base proc createThread is a few posix. pthread_attr_setinheritsched, posix. pthread_attr_setschedpolicy etc.
Another possibility, depending on what you actually want to do, is to change the niceness of the program : https://nim-lang.org/docs/posix.html#nice%2Ccint
I finally made a proper library!
https://nimble.directory/pkg/rtthread
It contains a single proc, createRealtimeThread. It works just like createThread and the result is just like any other Nim thread except the priority is configured differently internally.
Many thanks to @timothee for being stubborn about adding the import foo {.all.} syntax, it was really helpful.
Many thanks to @timothee for being stubborn about adding the import foo {.all.} syntax, it was really helpful.
Guess what, Nimony doesn't support it... ;-)
Guess what, Nimony doesn't support it... ;-)
Then we must add realtime threading to the standard library before the Nimony RC!
Excellent! Have you done any timings?
Nope- great idea. I have no idea yet what to measure though.
Nope- great idea. I have no idea yet what to measure though.
Spoken like a real audio developer who must of course avoid allocations and exception handling cause somebody in 2003 said so.
Sorry, couldn't resist. :-)
Andreas, I am going to have to report you to HR, you are being authentic again!
😉 All jokes aside- I find measuring RT performance in a meaningful way genuinely hard. It's relative to load and hardware. For a realistic test you load CPU and IO in different ways and after an hour you know if it works in your system, but not necessarily others.
Having said that, I have a raspberry pi that produces a pulse signal to control a motor on a pin with a realtime thread that looks like a rather convincing square wave on the oscilloscope.
I find measuring RT performance in a meaningful way genuinely hard.
RT isn't about performance though but determinism and guaranteeing no missed deadlines. Being fast and being deterministic are very different.
The way to measure the 'real-time' aspect of a system is to create tasks and measure missed deadlines, then increase it until you have the shortest deadlines with 0 missed deadlines (ofc if a certain % of deadlines miss are acceptable you can set that as your criteria).
Overall, unless you're working with very low power MCU or you have very fast sensors / very high frequency signals to process, basic RT-Linux on any modern CPU is enough for the large majority of use cases.
echo off > /sys/devices/system/cpu/smt/control