summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/portmidi/porttime/ptmacosx_mach.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/portmidi/porttime/ptmacosx_mach.c')
-rw-r--r--3rdparty/portmidi/porttime/ptmacosx_mach.c103
1 files changed, 87 insertions, 16 deletions
diff --git a/3rdparty/portmidi/porttime/ptmacosx_mach.c b/3rdparty/portmidi/porttime/ptmacosx_mach.c
index f4fec09d4c4..bc6a1a01084 100644
--- a/3rdparty/portmidi/porttime/ptmacosx_mach.c
+++ b/3rdparty/portmidi/porttime/ptmacosx_mach.c
@@ -4,20 +4,29 @@
#include <stdio.h>
#include <CoreAudio/HostTime.h>
-#include <mach/mach.h>
-#include <mach/mach_error.h>
-#include <mach/mach_time.h>
-#include <mach/clock.h>
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <mach/mach_time.h>
+#import <mach/clock.h>
#include <unistd.h>
+#include <AvailabilityMacros.h>
#include "porttime.h"
#include "sys/time.h"
#include "pthread.h"
-#if !defined NSEC_PER_MSEC
-#define NSEC_PER_MSEC 1000000ull
+#ifndef NSEC_PER_MSEC
+#define NSEC_PER_MSEC 1000000
+#endif
+#define THREAD_IMPORTANCE 63
+
+// QOS headers are available as of macOS 10.10
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
+#include "sys/qos.h"
+#define HAVE_APPLE_QOS 1
+#else
+#undef HAVE_APPLE_QOS
#endif
-#define THREAD_IMPORTANCE 30
static int time_started_flag = FALSE;
static UInt64 start_time;
@@ -47,7 +56,7 @@ static void *Pt_CallbackProc(void *p)
(thread_policy_t)&extendedPolicy,
THREAD_EXTENDED_POLICY_COUNT);
if (error != KERN_SUCCESS) {
- mach_error((char *)"Couldn't set thread timeshare policy", error);
+ mach_error("Couldn't set thread timeshare policy", error);
}
precedencePolicy.importance = THREAD_IMPORTANCE;
@@ -55,22 +64,65 @@ static void *Pt_CallbackProc(void *p)
(thread_policy_t)&precedencePolicy,
THREAD_PRECEDENCE_POLICY_COUNT);
if (error != KERN_SUCCESS) {
- mach_error((char *)"Couldn't set thread precedence policy", error);
+ mach_error("Couldn't set thread precedence policy", error);
}
+ // Most important, set real-time constraints.
+
+ // Define the guaranteed and max fraction of time for the audio thread.
+ // These "duty cycle" values can range from 0 to 1. A value of 0.5
+ // means the scheduler would give half the time to the thread.
+ // These values have empirically been found to yield good behavior.
+ // Good means that audio performance is high and other threads won't starve.
+ const double kGuaranteedAudioDutyCycle = 0.75;
+ const double kMaxAudioDutyCycle = 0.85;
+
+ // Define constants determining how much time the audio thread can
+ // use in a given time quantum. All times are in milliseconds.
+
+ // About 128 frames @44.1KHz
+ const double kTimeQuantum = 2.9;
+
+ // Time guaranteed each quantum.
+ const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
+
+ // Maximum time each quantum.
+ const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
+ // Get the conversion factor from milliseconds to absolute time
+ // which is what the time-constraints call needs.
+ mach_timebase_info_data_t tb_info;
+ mach_timebase_info(&tb_info);
+ double ms_to_abs_time =
+ ((double)tb_info.denom / (double)tb_info.numer) * 1000000;
+
+ thread_time_constraint_policy_data_t time_constraints;
+ time_constraints.period = (uint32_t)(kTimeQuantum * ms_to_abs_time);
+ time_constraints.computation = (uint32_t)(kAudioTimeNeeded * ms_to_abs_time);
+ time_constraints.constraint = (uint32_t)(kMaxTimeAllowed * ms_to_abs_time);
+ time_constraints.preemptible = 0;
+
+ error = thread_policy_set(mach_thread_self(),
+ THREAD_TIME_CONSTRAINT_POLICY,
+ (thread_policy_t)&time_constraints,
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ if (error != KERN_SUCCESS) {
+ mach_error("Couldn't set thread precedence policy", error);
+ }
+
/* to kill a process, just increment the pt_callback_proc_id */
- /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */
+ /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id,
+ parameters->id); */
while (pt_callback_proc_id == parameters->id) {
/* wait for a multiple of resolution ms */
UInt64 wait_time;
int delay = mytime++ * parameters->resolution - Pt_Time();
- PtTimestamp timestamp;
+ PtTimestamp timestamp;
if (delay < 0) delay = 0;
wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC);
wait_time += AudioGetCurrentHostTime();
- error = mach_wait_until(wait_time);
- timestamp = Pt_Time();
+ mach_wait_until(wait_time);
+ timestamp = Pt_Time();
(*(parameters->callback))(timestamp, parameters->userData);
}
free(parameters);
@@ -93,7 +145,26 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
parms->resolution = resolution;
parms->callback = callback;
parms->userData = userData;
+
+#ifdef HAVE_APPLE_QOS
+ pthread_attr_t qosAttribute;
+ pthread_attr_init(&qosAttribute);
+ pthread_attr_set_qos_class_np(&qosAttribute,
+ QOS_CLASS_USER_INTERACTIVE, 0);
+
+ res = pthread_create(&pt_thread_pid, &qosAttribute, Pt_CallbackProc,
+ parms);
+#else
res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms);
+#endif
+
+ struct sched_param sp;
+ memset(&sp, 0, sizeof(struct sched_param));
+ sp.sched_priority = sched_get_priority_max(SCHED_RR);
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == -1) {
+ return ptHostError;
+ }
+
if (res != 0) return ptHostError;
}
@@ -102,7 +173,7 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
}
-PtError Pt_Stop()
+PtError Pt_Stop(void)
{
/* printf("Pt_Stop called\n"); */
pt_callback_proc_id++;
@@ -112,13 +183,13 @@ PtError Pt_Stop()
}
-int Pt_Started()
+int Pt_Started(void)
{
return time_started_flag;
}
-PtTimestamp Pt_Time()
+PtTimestamp Pt_Time(void)
{
UInt64 clock_time, nsec_time;
clock_time = AudioGetCurrentHostTime() - start_time;