6 #include <mach/mach_time.h>
8 #include <sys/sysctl.h>
10 #include <darwintest.h>
12 static mach_timebase_info_data_t timebase_info
;
15 nanos_to_abs(uint64_t nanos
)
17 return nanos
* timebase_info
.denom
/ timebase_info
.numer
;
20 abs_to_nanos(uint64_t abs
)
22 return abs
* timebase_info
.numer
/ timebase_info
.denom
;
26 /* Spin until a specified number of seconds elapses */
28 spin_for_duration(uint32_t seconds
)
30 uint64_t duration
= nanos_to_abs((uint64_t)seconds
* NSEC_PER_SEC
);
31 uint64_t current_time
= mach_absolute_time();
32 uint64_t timeout
= duration
+ current_time
;
34 uint64_t spin_count
= 0;
36 while (mach_absolute_time() < timeout
) {
42 spin_thread(__unused
void *arg
)
49 bind_to_cluster(char type
)
52 size_t type_size
= sizeof(type
);
53 T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.sched_thread_bind_cluster_type",
54 &old_type
, &type_size
, &type
, sizeof(type
)),
55 "bind current thread to cluster %c", type
);
59 spin_bound_thread(void *arg
)
61 char type
= (char)arg
;
62 bind_to_cluster(type
);
63 spin_for_duration(10);
71 size_t sysctl_size
= sizeof(ncpu
);
72 int ret
= sysctlbyname("hw.ncpu", &ncpu
, &sysctl_size
, NULL
, 0);
74 return (unsigned int) ncpu
;
77 #define SPINNER_THREAD_LOAD_FACTOR (4)
79 T_DECL(test_cluster_bound_thread_timeshare
, "Make sure the low priority bound threads get CPU in the presence of non-bound CPU spinners",
80 T_META_BOOTARGS_SET("enable_skstb=1"), T_META_ASROOT(true))
82 #if TARGET_CPU_ARM64 && TARGET_OS_OSX
83 pthread_setname_np("main thread");
87 kr
= mach_timebase_info(&timebase_info
);
88 T_QUIET
; T_ASSERT_MACH_SUCCESS(kr
, "mach_timebase_info");
93 rv
= pthread_attr_init(&attr
);
94 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_attr_init");
96 rv
= pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
97 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_attr_setdetachstate");
99 rv
= pthread_attr_set_qos_class_np(&attr
, QOS_CLASS_USER_INITIATED
, 0);
100 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_attr_set_qos_class_np");
102 unsigned int ncpu
= get_ncpu();
103 pthread_t unbound_thread
;
104 pthread_t bound_thread
;
106 T_LOG("creating %u non-bound threads\n", ncpu
* SPINNER_THREAD_LOAD_FACTOR
);
108 for (int i
= 0; i
< ncpu
* SPINNER_THREAD_LOAD_FACTOR
; i
++) {
109 rv
= pthread_create(&unbound_thread
, &attr
, spin_thread
, NULL
);
110 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_create (non-bound)");
113 struct sched_param param
= { .sched_priority
= (int)20 };
114 T_ASSERT_POSIX_ZERO(pthread_attr_setschedparam(&attr
, ¶m
), "pthread_attr_setschedparam");
116 rv
= pthread_create(&bound_thread
, &attr
, spin_bound_thread
, (void *)(uintptr_t)'P');
117 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_create (P-bound)");
119 rv
= pthread_attr_destroy(&attr
);
120 T_QUIET
; T_ASSERT_POSIX_SUCCESS(rv
, "pthread_attr_destroy");
124 mach_msg_type_number_t count
= THREAD_BASIC_INFO_COUNT
;
125 mach_port_t thread_port
= pthread_mach_thread_np(bound_thread
);
126 thread_basic_info_data_t bound_thread_info
;
128 kr
= thread_info(thread_port
, THREAD_BASIC_INFO
, (thread_info_t
)&bound_thread_info
, &count
);
129 if (kr
!= KERN_SUCCESS
) {
130 err("%#x == thread_info(bound_thread, THREAD_BASIC_INFO)", kr
);
133 uint64_t bound_usr_usec
= bound_thread_info
.user_time
.seconds
* USEC_PER_SEC
+ bound_thread_info
.user_time
.microseconds
;
135 T_ASSERT_GT(bound_usr_usec
, 75000, "Check that bound thread got atleast 75ms CPU time");
136 T_PASS("Low priority bound threads got some CPU time in the presence of high priority unbound spinners");
137 #else /* TARGET_CPU_ARM64 && TARGET_OS_OSX */
138 T_SKIP("Test not supported on this platform!");
139 #endif /* TARGET_CPU_ARM64 && TARGET_OS_OSX */