qos.c   [plain text]


/*% clang -o # -Wall -Wextra -I/System/Library/Frameworks/System.framework/PrivateHeaders %
 */

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>

#include <sys/qos.h>
#include <sys/resource.h>
#include <pthread.h>

#include <mach/mach.h>
#include <mach/host_info.h>
#include <mach/mach_error.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mach_syscalls.h>
#include <mach/policy.h>
#include <mach/task_policy.h>

#define QOS_TIER(i) \
	(((i) == THREAD_QOS_UNSPECIFIED)        ?  QOS_CLASS_UNSPECIFIED     :       \
	 ((i)  == THREAD_QOS_USER_INTERACTIVE)   ? QOS_CLASS_USER_INTERACTIVE :       \
	 ((i)  == THREAD_QOS_USER_INITIATED)     ? QOS_CLASS_USER_INITIATED   :       \
	 ((i)  == THREAD_QOS_LEGACY)             ? QOS_CLASS_DEFAULT          :       \
	 ((i)  == THREAD_QOS_UTILITY)            ? QOS_CLASS_UTILITY          :       \
	 ((i)  == THREAD_QOS_BACKGROUND)         ? QOS_CLASS_BACKGROUND       :       \
	 ((i)  == THREAD_QOS_MAINTENANCE)        ? QOS_CLASS_MAINTENANCE      :       \
	 -1)

struct kern_qos {
	long requested;
	long override;
};

void get_kern_qos(struct kern_qos *kern_qos){
	kern_return_t kr;
	boolean_t get_default = false;
	mach_msg_type_number_t count;

	struct thread_policy_state thread_policy;
	count = THREAD_POLICY_STATE_COUNT;
	kr = thread_policy_get(mach_thread_self(), THREAD_POLICY_STATE, (thread_policy_t)&thread_policy, &count, &get_default);
	if (kr != KERN_SUCCESS) { mach_error("thread_policy_get(... THREAD_POLICY_STATE ...)", kr); }

	kern_qos->requested = QOS_TIER((thread_policy.requested & POLICY_REQ_TH_QOS_MASK) >> POLICY_REQ_TH_QOS_SHIFT);
	kern_qos->override = QOS_TIER((thread_policy.requested & POLICY_REQ_TH_QOS_OVER_MASK) >> POLICY_REQ_TH_QOS_OVER_SHIFT);
}

void assert_fixedpri(bool fixedpri){
	kern_return_t kr;
	boolean_t get_default = false;
	mach_msg_type_number_t count;

	thread_extended_policy_data_t extpol;
	count = THREAD_EXTENDED_POLICY_COUNT;
	kr = thread_policy_get(mach_thread_self(), THREAD_EXTENDED_POLICY, (thread_policy_t)&extpol, &count, &get_default);
	if (kr != KERN_SUCCESS) { mach_error("thread_policy_get(... THREAD_EXTENDED_POLICY ...)", kr); }

	assert(extpol.timeshare == !fixedpri);
}

void *assert_thread_qos(void *arg){
	struct kern_qos *correct_kern_qos = (struct kern_qos *)arg;
	struct kern_qos actual_kern_qos;

	get_kern_qos(&actual_kern_qos);

	assert(actual_kern_qos.requested == qos_class_self());
	assert(actual_kern_qos.requested == correct_kern_qos->requested);
	assert(actual_kern_qos.override == correct_kern_qos->override);

	return NULL;
}

void *fixedpri_test(void *arg){
	struct kern_qos *correct_kern_qos = (struct kern_qos *)arg;

	assert_thread_qos(correct_kern_qos);
	assert_fixedpri(false);

	pthread_set_fixedpriority_self();

	assert_thread_qos(correct_kern_qos);
	assert_fixedpri(true);

	pthread_set_timeshare_self();

	assert_thread_qos(correct_kern_qos);
	assert_fixedpri(false);

	return NULL;
}

int main(){
	if (geteuid() != 0){
		printf("Must be run as root\n");
		return 1;
	}

	struct kern_qos kern_qos;

	pthread_t thread;
	pthread_attr_t attr;
	pthread_attr_init(&attr);

	// Main thread QoS

	kern_qos.requested = qos_class_self();
	kern_qos.override = QOS_CLASS_UNSPECIFIED;

	assert_thread_qos(&kern_qos);
	assert(qos_class_self() == qos_class_main());

	// Created pthread

	kern_qos.requested = QOS_CLASS_UTILITY;
	kern_qos.override = QOS_CLASS_UNSPECIFIED;

	pthread_attr_set_qos_class_np(&attr, QOS_CLASS_UTILITY, 0);
	pthread_create(&thread, &attr, assert_thread_qos, &kern_qos);
	pthread_join(thread, NULL);

	// pthread_set_fixedpriority_self()

	kern_qos.requested = QOS_CLASS_USER_INITIATED;
	kern_qos.override = QOS_CLASS_UNSPECIFIED;

	pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INITIATED, 0);
	pthread_create(&thread, &attr, fixedpri_test, &kern_qos);
	pthread_join(thread, NULL);

	return 0;
}