unix: Implement `-X realtime` command-line option on macOS.

This adds a new command line option to the unix port `-X realtime` to
enable realtime priority on threads.  This enables high precision timers
for applications that need more accurate timers.

Related docs:
https://developer.apple.com/library/archive/technotes/tn2169/_index.html

Fixes issue #8621.

Signed-off-by: David Lechner <david@pybricks.com>
This commit is contained in:
David Lechner 2022-05-03 22:26:29 -05:00 committed by Damien George
parent be5657b64f
commit c012318d74
4 changed files with 65 additions and 0 deletions

View File

@ -73,6 +73,8 @@ General options:
- ``-X heapsize=<n>[w][K|M]`` sets the heap size for the garbage collector.
The suffix ``w`` means words instead of bytes. ``K`` means x1024 and ``M``
means x1024x1024.
- ``-X realtime`` sets thread priority to realtime. This can be used to
improve timer precision. Only available on macOS.

View File

@ -328,6 +328,10 @@ STATIC void print_help(char **argv) {
, heap_size);
impl_opts_cnt++;
#endif
#if defined(__APPLE__)
printf(" realtime -- set thread priority to realtime\n");
impl_opts_cnt++;
#endif
if (impl_opts_cnt == 0) {
printf(" (none)\n");
@ -399,6 +403,15 @@ STATIC void pre_process_options(int argc, char **argv) {
goto invalid_arg;
}
#endif
#if defined(__APPLE__)
} else if (strcmp(argv[a + 1], "realtime") == 0) {
#if MICROPY_PY_THREAD
mp_thread_is_realtime_enabled = true;
#endif
// main thread was already intialized before the option
// was parsed, so we have to enable realtime here.
mp_thread_set_realtime();
#endif
} else {
invalid_arg:
exit(invalid_args());

View File

@ -192,6 +192,13 @@ void mp_thread_set_state(mp_state_thread_t *state) {
}
void mp_thread_start(void) {
// enable realtime priority if `-X realtime` command line parameter was set
#if defined(__APPLE__)
if (mp_thread_is_realtime_enabled) {
mp_thread_set_realtime();
}
#endif
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
mp_thread_unix_begin_atomic_section();
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
@ -310,3 +317,39 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
}
#endif // MICROPY_PY_THREAD
// this is used even when MICROPY_PY_THREAD is disabled
#if defined(__APPLE__)
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <mach/thread_act.h>
#include <mach/thread_policy.h>
bool mp_thread_is_realtime_enabled;
// based on https://developer.apple.com/library/archive/technotes/tn2169/_index.html
void mp_thread_set_realtime(void) {
mach_timebase_info_data_t timebase_info;
mach_timebase_info(&timebase_info);
const uint64_t NANOS_PER_MSEC = 1000000ULL;
double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
thread_time_constraint_policy_data_t policy;
policy.period = 0;
policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
policy.constraint = (uint32_t)(10 * clock2abs);
policy.preemptible = FALSE;
int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
(thread_policy_t)&policy,
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (kr != KERN_SUCCESS) {
mach_error("thread_policy_set:", kr);
}
}
#endif

View File

@ -25,6 +25,7 @@
*/
#include <pthread.h>
#include <stdbool.h>
typedef pthread_mutex_t mp_thread_mutex_t;
@ -36,3 +37,9 @@ void mp_thread_gc_others(void);
// Functions as a port-global lock for any code that must be serialised.
void mp_thread_unix_begin_atomic_section(void);
void mp_thread_unix_end_atomic_section(void);
// for `-X realtime` command line option
#if defined(__APPLE__)
extern bool mp_thread_is_realtime_enabled;
void mp_thread_set_realtime(void);
#endif