Repository: SerenityOS/serenity
File: Kernel/Tasks/Scheduler.cpp (C++)

Code before:
#endif

    auto& proc = Processor::current();
    if (!thread->is_initialized()) {
        proc.init_context(*thread, false);
        thread->set_initialized(true);
    }
    thread->set_state(Thread::State::Running);

    PerformanceManager::add_context_switch_perf_event(*from_thread, *thread);

    proc.switch_context(from_thread, thread);

    // NOTE: from_thread at this point reflects the thread we were
    // switched from, and thread reflects Thread::current()
    enter_current(*from_thread);
    VERIFY(thread == Thread::current());

    {
        SpinlockLocker lock(thread->get_lock());
        InterruptDisabler disable;
        if (thread->dispatch_one_pending_signal() == DispatchSignalResult::Yield) {
            g_scheduler_lock.unlock(InterruptsState::Disabled);
            yield();
            g_scheduler_lock.lock();
        }
    }
}

void Scheduler::enter_current(Thread& prev_thread)
{
    VERIFY(g_scheduler_lock.is_locked_by_current_processor());

    // We already recorded the scheduled time when entering the trap, so this merely accounts for the kernel time since then
    auto scheduler_time = TimeManagement::scheduler_current_time();
    prev_thread.update_time_scheduled(scheduler_time, true, true);
    auto* current_thread = Thread::current();
    current_thread->update_time_scheduled(scheduler_time, true, false);

    // NOTE: When doing an exec(), we will context switch from and to the same thread!
    //       In that case, we must not mark the previous thread as inactive.
    if (&prev_thread != current_thread)
        prev_thread.set_active(false);

    if (prev_thread.state() == Thread::State::Dying) {
        // If the thread we switched from is marked as dying, then notify
        // the finalizer. Note that as soon as we leave the scheduler lock
        // the finalizer may free from_thread!
        notify_finalizer();
    }
}

Review comment (suggestion):
I don't really like this code.
We should either reschedule outside of `pick_next()` if we notice that we should yield or preferably we should not dispatch signals here either.

I think that we only ever need to dispatch signals before returning to user mode. That should also work for programs that never issue syscalls, since timer interrupts exist.
I'm not really sure if that's enough though. But if it is, signal dispatching would be a lot easier. (No clue what other Unixes do)

Code after:
#endif

    auto& proc = Processor::current();
    if (!thread->is_initialized()) {
        proc.init_context(*thread, false);
        thread->set_initialized(true);
    }
    thread->set_state(Thread::State::Running);

    PerformanceManager::add_context_switch_perf_event(*from_thread, *thread);

    proc.switch_context(from_thread, thread);

    // NOTE: from_thread at this point reflects the thread we were
    // switched from, and thread reflects Thread::current()
    enter_current(*from_thread);
    VERIFY(thread == Thread::current());

    {
        SpinlockLocker lock(thread->get_lock());
        return thread->dispatch_one_pending_signal() == DispatchSignalResult::Yield ? ShouldYield::Yes : ShouldYield::No;
    }
}

void Scheduler::enter_current(Thread& prev_thread)
{
    VERIFY(g_scheduler_lock.is_locked_by_current_processor());

    // We already recorded the scheduled time when entering the trap, so this merely accounts for the kernel time since then
    auto scheduler_time = TimeManagement::scheduler_current_time();
    prev_thread.update_time_scheduled(scheduler_time, true, true);
    auto* current_thread = Thread::current();
    current_thread->update_time_scheduled(scheduler_time, true, false);

    // NOTE: When doing an exec(), we will context switch from and to the same thread!
    //       In that case, we must not mark the previous thread as inactive.
    if (&prev_thread != current_thread)
        prev_thread.set_active(false);

    if (prev_thread.state() == Thread::State::Dying) {
        // If the thread we switched from is marked as dying, then notify
        // the finalizer. Note that as soon as we leave the scheduler lock
        // the finalizer may free from_thread!
        notify_finalizer();
    }
}

void Scheduler::leave_on_first_switch(InterruptsState previous_interrupts_state)
{
    // This is called when a thread is switched into for the first time.
    // At this point, enter_current has already be called, but because