+static inline void
+enable_ints_and_unlock(void)
+{
+ thread_call_unlock();
+ (void)spllo();
+}
+
+
+static inline boolean_t
+group_isparallel(thread_call_group_t group)
+{
+ return ((group->flags & TCG_PARALLEL) != 0);
+}
+
+static boolean_t
+thread_call_group_should_add_thread(thread_call_group_t group)
+{
+ uint32_t thread_count;
+
+ if (!group_isparallel(group)) {
+ if (group->pending_count > 0 && group->active_count == 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ if (group->pending_count > 0) {
+ if (group->idle_count > 0) {
+ panic("Pending work, but threads are idle?");
+ }
+
+ thread_count = group->active_count;
+
+ /*
+ * Add a thread if either there are no threads,
+ * the group has fewer than its target number of
+ * threads, or the amount of work is large relative
+ * to the number of threads. In the last case, pay attention
+ * to the total load on the system, and back off if
+ * it's high.
+ */
+ if ((thread_count == 0) ||
+ (thread_count < group->target_thread_count) ||
+ ((group->pending_count > THREAD_CALL_ADD_RATIO * thread_count) &&
+ (sched_mach_factor < THREAD_CALL_MACH_FACTOR_CAP))) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static inline integer_t
+thread_call_priority_to_sched_pri(thread_call_priority_t pri)
+{
+ switch (pri) {
+ case THREAD_CALL_PRIORITY_HIGH:
+ return BASEPRI_PREEMPT;
+ case THREAD_CALL_PRIORITY_KERNEL:
+ return BASEPRI_KERNEL;
+ case THREAD_CALL_PRIORITY_USER:
+ return BASEPRI_DEFAULT;
+ case THREAD_CALL_PRIORITY_LOW:
+ return DEPRESSPRI;
+ default:
+ panic("Invalid priority.");
+ }
+
+ return 0;
+}
+
+/* Lock held */
+static inline thread_call_group_t
+thread_call_get_group(
+ thread_call_t call)
+{
+ thread_call_priority_t pri = call->tc_pri;
+
+ assert(pri == THREAD_CALL_PRIORITY_LOW ||
+ pri == THREAD_CALL_PRIORITY_USER ||
+ pri == THREAD_CALL_PRIORITY_KERNEL ||
+ pri == THREAD_CALL_PRIORITY_HIGH);
+
+ return &thread_call_groups[pri];
+}
+
+static void
+thread_call_group_setup(
+ thread_call_group_t group,
+ thread_call_priority_t pri,
+ uint32_t target_thread_count,
+ boolean_t parallel)
+{
+ queue_init(&group->pending_queue);
+ queue_init(&group->delayed_queue);
+
+ timer_call_setup(&group->delayed_timer, thread_call_delayed_timer, group);
+ timer_call_setup(&group->dealloc_timer, thread_call_dealloc_timer, group);
+
+ wait_queue_init(&group->idle_wqueue, SYNC_POLICY_FIFO);
+
+ group->target_thread_count = target_thread_count;
+ group->pri = thread_call_priority_to_sched_pri(pri);
+
+ group->sched_call = sched_call_thread;
+ if (parallel) {
+ group->flags |= TCG_PARALLEL;
+ group->sched_call = NULL;
+ }
+}
+
+/*
+ * Simple wrapper for creating threads bound to
+ * thread call groups.
+ */
+static kern_return_t
+thread_call_thread_create(
+ thread_call_group_t group)
+{
+ thread_t thread;
+ kern_return_t result;
+
+ result = kernel_thread_start_priority((thread_continue_t)thread_call_thread, group, group->pri, &thread);
+ if (result != KERN_SUCCESS) {
+ return result;
+ }
+
+ if (group->pri < BASEPRI_PREEMPT) {
+ /*
+ * New style doesn't get to run to completion in
+ * kernel if there are higher priority threads
+ * available.
+ */
+ thread_set_eager_preempt(thread);
+ }
+
+ thread_deallocate(thread);
+ return KERN_SUCCESS;
+}