/* * thread_call_daemon: */ static void thread_call_daemon_continue( thread_call_group_t group) { kern_return_t result; thread_t thread; (void) splsched(); thread_call_lock_spin(); while (group->active_count == 0 && group->pending_count > 0) { group->active_count++; thread_call_unlock(); (void) spllo(); result = kernel_thread_start_priority((thread_continue_t)thread_call_thread, group, BASEPRI_PREEMPT, &thread); if (result != KERN_SUCCESS) panic("thread_call_daemon"); thread_deallocate(thread); (void) splsched(); thread_call_lock_spin(); } thread_call_daemon_awake = FALSE; wait_queue_assert_wait(&group->daemon_wqueue, NO_EVENT, THREAD_UNINT, 0); thread_call_unlock(); (void) spllo(); thread_block_parameter((thread_continue_t)thread_call_daemon_continue, group); /* NOTREACHED */ }
/* * thread_call_daemon: walk list of groups, allocating * threads if appropriate (as determined by * thread_call_group_should_add_thread()). */ static void thread_call_daemon_continue(__unused void *arg) { int i; kern_return_t kr; thread_call_group_t group; (void)disable_ints_and_lock(); /* Starting at zero happens to be high-priority first. */ for (i = 0; i < THREAD_CALL_GROUP_COUNT; i++) { group = &thread_call_groups[i]; while (thread_call_group_should_add_thread(group)) { group->active_count++; enable_ints_and_unlock(); kr = thread_call_thread_create(group); if (kr != KERN_SUCCESS) { /* * On failure, just pause for a moment and give up. * We can try again later. */ delay(10000); /* 10 ms */ (void)disable_ints_and_lock(); goto out; } (void)disable_ints_and_lock(); } } out: thread_call_daemon_awake = FALSE; wait_queue_assert_wait(&daemon_wqueue, NO_EVENT, THREAD_UNINT, 0); enable_ints_and_unlock(); thread_block_parameter((thread_continue_t)thread_call_daemon_continue, NULL); /* NOTREACHED */ }
/* * thread_call_thread: */ static void thread_call_thread( thread_call_group_t group, wait_result_t wres) { thread_t self = current_thread(); boolean_t canwait; /* * A wakeup with THREAD_INTERRUPTED indicates that * we should terminate. */ if (wres == THREAD_INTERRUPTED) { thread_terminate(self); /* NOTREACHED */ panic("thread_terminate() returned?"); } (void)disable_ints_and_lock(); thread_sched_call(self, group->sched_call); while (group->pending_count > 0) { thread_call_t call; thread_call_func_t func; thread_call_param_t param0, param1; call = TC(dequeue_head(&group->pending_queue)); group->pending_count--; func = call->tc_call.func; param0 = call->tc_call.param0; param1 = call->tc_call.param1; call->tc_call.queue = NULL; _internal_call_release(call); /* * Can only do wakeups for thread calls whose storage * we control. */ if ((call->tc_flags & THREAD_CALL_ALLOC) != 0) { canwait = TRUE; call->tc_refs++; /* Delay free until we're done */ } else canwait = FALSE; enable_ints_and_unlock(); KERNEL_DEBUG_CONSTANT( MACHDBG_CODE(DBG_MACH_SCHED,MACH_CALLOUT) | DBG_FUNC_NONE, VM_KERNEL_UNSLIDE(func), param0, param1, 0, 0); (*func)(param0, param1); if (get_preemption_level() != 0) { int pl = get_preemption_level(); panic("thread_call_thread: preemption_level %d, last callout %p(%p, %p)", pl, (void *)VM_KERNEL_UNSLIDE(func), param0, param1); } (void)thread_funnel_set(self->funnel_lock, FALSE); /* XXX */ (void) disable_ints_and_lock(); if (canwait) { /* Frees if so desired */ thread_call_finish(call); } } thread_sched_call(self, NULL); group->active_count--; if (group_isparallel(group)) { /* * For new style of thread group, thread always blocks. * If we have more than the target number of threads, * and this is the first to block, and it isn't active * already, set a timer for deallocating a thread if we * continue to have a surplus. */ group->idle_count++; if (group->idle_count == 1) { group->idle_timestamp = mach_absolute_time(); } if (((group->flags & TCG_DEALLOC_ACTIVE) == 0) && ((group->active_count + group->idle_count) > group->target_thread_count)) { group->flags |= TCG_DEALLOC_ACTIVE; thread_call_start_deallocate_timer(group); } /* Wait for more work (or termination) */ wres = wait_queue_assert_wait(&group->idle_wqueue, NO_EVENT, THREAD_INTERRUPTIBLE, 0); if (wres != THREAD_WAITING) { panic("kcall worker unable to assert wait?"); } enable_ints_and_unlock(); thread_block_parameter((thread_continue_t)thread_call_thread, group); } else { if (group->idle_count < group->target_thread_count) { group->idle_count++; wait_queue_assert_wait(&group->idle_wqueue, NO_EVENT, THREAD_UNINT, 0); /* Interrupted means to exit */ enable_ints_and_unlock(); thread_block_parameter((thread_continue_t)thread_call_thread, group); /* NOTREACHED */ } } enable_ints_and_unlock(); thread_terminate(self); /* NOTREACHED */ }
/* * thread_call_thread: */ static void thread_call_thread( thread_call_group_t group) { thread_t self = current_thread(); (void) splsched(); thread_call_lock_spin(); thread_sched_call(self, sched_call_thread); while (group->pending_count > 0) { thread_call_t call; thread_call_func_t func; thread_call_param_t param0, param1; call = TC(dequeue_head(&group->pending_queue)); group->pending_count--; func = call->func; param0 = call->param0; param1 = call->param1; call->queue = NULL; _internal_call_release(call); thread_call_unlock(); (void) spllo(); KERNEL_DEBUG_CONSTANT( MACHDBG_CODE(DBG_MACH_SCHED,MACH_CALLOUT) | DBG_FUNC_NONE, func, param0, param1, 0, 0); (*func)(param0, param1); if (get_preemption_level() != 0) { int pl = get_preemption_level(); panic("thread_call_thread: preemption_level %d, last callout %p(%p, %p)", pl, func, param0, param1); } (void)thread_funnel_set(self->funnel_lock, FALSE); /* XXX */ (void) splsched(); thread_call_lock_spin(); } thread_sched_call(self, NULL); group->active_count--; if (group->idle_count < thread_call_thread_min) { group->idle_count++; wait_queue_assert_wait(&group->idle_wqueue, NO_EVENT, THREAD_UNINT, 0); thread_call_unlock(); (void) spllo(); thread_block_parameter((thread_continue_t)thread_call_thread, group); /* NOTREACHED */ } thread_call_unlock(); (void) spllo(); thread_terminate(self); /* NOTREACHED */ }