+inline static void
+yield(void)
+{
+#if defined(__arm__) || defined(__arm64__)
+ asm volatile("yield");
+#elif defined(__x86_64__) || defined(__i386__)
+ asm volatile("pause");
+#else
+#error Unrecognized architecture
+#endif
+}
+
+static void *
+churn_thread(__unused void *arg)
+{
+ uint64_t spin_count = 0;
+
+ /*
+ * As a safety measure to avoid wedging, we will bail on the spin if
+ * it's been more than 1s after the most recent run start
+ */
+
+ while (g_churn_stop == FALSE &&
+ mach_absolute_time() < (g_starttime_abs + NSEC_PER_SEC)) {
+ spin_count++;
+ yield();
+ }
+
+ /* This is totally racy, but only here to detect if anyone stops early */
+ atomic_fetch_add_explicit(&g_churn_stopped_at, spin_count, memory_order_relaxed);
+
+ return NULL;
+}
+
+static void
+create_churn_threads()
+{
+ if (g_churn_count == 0)
+ g_churn_count = g_numcpus - 1;
+
+ errno_t err;
+
+ struct sched_param param = { .sched_priority = (int)g_churn_pri };
+ pthread_attr_t attr;
+
+ /* Array for churn threads */
+ g_churn_threads = (pthread_t*) valloc(sizeof(pthread_t) * g_churn_count);
+ assert(g_churn_threads);
+
+ if ((err = pthread_attr_init(&attr)))
+ errc(EX_OSERR, err, "pthread_attr_init");
+
+ if ((err = pthread_attr_setschedparam(&attr, ¶m)))
+ errc(EX_OSERR, err, "pthread_attr_setschedparam");
+
+ if ((err = pthread_attr_setschedpolicy(&attr, SCHED_RR)))
+ errc(EX_OSERR, err, "pthread_attr_setschedpolicy");
+
+ for (uint32_t i = 0 ; i < g_churn_count ; i++) {
+ pthread_t new_thread;
+
+ if ((err = pthread_create(&new_thread, &attr, churn_thread, NULL)))
+ errc(EX_OSERR, err, "pthread_create");
+ g_churn_threads[i] = new_thread;
+ }
+
+ if ((err = pthread_attr_destroy(&attr)))
+ errc(EX_OSERR, err, "pthread_attr_destroy");
+}
+
+static void
+join_churn_threads(void)
+{
+ if (atomic_load_explicit(&g_churn_stopped_at, memory_order_seq_cst) != 0)
+ printf("Warning: Some of the churn threads may have stopped early: %lld\n",
+ g_churn_stopped_at);
+
+ atomic_store_explicit(&g_churn_stop, TRUE, memory_order_seq_cst);
+
+ /* Rejoin churn threads */
+ for (uint32_t i = 0; i < g_churn_count; i++) {
+ errno_t err = pthread_join(g_churn_threads[i], NULL);
+ if (err) errc(EX_OSERR, err, "pthread_join %d", i);
+ }
+}
+