added single-queue scheduler; the old scheduler is compiled by default - to enable the new one uncomment the #define at the beginning of thread.h
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@776 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -12,6 +12,9 @@ extern "C" {
|
|||||||
#include <thread_types.h>
|
#include <thread_types.h>
|
||||||
#include <arch/thread.h>
|
#include <arch/thread.h>
|
||||||
|
|
||||||
|
// Uncomment the line below to compile the single-queue scheduler
|
||||||
|
//#define NEW_SCHEDULER
|
||||||
|
|
||||||
void resched(void);
|
void resched(void);
|
||||||
void start_scheduler(void);
|
void start_scheduler(void);
|
||||||
|
|
||||||
@@ -19,15 +22,23 @@ void thread_enqueue(struct thread *t, struct thread_queue *q);
|
|||||||
struct thread *thread_lookat_queue(struct thread_queue *q);
|
struct thread *thread_lookat_queue(struct thread_queue *q);
|
||||||
struct thread *thread_dequeue(struct thread_queue *q);
|
struct thread *thread_dequeue(struct thread_queue *q);
|
||||||
struct thread *thread_dequeue_id(struct thread_queue *q, thread_id thr_id);
|
struct thread *thread_dequeue_id(struct thread_queue *q, thread_id thr_id);
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
struct thread *thread_lookat_run_q(int priority);
|
struct thread *thread_lookat_run_q(int priority);
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
void thread_enqueue_run_q(struct thread *t);
|
void thread_enqueue_run_q(struct thread *t);
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
struct thread *thread_dequeue_run_q(int priority);
|
struct thread *thread_dequeue_run_q(int priority);
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
void thread_atkernel_entry(void); // called when the thread enters the kernel on behalf of the thread
|
void thread_atkernel_entry(void); // called when the thread enters the kernel on behalf of the thread
|
||||||
void thread_atkernel_exit(void);
|
void thread_atkernel_exit(void);
|
||||||
|
|
||||||
int thread_suspend_thread(thread_id id);
|
int thread_suspend_thread(thread_id id);
|
||||||
int thread_resume_thread(thread_id id);
|
int thread_resume_thread(thread_id id);
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
int thread_set_priority(thread_id id, int priority);
|
int thread_set_priority(thread_id id, int priority);
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
status_t thread_set_priority(thread_id id, int32 priority);
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
int thread_init(kernel_args *ka);
|
int thread_init(kernel_args *ka);
|
||||||
int thread_init_percpu(int cpu_num);
|
int thread_init_percpu(int cpu_num);
|
||||||
void thread_exit(int retcode);
|
void thread_exit(int retcode);
|
||||||
@@ -37,6 +48,9 @@ int thread_kill_thread_nowait(thread_id id);
|
|||||||
#define thread_get_current_thread arch_thread_get_current_thread
|
#define thread_get_current_thread arch_thread_get_current_thread
|
||||||
|
|
||||||
struct thread *thread_get_thread_struct(thread_id id);
|
struct thread *thread_get_thread_struct(thread_id id);
|
||||||
|
#ifdef NEW_SCHEDULER
|
||||||
|
struct thread *thread_get_thread_struct_locked(thread_id id);
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
thread_id thread_get_current_thread_id(void);
|
thread_id thread_get_current_thread_id(void);
|
||||||
|
|
||||||
extern inline thread_id thread_get_current_thread_id(void) {
|
extern inline thread_id thread_get_current_thread_id(void) {
|
||||||
|
|||||||
@@ -31,6 +31,19 @@ static int counter_thread(void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int priority_test(void *data)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i<10; i++) {
|
||||||
|
printf("%s: %d\n", (char *)data, i);
|
||||||
|
// sys_snooze(1000);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
thread_id t[THREADS];
|
thread_id t[THREADS];
|
||||||
@@ -62,5 +75,12 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
delete_sem(lock);
|
delete_sem(lock);
|
||||||
|
|
||||||
|
t[0] = spawn_thread(priority_test, "thread0", B_DISPLAY_PRIORITY, "thread0");
|
||||||
|
t[1] = spawn_thread(priority_test, "thread1", B_URGENT_DISPLAY_PRIORITY, "thread1");
|
||||||
|
resume_thread(t[0]);
|
||||||
|
resume_thread(t[1]);
|
||||||
|
sys_wait_on_thread(t[0], NULL);
|
||||||
|
sys_wait_on_thread(t[1], NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,102 @@
|
|||||||
#include <smp.h>
|
#include <smp.h>
|
||||||
#include <cpu.h>
|
#include <cpu.h>
|
||||||
#include <khash.h>
|
#include <khash.h>
|
||||||
|
#ifdef NEW_SCHEDULER
|
||||||
|
#include <Errors.h>
|
||||||
|
#include <kerrors.h>
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
|
|
||||||
static int _rand(void);
|
static int _rand(void);
|
||||||
|
|
||||||
|
#ifdef NEW_SCHEDULER
|
||||||
|
|
||||||
|
// The run queue. Holds the threads ready to run ordered by priority.
|
||||||
|
static struct thread_queue run_q = { NULL, NULL };
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_enqueue_run_q(struct thread *t)
|
||||||
|
{
|
||||||
|
struct thread *curr, *prev;
|
||||||
|
|
||||||
|
// these shouldn't exist
|
||||||
|
if(t->priority > B_MAX_PRIORITY)
|
||||||
|
t->priority = B_MAX_PRIORITY;
|
||||||
|
if(t->priority < B_MIN_PRIORITY)
|
||||||
|
t->priority = B_MIN_PRIORITY;
|
||||||
|
|
||||||
|
if (run_q.head == NULL) {
|
||||||
|
t->q_next = NULL;
|
||||||
|
run_q.head = t;
|
||||||
|
} else {
|
||||||
|
for (curr = run_q.head, prev = NULL; curr && (curr->priority >= t->priority); curr = curr->q_next) {
|
||||||
|
if (prev)
|
||||||
|
prev = prev->q_next;
|
||||||
|
else
|
||||||
|
prev = run_q.head;
|
||||||
|
}
|
||||||
|
t->q_next = curr;
|
||||||
|
if (prev)
|
||||||
|
prev->q_next = t;
|
||||||
|
else
|
||||||
|
run_q.head = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
thread_set_priority(thread_id id, int32 priority)
|
||||||
|
{
|
||||||
|
struct thread *t, *curr, *prev;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
// make sure the passed in priority is within bounds
|
||||||
|
if (priority > B_MAX_PRIORITY)
|
||||||
|
priority = B_MAX_PRIORITY;
|
||||||
|
if (priority < B_MIN_PRIORITY)
|
||||||
|
priority = B_MIN_PRIORITY;
|
||||||
|
|
||||||
|
t = thread_get_current_thread();
|
||||||
|
if (t->id == id) {
|
||||||
|
// it's ourself, so we know we aren't in the run queue, and we can manipulate
|
||||||
|
// our structure directly
|
||||||
|
retval = t->priority;
|
||||||
|
t->priority = priority;
|
||||||
|
} else {
|
||||||
|
int state = disable_interrupts();
|
||||||
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
|
t = thread_get_thread_struct_locked(id);
|
||||||
|
if (t) {
|
||||||
|
retval = t->priority;
|
||||||
|
if ((t->state == B_THREAD_READY) && (t->priority != priority)) {
|
||||||
|
// this thread is in the ready queue right now, so it needs to be reinserted
|
||||||
|
for (curr = run_q.head, prev = NULL; curr && (curr->id != t->id); curr = curr->q_next) {
|
||||||
|
if (prev)
|
||||||
|
prev = prev->q_next;
|
||||||
|
else
|
||||||
|
prev = run_q.head;
|
||||||
|
}
|
||||||
|
if (prev)
|
||||||
|
prev->q_next = curr->q_next;
|
||||||
|
else
|
||||||
|
run_q.head = curr->q_next;
|
||||||
|
t->priority = priority;
|
||||||
|
thread_enqueue_run_q(t);
|
||||||
|
} else
|
||||||
|
t->priority = priority;
|
||||||
|
} else
|
||||||
|
retval = B_BAD_THREAD_ID;
|
||||||
|
|
||||||
|
RELEASE_THREAD_LOCK();
|
||||||
|
restore_interrupts(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
static int
|
static int
|
||||||
_rand(void)
|
_rand(void)
|
||||||
{
|
{
|
||||||
@@ -93,14 +186,24 @@ reschedule_event(timer *unused)
|
|||||||
void
|
void
|
||||||
resched(void)
|
resched(void)
|
||||||
{
|
{
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
struct thread *next_thread = NULL;
|
struct thread *next_thread = NULL;
|
||||||
int last_thread_pri = -1;
|
int last_thread_pri = -1;
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
struct thread *next_thread, *prev_thread = NULL;
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
struct thread *old_thread = thread_get_current_thread();
|
struct thread *old_thread = thread_get_current_thread();
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
int i;
|
int i;
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
bigtime_t quantum;
|
bigtime_t quantum;
|
||||||
timer *quantum_timer;
|
timer *quantum_timer;
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
// dprintf("top of thread_resched: cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread());
|
// dprintf("top of thread_resched: cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread());
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
// dprintf("resched(): cpu %d, cur_thread = 0x%x\n", smp_get_current_cpu(), thread_get_current_thread());
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
|
|
||||||
switch(old_thread->next_state) {
|
switch(old_thread->next_state) {
|
||||||
case B_THREAD_RUNNING:
|
case B_THREAD_RUNNING:
|
||||||
@@ -109,7 +212,11 @@ resched(void)
|
|||||||
thread_enqueue_run_q(old_thread);
|
thread_enqueue_run_q(old_thread);
|
||||||
break;
|
break;
|
||||||
case B_THREAD_SUSPENDED:
|
case B_THREAD_SUSPENDED:
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
dprintf("suspending thread 0x%x\n", old_thread->id);
|
dprintf("suspending thread 0x%x\n", old_thread->id);
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
dprintf("resched(): suspending thread 0x%x\n", old_thread->id);
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
break;
|
break;
|
||||||
case THREAD_STATE_FREE_ON_RESCHED:
|
case THREAD_STATE_FREE_ON_RESCHED:
|
||||||
// This will hopefully be eliminated once the slab
|
// This will hopefully be eliminated once the slab
|
||||||
@@ -122,6 +229,7 @@ resched(void)
|
|||||||
}
|
}
|
||||||
old_thread->state = old_thread->next_state;
|
old_thread->state = old_thread->next_state;
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
// search the real-time queue
|
// search the real-time queue
|
||||||
for(i = B_MAX_PRIORITY; i >= B_FIRST_REAL_TIME_PRIORITY; i-=2) {
|
for(i = B_MAX_PRIORITY; i >= B_FIRST_REAL_TIME_PRIORITY; i-=2) {
|
||||||
next_thread = thread_dequeue_run_q(i);
|
next_thread = thread_dequeue_run_q(i);
|
||||||
@@ -152,9 +260,37 @@ resched(void)
|
|||||||
if(next_thread == NULL)
|
if(next_thread == NULL)
|
||||||
panic("next_thread == NULL! no idle priorities!\n");
|
panic("next_thread == NULL! no idle priorities!\n");
|
||||||
}
|
}
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
// select next thread from the run queue
|
||||||
|
next_thread = run_q.head;
|
||||||
|
while ((next_thread) && (next_thread->priority > B_IDLE_PRIORITY)) {
|
||||||
|
// always extract real time threads
|
||||||
|
if (next_thread->priority >= B_FIRST_REAL_TIME_PRIORITY)
|
||||||
|
break;
|
||||||
|
// never skip last non-idle normal thread
|
||||||
|
if (next_thread->q_next && (next_thread->q_next->priority == B_IDLE_PRIORITY))
|
||||||
|
break;
|
||||||
|
// skip normal threads sometimes
|
||||||
|
if (_rand() > 0x3000)
|
||||||
|
break;
|
||||||
|
prev_thread = next_thread;
|
||||||
|
next_thread = next_thread->q_next;
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
}
|
}
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
|
|
||||||
found_thread:
|
found_thread:
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
if (!next_thread)
|
||||||
|
panic("resched(): run queue is empty!\n");
|
||||||
|
|
||||||
|
// extract selected thread from the run queue
|
||||||
|
if (prev_thread)
|
||||||
|
prev_thread->q_next = next_thread->q_next;
|
||||||
|
else
|
||||||
|
run_q.head = next_thread->q_next;
|
||||||
|
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
next_thread->state = B_THREAD_RUNNING;
|
next_thread->state = B_THREAD_RUNNING;
|
||||||
next_thread->next_state = B_THREAD_READY;
|
next_thread->next_state = B_THREAD_READY;
|
||||||
|
|
||||||
@@ -169,6 +305,7 @@ found_thread:
|
|||||||
if (next_thread != old_thread)
|
if (next_thread != old_thread)
|
||||||
context_switch(old_thread, next_thread);
|
context_switch(old_thread, next_thread);
|
||||||
}
|
}
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// XXX should only reset the quantum timer if we are switching to a new thread,
|
// XXX should only reset the quantum timer if we are switching to a new thread,
|
||||||
@@ -191,5 +328,6 @@ found_thread:
|
|||||||
context_switch(old_thread, next_thread);
|
context_switch(old_thread, next_thread);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,17 +61,28 @@ static unsigned int num_death_stacks;
|
|||||||
static unsigned int volatile death_stack_bitmap;
|
static unsigned int volatile death_stack_bitmap;
|
||||||
static sem_id death_stack_sem;
|
static sem_id death_stack_sem;
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
// thread queues
|
// thread queues
|
||||||
// Thread priority has a granularity of 2; this means that we have 61 real
|
// Thread priority has a granularity of 2; this means that we have 61 real
|
||||||
// priority levels: 60 to map BeOS priorities 1-120, plus the idle priority (0).
|
// priority levels: 60 to map BeOS priorities 1-120, plus the idle priority (0).
|
||||||
static struct thread_queue run_q[(B_MAX_PRIORITY / 2) + 1] = { { NULL, NULL }, };
|
static struct thread_queue run_q[(B_MAX_PRIORITY / 2) + 1] = { { NULL, NULL }, };
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
// The dead queue is used as a pool from which to retrieve and reuse previously
|
||||||
|
// allocated thread structs when creating a new thread. It should be gone once
|
||||||
|
// the slab allocator is in.
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
struct thread_queue dead_q;
|
struct thread_queue dead_q;
|
||||||
|
|
||||||
static void thread_entry(void);
|
static void thread_entry(void);
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
static struct thread *thread_get_thread_struct_locked(thread_id id);
|
static struct thread *thread_get_thread_struct_locked(thread_id id);
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
static void thread_kthread_exit(void);
|
static void thread_kthread_exit(void);
|
||||||
static void deliver_signal(struct thread *t, int signal);
|
static void deliver_signal(struct thread *t, int signal);
|
||||||
|
|
||||||
|
#ifdef NEW_SCHEDULER
|
||||||
|
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
// insert a thread onto the tail of a queue
|
// insert a thread onto the tail of a queue
|
||||||
void thread_enqueue(struct thread *t, struct thread_queue *q)
|
void thread_enqueue(struct thread *t, struct thread_queue *q)
|
||||||
{
|
{
|
||||||
@@ -126,6 +137,7 @@ struct thread *thread_dequeue_id(struct thread_queue *q, thread_id thr_id)
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
struct thread *thread_lookat_run_q(int priority)
|
struct thread *thread_lookat_run_q(int priority)
|
||||||
{
|
{
|
||||||
return thread_lookat_queue(&run_q[(priority + 1) >> 1]);
|
return thread_lookat_queue(&run_q[(priority + 1) >> 1]);
|
||||||
@@ -147,6 +159,7 @@ struct thread *thread_dequeue_run_q(int priority)
|
|||||||
return thread_dequeue(&run_q[(priority + 1) >> 1]);
|
return thread_dequeue(&run_q[(priority + 1) >> 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
static void insert_thread_into_team(struct team *p, struct thread *t)
|
static void insert_thread_into_team(struct team *p, struct thread *t)
|
||||||
{
|
{
|
||||||
t->team_next = p->thread_list;
|
t->team_next = p->thread_list;
|
||||||
@@ -483,6 +496,7 @@ int thread_resume_thread(thread_id id)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
int thread_set_priority(thread_id id, int priority)
|
int thread_set_priority(thread_id id, int priority)
|
||||||
{
|
{
|
||||||
struct thread *t;
|
struct thread *t;
|
||||||
@@ -525,6 +539,7 @@ int thread_set_priority(thread_id id, int priority)
|
|||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
|
|
||||||
static const char *state_to_text(int state)
|
static const char *state_to_text(int state)
|
||||||
{
|
{
|
||||||
@@ -768,9 +783,11 @@ int thread_init(kernel_args *ka)
|
|||||||
thread_hash = hash_init(15, (addr)&t->all_next - (addr)t,
|
thread_hash = hash_init(15, (addr)&t->all_next - (addr)t,
|
||||||
&thread_struct_compare, &thread_struct_hash);
|
&thread_struct_compare, &thread_struct_hash);
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
// zero out the run queues
|
// zero out the run queues
|
||||||
memset(run_q, 0, sizeof(run_q));
|
memset(run_q, 0, sizeof(run_q));
|
||||||
|
|
||||||
|
#endif /* not NEW_SCHEDULER */
|
||||||
// zero out the dead thread structure q
|
// zero out the dead thread structure q
|
||||||
memset(&dead_q, 0, sizeof(dead_q));
|
memset(&dead_q, 0, sizeof(dead_q));
|
||||||
|
|
||||||
@@ -1173,7 +1190,11 @@ struct thread *thread_get_thread_struct(thread_id id)
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NEW_SCHEDULER
|
||||||
static struct thread *thread_get_thread_struct_locked(thread_id id)
|
static struct thread *thread_get_thread_struct_locked(thread_id id)
|
||||||
|
#else /* NEW_SCHEDULER */
|
||||||
|
struct thread *thread_get_thread_struct_locked(thread_id id)
|
||||||
|
#endif /* NEW_SCHEDULER */
|
||||||
{
|
{
|
||||||
struct thread_key key;
|
struct thread_key key;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user