blob: 31a2840dd5819ad77f49d84c6fe3f520efc4dd5f [file] [log] [blame]
#include <klibc/compiler.h>
#include <sys/cpu.h>
#include "thread.h"
#include "core.h"
#include <dprintf.h>
#ifndef DEBUG_THREAD
#define dprintf
#endif
void (*sched_hook_func)(void);
/*
* __schedule() should only be called with interrupts locked out!
*/
void __schedule(void)
{
static bool in_sched_hook;
struct thread *curr = current();
struct thread *st, *nt, *best;
#if DEBUG
if (__unlikely(irq_state() & 0x200)) {
dprintf("In __schedule with interrupts on!\n");
kaboom();
}
#endif
/*
* Are we called from inside sched_hook_func()? If so we'll
* schedule anyway on the way out.
*/
if (in_sched_hook)
return;
dprintf("Schedule ");
/* Possibly update the information on which we make
* scheduling decisions.
*/
if (sched_hook_func) {
in_sched_hook = true;
sched_hook_func();
in_sched_hook = false;
}
/*
* The unusual form of this walk is because we have to start with
* the thread *following* curr, and curr may not actually be part
* of the list anymore (in the case of __exit_thread).
*/
best = NULL;
nt = st = container_of(curr->list.next, struct thread, list);
do {
if (__unlikely(nt->thread_magic != THREAD_MAGIC)) {
dprintf("Invalid thread on thread list %p magic = 0x%08x\n",
nt, nt->thread_magic);
kaboom();
}
dprintf("Thread %p (%s) ", nt, nt->name);
if (!nt->blocked) {
dprintf("runnable priority %d\n", nt->prio);
if (!best || nt->prio < best->prio)
best = nt;
} else {
dprintf("blocked\n");
}
nt = container_of(nt->list.next, struct thread, list);
} while (nt != st);
if (!best)
kaboom(); /* No runnable thread */
if (best != curr) {
uint64_t tsc;
asm volatile("rdtsc" : "=A" (tsc));
dprintf("@ %llu -> %p (%s)\n", tsc, best, best->name);
__switch_to(best);
} else {
dprintf("no change\n");
}
}
/*
* This can be called from "normal" code...
*/
void thread_yield(void)
{
irq_state_t irq = irq_save();
__schedule();
irq_restore(irq);
}