zircon/kernel/kernel/scheduler.cc
// Selects a thread to run. Performs any necessary maintenanace if the current
// thread is changing, depending on the reason for the change.
Selects a thread to run. Performs any necessary maintenanace if the current
thread is changing, depending on the reason for the change.
Selects a thread to run . Performs any necessary maintenanace if the current thread is changing depending on the reason for the change .
Thread* Scheduler::EvaluateNextThread(SchedTime now, Thread* current_thread, bool timeslice_expired,
                                      SchedDuration total_runtime_ns) {
  LocalTraceDuration<KTRACE_DETAILED> trace{"find_thread"_stringref};
  const bool is_idle = current_thread->IsIdle();
  const bool is_active = current_thread->state() == THREAD_READY;
  const bool is_deadline = IsDeadlineThread(current_thread);
  const bool is_new_deadline_eligible = IsDeadlineThreadEligible(now);
  const cpu_num_t current_cpu = arch_curr_cpu_num();
  const cpu_mask_t current_cpu_mask = cpu_num_to_mask(current_cpu);
  const cpu_mask_t active_mask = mp_get_active_mask();
  Thread* next_thread = nullptr;
  if (is_active && likely(!is_idle)) {
    if (timeslice_expired) {
      QueueThread(current_thread, Placement::Insertion, now, total_runtime_ns);
    } else if (is_new_deadline_eligible && is_deadline) {
      const SchedTime deadline_ns = current_thread->scheduler_state().finish_time_;
      if (Thread* const earlier_thread = DequeueEarlierDeadlineThread(now, deadline_ns);
          earlier_thread != nullptr) {
        QueueThread(current_thread, Placement::Preemption, now, total_runtime_ns);
        next_thread = earlier_thread;
      } else {
        next_thread = current_thread;
      }
    } else if (is_new_deadline_eligible && !is_deadline) {
      QueueThread(current_thread, Placement::Preemption, now, total_runtime_ns);
    } else {
      next_thread = current_thread;
    }
  } else if (!is_active && likely(!is_idle)) {
    Remove(current_thread);
  }
  if (next_thread == nullptr) {
    next_thread = DequeueThread(now);
  }
  const auto needs_migration = [active_mask, current_cpu_mask](Thread* const thread) {
    return (thread->scheduler_state().GetEffectiveCpuMask(active_mask) & current_cpu_mask) == 0 ||
           thread->scheduler_state().next_cpu_ != INVALID_CPU;
  };
   Most migrations are passive. Passive migration happens whenever a thread
   becomes READY and a different CPU is selected than the last CPU the thread
   ran on.
   Active migration happens under the following conditions:
    1. The CPU affinity of a thread that is READY or RUNNING is changed to
       exclude the CPU it is currently active on.
    2. Passive migration, or active migration due to #1, selects a different
       CPU for a thread with a migration function. Migration to the next CPU
       is delayed until the migration function is called on the last CPU.
    3. A thread that is READY or RUNNING is relocated by the periodic load
       balancer. NOT YET IMPLEMENTED.
  cpu_mask_t cpus_to_reschedule_mask = 0;
  for (; needs_migration(next_thread); next_thread = DequeueThread(now)) {
     If the thread is not scheduled to migrate to a specifc CPU, find a
     suitable target CPU. If the thread has a migration function, the search
     will schedule the thread to migrate to a specific CPU and return the
     current CPU.
    cpu_num_t target_cpu = INVALID_CPU;
    if (next_thread->scheduler_state().next_cpu_ == INVALID_CPU) {
      target_cpu = FindTargetCpu(next_thread);
      DEBUG_ASSERT(target_cpu != this_cpu() ||
                   next_thread->scheduler_state().next_cpu_ != INVALID_CPU);
    }
     If the thread is scheduled to migrate to a specific CPU, set the target
     to that CPU and call the migration function.
    if (next_thread->scheduler_state().next_cpu_ != INVALID_CPU) {
      DEBUG_ASSERT(next_thread->scheduler_state().last_cpu_ == this_cpu());
      target_cpu = next_thread->scheduler_state().next_cpu_;
      next_thread->CallMigrateFnLocked(Thread::MigrateStage::Before);
      next_thread->scheduler_state().next_cpu_ = INVALID_CPU;
    }
     The target CPU must always be different than the current CPU.
    DEBUG_ASSERT(target_cpu != this_cpu());
     Remove accounting from this run queue and insert in the target run queue.
    Remove(next_thread);
    Scheduler* const target = Get(target_cpu);
    target->Insert(now, next_thread);
    cpus_to_reschedule_mask |= cpu_num_to_mask(target_cpu);
  }
   Issue reschedule IPIs to CPUs with migrated threads.
  if (cpus_to_reschedule_mask) {
    mp_reschedule(cpus_to_reschedule_mask, 0);
  }
  return next_thread;
}
Thread * Scheduler :: EvaluateNextThread ( SchedTime now , Thread * current_thread , bool timeslice_expired , SchedDuration total_runtime_ns ) { LocalTraceDuration < KTRACE_DETAILED > trace { " " _stringref } ; const bool is_idle = current_thread -> IsIdle ( ) ; const bool is_active = current_thread -> state ( ) == THREAD_READY ; const bool is_deadline = IsDeadlineThread ( current_thread ) ; const bool is_new_deadline_eligible = IsDeadlineThreadEligible ( now ) ; const cpu_num_t current_cpu = arch_curr_cpu_num ( ) ; const cpu_mask_t current_cpu_mask = cpu_num_to_mask ( current_cpu ) ; const cpu_mask_t active_mask = mp_get_active_mask ( ) ; Thread * next_thread = nullptr ; if ( is_active && likely ( ! is_idle ) ) { if ( timeslice_expired ) { QueueThread ( current_thread , Placement :: Insertion , now , total_runtime_ns ) ; } else if ( is_new_deadline_eligible && is_deadline ) { const SchedTime deadline_ns = current_thread -> scheduler_state ( ) . finish_time_ ; if ( Thread * const earlier_thread = DequeueEarlierDeadlineThread ( now , deadline_ns ) ; earlier_thread != nullptr ) { QueueThread ( current_thread , Placement :: Preemption , now , total_runtime_ns ) ; next_thread = earlier_thread ; } else { next_thread = current_thread ; } } else if ( is_new_deadline_eligible && ! is_deadline ) { QueueThread ( current_thread , Placement :: Preemption , now , total_runtime_ns ) ; } else { next_thread = current_thread ; } } else if ( ! is_active && likely ( ! is_idle ) ) { Remove ( current_thread ) ; } if ( next_thread == nullptr ) { next_thread = DequeueThread ( now ) ; } const auto needs_migration = [ active_mask , current_cpu_mask ] ( Thread * const thread ) { return ( thread -> scheduler_state ( ) . GetEffectiveCpuMask ( active_mask ) & current_cpu_mask ) == 0 || thread -> scheduler_state ( ) . next_cpu_ != INVALID_CPU ; } ; cpu_mask_t cpus_to_reschedule_mask = 0 ; for ( ; needs_migration ( next_thread ) ; next_thread = DequeueThread ( now ) ) { cpu_num_t target_cpu = INVALID_CPU ; if ( next_thread -> scheduler_state ( ) . next_cpu_ == INVALID_CPU ) { target_cpu = FindTargetCpu ( next_thread ) ; DEBUG_ASSERT ( target_cpu != this_cpu ( ) || next_thread -> scheduler_state ( ) . next_cpu_ != INVALID_CPU ) ; } if ( next_thread -> scheduler_state ( ) . next_cpu_ != INVALID_CPU ) { DEBUG_ASSERT ( next_thread -> scheduler_state ( ) . last_cpu_ == this_cpu ( ) ) ; target_cpu = next_thread -> scheduler_state ( ) . next_cpu_ ; next_thread -> CallMigrateFnLocked ( Thread :: MigrateStage :: Before ) ; next_thread -> scheduler_state ( ) . next_cpu_ = INVALID_CPU ; } DEBUG_ASSERT ( target_cpu != this_cpu ( ) ) ; Remove ( next_thread ) ; Scheduler * const target = Get ( target_cpu ) ; target -> Insert ( now , next_thread ) ; cpus_to_reschedule_mask |= cpu_num_to_mask ( target_cpu ) ; } if ( cpus_to_reschedule_mask ) { mp_reschedule ( cpus_to_reschedule_mask , 0 ) ; } return next_thread ; }
Selects a thread to run.
Selects a thread to run .
// If the timeslice expired insert the current thread into the run queue. // The current thread is deadline scheduled and there is at least one // eligible deadline thread in the run queue: select the eligible thread // with the earliest deadline, which may still be the current thread. // The current thread still has the earliest deadline. // The current thread is fair scheduled and there is at least one eligible // deadline thread in the run queue: return this thread to the run queue. // The current thread has remaining time and no eligible contender. // The current thread is no longer ready, remove its accounting. // The current thread is no longer running or has returned to the run queue, // select another thread to run. // Returns true when the given thread requires active migration. // If the next thread needs *active* migration, call the migration function, // migrate the thread, and select another thread to run. // // Most migrations are passive. Passive migration happens whenever a thread // becomes READY and a different CPU is selected than the last CPU the thread // ran on. // // Active migration happens under the following conditions: //  1. The CPU affinity of a thread that is READY or RUNNING is changed to //     exclude the CPU it is currently active on. //  2. Passive migration, or active migration due to #1, selects a different //     CPU for a thread with a migration function. Migration to the next CPU //     is delayed until the migration function is called on the last CPU. //  3. A thread that is READY or RUNNING is relocated by the periodic load //     balancer. NOT YET IMPLEMENTED. // // If the thread is not scheduled to migrate to a specifc CPU, find a // suitable target CPU. If the thread has a migration function, the search // will schedule the thread to migrate to a specific CPU and return the // current CPU. // If the thread is scheduled to migrate to a specific CPU, set the target // to that CPU and call the migration function. // The target CPU must always be different than the current CPU. // Remove accounting from this run queue and insert in the target run queue. // Issue reschedule IPIs to CPUs with migrated threads.